You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Jetpack Compose中用协程后台执行同步并更新钱包UI

解决Jetpack Compose比特币钱包主线程阻塞问题

核心思路

将耗时的网络同步操作放到IO线程执行,完成后自动切回主线程更新UI,关键是利用协程调度器和ViewModel的生命周期安全协程作用域。


1. 改造Wallet.kt的sync()方法

给sync()添加suspend修饰符,把内部网络操作包裹在withContext(Dispatchers.IO)中,强制切换到IO线程执行耗时任务:

class Wallet {
    // 假设你有网络请求的API实例
    private val api = BitcoinWalletApi()

    suspend fun sync(): BigDecimal {
        // 将网络操作切换到IO调度器,避免阻塞主线程
        return withContext(Dispatchers.IO) {
            // 执行实际的网络同步逻辑,比如拉取最新余额
            val balanceResponse = api.getWalletLatestBalance()
            balanceResponse.amount
        }
    }
}

2. 优化WalletViewModel的updateBalance()方法

使用viewModelScope.launch启动协程(默认绑定主线程),调用挂起函数sync()时,协程会自动切换到IO线程执行,完成后切回主线程更新LiveData:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.launch
import java.math.BigDecimal

class WalletViewModel : ViewModel() {
    private val wallet = Wallet()
    val balanceLiveData = MutableLiveData<BigDecimal>(BigDecimal.ZERO)

    fun updateBalance() {
        // viewModelScope会在ViewModel销毁时自动取消协程,避免内存泄漏
        viewModelScope.launch {
            try {
                // 调用挂起函数,自动切换到IO线程执行同步
                val newBalance = wallet.sync()
                // 处于主线程,安全更新LiveData
                balanceLiveData.value = newBalance
            } catch (e: Exception) {
                // 处理同步失败,比如记录日志、提示用户
                e.printStackTrace()
            }
        }
    }
}

3. Compose页面调用逻辑

在HomeScreen中直接触发updateBalance()即可,点击操作不会阻塞主线程:

import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import java.math.BigDecimal

@Composable
fun HomeScreen() {
    val viewModel: WalletViewModel = viewModel()
    val balance by viewModel.balanceLiveData.collectAsState()

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(text = "当前余额: ${balance.toPlainString()} BTC")
        Image(
            painter = painterResource(id = R.drawable.ic_sync),
            contentDescription = "同步余额",
            modifier = Modifier
                .padding(top = 16.dp)
                .clickable {
                    // 点击触发同步,无主线程阻塞
                    viewModel.updateBalance()
                }
        )
    }
}

常见问题排查

  • 之前加suspend无效?大概率是sync()内部没切换到IO线程,挂起函数仅允许协程挂起,但若耗时操作仍在主线程执行,还是会阻塞。
  • 用async没效果?async用于并发任务,需配合await()获取结果,但在主线程直接await()会阻塞,正确用法是在launch内部调用async.await(),但此处直接用launch调用挂起函数更简洁。
  • 确保导入正确的viewModelScope:import androidx.lifecycle.viewModelScope,它由Jetpack Lifecycle库提供,自动绑定ViewModel生命周期。

内容的提问来源于stack exchange,提问作者W4nnaCryy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 15:10:28