在 Android 开发中,我们有大量的应用场景需要启动一个 Activity 并接收其返回的结果来完成业务逻辑的执行。例如,你的应用可启动相机并接收拍摄的照片作为结果。或者,你可以启动“通讯录”以便用户选择联系人,然后接收联系人详细信息作为结果。
虽然所有 API 级别的 Activity 类均提供了底层 startActivityForResult
和 onActivityResult
API,但 Google 强烈建议使用 registerForActivityResult
来替代它们。因为新的 API 可以极大程度的保证数据的类型安全、Activity 的生命周期安全,同时还能增加代码的复用性。详情参考「官方文档」。
1、新旧 API 使用对比
1.1 旧方法
1 | override fun onCreate(savedInstanceState: Bundle?) { |
1.2 新方法
1 | private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> |
2、常用预定义协议
Jetpack 提供了多种预定义的合约类型,开箱即用:
合约类型 | 用途 | 返回值类型 |
---|---|---|
StartActivityForResult() | 通用 Activity 启动 | ActivityResult |
TakePicture() | 拍照 | Bitmap? |
PickContact() | 选择联系人 | Uri? |
GetContent() | 选择文件(文本/图片等) | Uri? |
RequestPermission() | 请求单个权限 | Boolean |
OpenDocument() | 打开文档 | Uri? |
OpenDocumentTree() | 访问目录树 | Uri? |
StartIntentSenderForResult() | 处理 PendingIntent | ActivityResult |
2.1 拍照并获取图片
1 | // 1. 注册合约 |
2.2 请求位置权限
1 | private val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> |
2.3 打开类型文档
1 | private val launcher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? -> |
3、创建自定义协议
虽然 ActivityResultContracts
包含一些预先构建的 ActivityResultContract
类供您使用,但您可以使用自己的协定,提供您所需要的精确类型安全 API。
每个 ActivityResultContract
都需要定义好的输入和输出类,如果您不需要任何输入,请使用 Void 作为输入类型(在 Kotlin 中,请使用 Void?
或 Unit
)。
每个协定都必须实现 createIntent()
方法,该方法接受 Context
和输入内容作为参数,并构造将与 startActivityForResult()
配合使用的 Intent
。
每个协定还必须实现 parseResult()
,它会根据指定的 resultCode
(如 Activity.RESULT_OK
或 Activity.RESULT_CANCELED
)和 Intent
生成输出。
如果无需调用 createIntent()
、启动另一个 activity 并借助 parseResult()
来构建结果即可确定指定输入内容的结果,协定可以选择性地实现 getSynchronousResult()
。
以下示例展示了如何构造 ActivityResultContract:
1 | class PickRingtone : ActivityResultContract<Int, Uri?>() { |