1. 介绍
启动另一个 Activity(无论是在自己的应用内还是从另一个应用启动),不必只是单向操作。还可以启动另一个活动并接收返回的结果。
虽然之前我们一直使用 startActivityForResult()
和 onActivityResult()
API 启动另一个 Activity 并回调,而且在所有 API 级别的 Activity
类上都可用,但官方现在强烈建议使用 androidx 的 Activity 和 Fragment 中引入的 Activity Result API - ActivityResultRegistry
。
2. 为什么废弃 startActivityForResult
startActivityForResult
和 onActivityResult
有什么缺点:
- 从哪里发出某个请求必须发送唯一的请求代码,以防使用重复。有时会导致错误的结果
- 如果重新创建组件,则会丢失结果
onActivityResult
回调不适用于 Fragment
为什么新的 ActivityResultRegistry
方式更好?
- 可以从
Fragment
中启动回调结果的 Activity
,并以 Fragment
形式接收结果,而无需通过 Activity
。
- 现在不再使用易出错的
int
类型来区分请求,而是使用强类型类。
- 可以对单独的请求进行单独的回调,从而消除了回调中切换情况的需要。
3. 如何使用 ActivityResultRegistry
-
声明 ActivityResultLauncher
lateinit var launcher : ActivityResultLauncher
-
注册 ActivityResultLauncher
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// TODO use the result
}
注意:
必须在创建完成 (onCreate 结束前) Fragment 或 Activity 之前调用 registerForActivityResult
在 Fragment 或 Activity 的生命周期达到 “已创建” 之前,无法启动另一个 Activity。
-
使用 ActivityResultLauncher
launcher.launch(Intent(context, AnotherActivity::class.java)
.putExtra(AnotherActivity.PARAM_NAME, ExtraData))
上面的介绍用法使用的是 StartActivityFroResult
作为协定,不需要自定义协定时可以用它来处理结果。
注意:
由于在调用 launch()
与触发 onActivityResult()
回调的两个时间点之间,您的进程和 Activity 可能会被销毁,因此,处理结果所需的任何其他状态都必须与这些 API 分开保存和恢复。
3.1 一些常用的内置协定 ActivityResultContracts
:
- StartActivityForResult
- RequestPermission
- TakePicture
- TakeVideo
- GetContent
- OpenDocument
具体用法参见官方文档,或 ActivityResultContracts 源码文件中继承 ActivitResultContracts 的子类。
3.2 自定义协定 ActivityResultContracts
除了内置的 ActivityResultContracts 外,还支持自定义协定。
// 定义一个自定义的协定,接受 String 类型的参数,并返回一个 String? 类型的结果
class CustomContract : ActivityResultContract<String, String?>() {
override fun createIntent(context: Context, input: String): Intent {
val intent = Intent(context, AnotherActivity::class.java)
intent.putExtra(EXTRA_DATA, input)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
return when (resultCode) {
// TODO 处理打开的另一个 Activity 返回的 intent 携带的数据,并返回 String? 类型的结果
}
}
companion object {
const val EXTRA_DATA = "extra_data"
}
}
// 自定义协定的用法
class MainActivity : ComponentActivity() {
val resultLauncher = registerForActivityResult(CustomContract()) { result ->
// Handle the returned result
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTextDemoTheme {
LoginItmob {
resultLauncher.launch("input-data itmob.cn")
}
}
}
}
@Composable
private fun LoginItmob(
launchLogin: () -> Unit,
) {
Button(
onClick = launchLogin,
) {
Text(text = "Login ITmob.cn")
}
}
4. 示例
4.1 ActivityA 调用 ActivityB 并获取结果
class MainActivity : ComponentActivity() {
// result launcher
private val resultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
// Handle the returned result
}
}
}
// 调用 ActivityB
getResult.launch(Intent(context, ActivityB::class.java))
4.2 FragmentA 调用 ActivityB 并获取结果
Fragment
调用 Activity
并获取结果,是旧实现方式的一个问题,没有可用的明确实现,但新 API 可以跨 Activity 和 Fragment 提供一致的实现。
因此,只需要将示例 a 中的代码添加到我们的 Fragment
即可
4.3 启动外部组件,如启动相机获取图像
// 注册 result launcher
private val getImage = registerForActivityResult(
ActivityResultContracts.TakePicture()
) { result ->
// TODO 如果图像已保存到给定的 Uri 中,则返回 true。
}
// 调用相机拍照,并将其保存到给定的content-Uri 中
getImage.launch(uri)
4.4 在单独的类中接收 Activity 的结果
androidx 的 ComponentActivity
和 Fragment
类实现了 ActivityResultCaller
接口以允许使用 registerForActivityResult
API,但也可以在单独的类中接收 Activity 的结果,即使该类不直接使用 ActivityResultRegistry
实现 ActivityResultCaller
。
例如,可能想要实现一个 LifecycleObserver
来处理注册协定以及启动 ResultLauncher:
class MyLifecycleObserver(private val registry : ActivityResultRegistry)
: DefaultLifecycleObserver {
lateinit var getContent : ActivityResultLauncher<String>
override fun onCreate(owner: LifecycleOwner) {
getContent = registry.register("key", owner, GetContent()) { uri ->
// Handle the returned Uri
}
}
fun selectImage() {
getContent.launch("image/*")
}
}
class MyFragment : Fragment() {
lateinit var observer : MyLifecycleObserver
override fun onCreate(savedInstanceState: Bundle?) {
// ...
observer = MyLifecycleObserver(requireActivity().activityResultRegistry)
lifecycle.addObserver(observer)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val selectButton = view.findViewById<Button>(R.id.select_button)
selectButton.setOnClickListener {
// Open the activity to select an image
observer.selectImage()
}
}
}
5. 注意:
使用 ActivityResultRegistry
API 时,强烈建议使用可接受 LifecycleOwner 作为参数的 API,因为 LifecycleOwner
会在 Lifecycle
被销毁时自动移除已注册的启动器。不过,如果 LifecycleOwner
不存在,每个 ActivityResultLauncher
类都允许手动调用 unregister()
作为替代。
上面内容是文档中的描述,接受 LifecycleOwner 作为参数的 API 是什么意思呢?
就是使用 ActivityResultRegistry
的 register
方法注册结果回调时,强烈建议提供 LifecycleOwner 参数,这样在 Lifecycle 销毁时会自动移除注册的 ResultLauncher。
// 正如 ComponentActivity 的源码中,
// registerForActivityResult 方法其实也是调用的 register 方法,并将 LifecycleOwner 作为参数
@NonNull
@Override
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultRegistry registry,
@NonNull final ActivityResultCallback<O> callback) {
return registry.register(
"activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback);
}