ActivityResult API最佳实践指南

462 阅读2分钟

为了有效使用Android的ActivityResult API,遵循以下最佳实践可提升代码质量和维护性:

1. 初始化与注册位置

  • onCreate中注册:在Activity或Fragment的onCreate方法中注册ActivityResultLauncher,确保生命周期安全,避免重复注册。
    lateinit var imagePickerLauncher: ActivityResultLauncher<String>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        imagePickerLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
            uri?.let { handleImage(it) }
        }
    }
    

2. 使用预定义Contract

  • 利用系统提供的Contracts:如GetContentRequestPermissionTakePicture等,简化常见操作。
    val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) showCamera() else showPermissionDenied()
    }
    

3. 自定义Contract封装逻辑

  • 内聚数据传递与解析:自定义Contract处理特定业务,如启动编辑页面并返回结果。
    class EditContract : ActivityResultContract<String, String?>() {
        override fun createIntent(context: Context, input: String): Intent =
            Intent(context, EditActivity::class.java).apply { putExtra("INITIAL_TEXT", input) }
    
        override fun parseResult(resultCode: Int, intent: Intent?): String? =
            if (resultCode == Activity.RESULT_OK) intent?.getStringExtra("RESULT_TEXT") else null
    }
    
    // 注册使用
    val editLauncher = registerForActivityResult(EditContract()) { result ->
        result?.let { updateTextView(it) }
    }
    

4. 处理结果与异常情况

  • 检查结果有效性:处理用户取消或数据缺失情况,避免崩溃。
    val startForResult = registerForActivityResult(StartActivityForResult()) { result ->
        when (result.resultCode) {
            RESULT_OK -> parseData(result.data)
            RESULT_CANCELED -> showUserCanceledToast()
            else -> handleUnexpectedResult()
        }
    }
    

5. 线程与UI安全

  • 主线程回调:直接在回调中更新UI,避免耗时操作。若需处理数据,使用协程或后台线程。
    imagePickerLauncher = registerForActivityResult(GetContent()) { uri ->
        uri?.let {
            lifecycleScope.launch {
                val bitmap = withContext(Dispatchers.IO) { loadBitmap(it) }
                imageView.setImageBitmap(bitmap)
            }
        }
    }
    

6. 代码组织与可读性

  • 逻辑分组:按功能模块组织launcher,避免onCreate臃肿。使用Kotlin扩展或单独类管理。
    // 扩展函数分组
    fun MainActivity.registerLaunchers() {
        imagePickerLauncher = registerForActivityResult(GetContent()) { /* ... */ }
        editLauncher = registerForActivityResult(EditContract()) { /* ... */ }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        registerLaunchers()
    }
    

7. 内存与生命周期管理

  • 避免泄露:回调中避免直接引用View,使用弱引用或检查生命周期状态。
    imagePickerLauncher = registerForActivityResult(GetContent()) { uri ->
        if (isDestroyed) return@registerForActivityResult // 检查Activity状态
        uri?.let { loadImage(it) }
    }
    

8. 测试与调试

  • 模拟结果场景:单元测试中验证各种结果码和数据组合,确保健壮性。
    @Test
    fun testEditContract() {
        val contract = EditContract()
        val intent = contract.createIntent(context, "Test")
        val result = contract.parseResult(Activity.RESULT_OK, Intent().apply { putExtra("RESULT_TEXT", "New Text") })
        assertEquals("New Text", result)
    }
    

9. 处理配置变更

  • 自动重建Launcher:系统自动处理launcher的重建,无需额外代码,但需测试旋转后的行为。

10. 文档与注释

  • 明确合约职责:为自定义Contract添加文档,说明输入输出及使用场景。
    /**
     * 启动EditActivity并返回编辑后的文本
     * @param String 初始文本
     * @return String? 编辑后的文本(RESULT_OK时非空)
     */
    class EditContract : ActivityResultContract<String, String?>() { ... }
    

总结

通过合理利用ActivityResult API,将请求与结果处理逻辑集中,提升代码清晰度。结合预定义和自定义Contract,适应多样化场景,同时注意生命周期和异常处理,确保应用稳健高效。