ActivityResult API 替代 onActivityResult
问题说明
自 Android 10(API 级别 30)开始,传统的 startActivityForResult()
和 onActivityResult()
方法已被标记为已弃用。这意味着这些方法虽然在当前版本仍可使用,但未来的 Android 版本可能会移除它们,因此建议开发者迁移到新的 Activity Result API。
解决方案
Android 引入了新的 Activity Result API 来处理活动结果,提供了更清晰、更灵活的解决方案。新 API 基于 registerForActivityResult()
方法,使用 ActivityResultContract
来定义不同类型的操作。
基础使用示例
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 版本
// 在 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 需要为每个不同类型的请求创建单独的启动器:
// 拍照请求
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 也简化了权限请求流程:
// 请求单个权限
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 获取特定类型的内容:
// 获取图片
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. 封装工具类
为方便复用,可以创建封装工具类:
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. 向后兼容方案
如需保持与旧代码兼容,可以创建适配器:
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) {
// 供子类重写
}
}
迁移建议
- 逐步迁移:先迁移较简单的用例,再处理复杂场景
- 测试验证:确保新实现与旧行为一致
- 代码清理:移除不再需要的
requestCode
常量
总结
新的 Activity Result API 提供了更现代、类型安全的替代方案,虽然需要一些适应,但最终会带来更清晰、更易维护的代码。建议开发者尽快迁移到新 API,以适应 Android 平台的未来发展。