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

如何在Compose中将StartActivityResult回调转为协程线性风格?

解决方案:Compose中结合协程实现线性风格获取Activity返回结果

核心思路是将挂起逻辑封装在ViewModel中(ViewModel支持协程),同时遵守registerForActivityResult/rememberLauncherForActivityResult的初始化规范,通过状态流(StateFlow)连接Activity结果回调与协程挂起逻辑,最终在Compose中通过协程Scope触发调用,实现线性代码风格。

方案一:Activity中初始化Launcher(符合规范)

1. Activity层:无条件初始化Launcher

class MainActivity : ComponentActivity() {
    // 符合规范:在Activity字段初始化阶段无条件注册Launcher
    private val otherAppLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        viewModel.onOtherAppResult(result)
    }

    private val viewModel by viewModels<MainViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                MainScreen(
                    viewModel = viewModel,
                    launchOtherApp = { intent -> otherAppLauncher.launch(intent) }
                )
            }
        }
    }
}

2. ViewModel层:封装挂起函数实现线性等待

class MainViewModel : ViewModel() {
    private val resultFlow = MutableStateFlow<ActivityResult?>(null)

    /**
     * 挂起函数:启动外部App并等待返回结果
     * @param launchIntent 启动外部App的Intent
     * @param launchAction 实际触发启动的lambda(由Activity层提供)
     */
    suspend fun launchOtherAppAndWaitResult(
        launchIntent: Intent,
        launchAction: (Intent) -> Unit
    ): ActivityResult {
        // 清空历史结果,避免干扰
        resultFlow.value = null
        // 触发外部App启动
        launchAction(launchIntent)
        
        // 挂起等待结果,实现线性代码
        return suspendCancellableCoroutine { cont ->
            val collectJob = viewModelScope.launch {
                resultFlow.filterNotNull().collect { result ->
                    cont.resume(result)
                }
            }
            // 协程取消时清理收集任务
            cont.invokeOnCancellation { collectJob.cancel() }
        }
    }

    // 接收Activity返回的结果并发送到流中
    fun onOtherAppResult(result: ActivityResult) {
        resultFlow.value = result
    }
}

3. Compose层:触发协程调用

@Composable
fun MainScreen(
    viewModel: MainViewModel,
    launchOtherApp: (Intent) -> Unit
) {
    // 获取Compose生命周期绑定的协程Scope
    val coroutineScope = rememberCoroutineScope()

    Button(onClick = {
        coroutineScope.launch {
            // 线性代码风格:启动App -> 等待结果 -> 处理结果
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse("your_app_scheme://target"))
            val result = viewModel.launchOtherAppAndWaitResult(intent, launchOtherApp)
            
            // 处理返回结果
            if (result.resultCode == Activity.RESULT_OK) {
                val data = result.data
                // 业务逻辑处理,如解析返回数据
            }
        }
    }) {
        Text("启动外部App")
    }
}

方案二:Compose中直接使用rememberLauncherForActivityResult(需遵守初始化规范)

如果希望完全在Compose层处理Launcher,需确保rememberLauncherForActivityResult在Composable顶层无条件调用(符合初始化规范):

@Composable
fun MainScreen(viewModel: MainViewModel) {
    val resultFlow = remember { MutableStateFlow<ActivityResult?>(null) }
    
    // 符合规范:无条件在Composable顶层注册Launcher
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        resultFlow.value = result
    }

    val coroutineScope = rememberCoroutineScope()

    Button(onClick = {
        coroutineScope.launch {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse("your_app_scheme://target"))
            val result = viewModel.waitForActivityResult(resultFlow) {
                launcher.launch(intent)
            }
            
            // 处理返回结果
            if (result.resultCode == Activity.RESULT_OK) {
                // ...
            }
        }
    }) {
        Text("启动外部App")
    }
}

// ViewModel中对应的挂起函数
fun MainViewModel.waitForActivityResult(
    resultFlow: StateFlow<ActivityResult?>,
    launchAction: () -> Unit
): ActivityResult = suspendCancellableCoroutine { cont ->
    resultFlow.value = null
    launchAction()
    
    val collectJob = viewModelScope.launch {
        resultFlow.filterNotNull().collect { result ->
            cont.resume(result)
        }
    }
    cont.invokeOnCancellation { collectJob.cancel() }
}

关键注意点

  • 可组合函数不能挂起:必须通过rememberCoroutineScope或LaunchedEffect在协程中调用挂起函数。
  • Launcher初始化规范:registerForActivityResult/rememberLauncherForActivityResult必须在初始化路径(Activity字段、Composable顶层)无条件调用,不能放在if、LaunchedEffect等条件分支内。
  • 协程安全:通过viewModelScope管理协程生命周期,避免内存泄漏;协程取消时及时清理结果收集任务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:02:32