在 Android 开发中,我们有大量的应用场景需要启动一个 Activity 并接收其返回的结果来完成业务逻辑的执行。例如,你的应用可启动相机并接收拍摄的照片作为结果。或者,你可以启动“通讯录”以便用户选择联系人,然后接收联系人详细信息作为结果。
虽然所有 API 级别的 Activity 类均提供了底层 startActivityForResult 和 onActivityResult API,但 Google 强烈建议使用 registerForActivityResult 来替代它们。因为新的 API 可以极大程度的保证数据的类型安全、Activity 的生命周期安全,同时还能增加代码的复用性,详情参考「官方文档」。
1、新旧 API 使用对比
1.1 旧方法
1 | val intent = Intent(this, TargetActivity::class.java) |
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 | private val launcher = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap: Bitmap? -> |
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?>() { |