Skip to content

ActivityResult API 替代 onActivityResult

问题说明

自 Android 10(API 级别 30)开始,传统的 startActivityForResult()onActivityResult() 方法已被标记为已弃用。这意味着这些方法虽然在当前版本仍可使用,但未来的 Android 版本可能会移除它们,因此建议开发者迁移到新的 Activity Result API。

解决方案

Android 引入了新的 Activity Result API 来处理活动结果,提供了更清晰、更灵活的解决方案。新 API 基于 registerForActivityResult() 方法,使用 ActivityResultContract 来定义不同类型的操作。

基础使用示例

Kotlin 版本

kotlin
// 在 Activity 或 Fragment 中注册结果处理器
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // 处理返回结果
        doSomeOperations()
    }
}

// 启动活动
fun openSomeActivityForResult() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

Java 版本

java
// 在 Activity 或 Fragment 中注册结果处理器
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    Intent data = result.getData();
                    doSomeOperations();
                }
            }
        });

// 启动活动
public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    someActivityResultLauncher.launch(intent);
}

处理多个请求类型

传统方法使用 requestCode 区分不同的请求,新 API 需要为每个不同类型的请求创建单独的启动器:

kotlin
// 拍照请求
private val takePhotoLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // 处理拍照结果
    }
}

// 选择图片请求
private val pickImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        // 处理选择图片结果
    }
}

// 使用不同的启动器启动不同活动
private fun takePhoto() {
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    takePhotoLauncher.launch(intent)
}

private fun pickImage() {
    val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    pickImageLauncher.launch(intent)
}

权限请求

新 API 也简化了权限请求流程:

kotlin
// 请求单个权限
private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
    if (isGranted) {
        // 权限已授予
    } else {
        // 权限被拒绝
    }
}

// 请求多个权限
private val requestMultiplePermissionsLauncher = registerForActivityResult(
    ActivityResultContracts.RequestMultiplePermissions()
) { permissions: Map<String, Boolean> ->
    permissions.entries.forEach { (permission, isGranted) ->
        // 处理每个权限的授权状态
    }
}

// 使用权限启动器
fun requestCameraPermission() {
    requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}

fun requestMultiplePermissions() {
    requestMultiplePermissionsLauncher.launch(arrayOf(
        Manifest.permission.CAMERA,
        Manifest.permission.ACCESS_FINE_LOCATION
    ))
}

特定内容获取

使用预定义的 Contract 获取特定类型的内容:

kotlin
// 获取图片
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
    // 处理返回的 Uri
    binding.myImageView.setImageURI(uri)
}

// 启动内容获取
binding.selectPhotoButton.setOnClickListener {
    getContent.launch("image/*")
}

最佳实践

1. 注册时机

WARNING

必须在 Activity 或 Fragment 的 onCreate()onAttach() 方法中注册启动器,不能在 onResume() 或之后注册,否则会抛出 IllegalStateException

2. 封装工具类

为方便复用,可以创建封装工具类:

kotlin
class ActivityLauncher(private val caller: ActivityResultCaller) {
    private val intentLauncher = caller.registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result -> /* 默认处理 */ }
    
    private val permissionLauncher = caller.registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted -> /* 默认处理 */ }
    
    fun launchIntent(intent: Intent, onResult: (ActivityResult) -> Unit = {}) {
        // 实现启动逻辑
    }
    
    fun launchPermission(permission: String, onResult: (Boolean) -> Unit = {}) {
        // 实现权限请求逻辑
    }
}

3. 向后兼容方案

如需保持与旧代码兼容,可以创建适配器:

kotlin
abstract class BaseActivity : AppCompatActivity() {
    private var requestCode: Int = -1
    private var resultHandler: ActivityResultLauncher<Intent>? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        registerForActivityResult()
    }
    
    private fun registerForActivityResult() {
        resultHandler = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) { result ->
            onActivityResult(result.data, requestCode, result.resultCode)
            this.requestCode = -1
        }
    }
    
    fun startActivityForResult(requestCode: Int, intent: Intent) {
        this.requestCode = requestCode
        resultHandler?.launch(intent)
    }
    
    protected open fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int) {
        // 供子类重写
    }
}

迁移建议

  1. 逐步迁移:先迁移较简单的用例,再处理复杂场景
  2. 测试验证:确保新实现与旧行为一致
  3. 代码清理:移除不再需要的 requestCode 常量

总结

新的 Activity Result API 提供了更现代、类型安全的替代方案,虽然需要一些适应,但最终会带来更清晰、更易维护的代码。建议开发者尽快迁移到新 API,以适应 Android 平台的未来发展。