持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
概述
一般情况下,我们在进行Activity
之间的跳转的时候,如果需要被启动的Activity
能够在工作完成后向之前的Activity
中传递数据,就会使用startActivityForResult(intent: Intent,requestCode: Int)
去启动这个Activity
,然后在当前Activity
的onActivityResult()
中获取返回的数据,再做后续的操作。但是今天在使用这个方法的时候,AS提示这个方法已经被弃用,取而代之的是推荐使用registerForActivityResult(ActivityResultContract, ActivityResultCallback)
这个方法去做之前的那种操作,这篇笔记主要是记录一下这个方法的作用和在使用过程中遇到的问题。
registerForActivityResult(ActivityResultContract, ActivityResultCallback)
这个方法要求我们传递ActivityResultContract
和ActivityResultCallback
两个参数,同时会返回给我们一个ActivityResultLauncher
对象,我们可以使用这个对象去进行Activity
跳转。
ActivityResultContract
从代码中的注释可以看出,这是一个抽象类,主要是用于定义在Activity
跳转前后的输入数据类型和输出数据类型,下面是这个类提供的几个方法的简单介绍:
createIntent()
这个方法包含Context
和input
两个参数,input
参数的类型就是我们定义的泛型中的输入参数类型,要求我们返回一个Intent
,一般情况下我们可以指定输入的参数类型即为Intent
,这样这个方法就可以直接返回输入的数据,如下是ActivityResultContracts
中的StartActivityForResult
中的实现:
@NonNull
@Override
public Intent createIntent(@NonNull Context context, @NonNull Intent input) {
return input;
}
可以看到,这个类的实现就是输入类型就是Intent
,并将这个数据直接返回。当然我们完全可以定义任意的数据类型,只不过需要在这里处理一下,创建一个Intent
并将设置的输入数据设置进去,下面是我们实现的这个方法:
override fun createIntent(context: Context, input: String): Intent =
Intent(context, OtherActivity::class.java).apply{
this.putExtra(key,input)
}
在我们自己的实现中,就是会传递进来一个String
类型的参数,然后我们会在这里创建一个Intent
,之后将传递进来的值设置到这个Intent
中,在OtherActivity
这个页面中就可以获取到这个数据了。
parseResult()
这个方法主要是处理页面返回之后的操作,这个方法包含了resultCode: Int
和intent: Intent
这两个参数,一般情况下我们需要在页面返回之后获取上个页面退出时携带的数据,这个时候我们就可以通过resultCode
来判断上个页面的操作结果,以及从Intent
中获取上个页面退出时携带的数据。
一般情况下我们可以定义一个类,然后将数据处理成这个类的对象并返回,下面是ActivityResultContracts
中的StartActivityForResult
中的实现:
@NonNull
@Override
public ActivityResult parseResult(
int resultCode, @Nullable Intent intent) {
return new ActivityResult(resultCode, intent);
}
可以看到,这里就是创建了一个ActivityResult
对象,并将这个对象返回。
如果我们从上个页面返回的时候携带的数据不多,我们也可以直接返回这个数据,比如我们在上个页面退出时会携带一个String
类型的参数,那么我们的实现如下:
override fun parseResult(resultCode: Int, intent: Intent?): String
= intent?.getStringExtra(key) ?: ""
这里我们就直接将上个页面数据获取并返回。
ActivityResultCallback
这是一个接口,当我们从上个退出的时候会收到这个回调,这个接口中只有一个方法:onActivityResult(O result)
,其中O
就是我们定义的从上个页面返回的时候需要携带的数据类型。上面的parseResult()
方法处理后的数据就是提交给了onActivityResult()
,所以我们可以在这个方法中执行后续的操作。比如在我们的实现中,我们需要上个页面传递过来一个String
类型的数据,那么在上个页面退出之后我们从这个接口中就可以获取到相应的数据然后执行操作:
{
if (!TextUtils.isEmpty(it)) {
//请求这个value对应的数据
requestData(it)
}
ActivityResultLauncher
当我们执行了registerForActivityResult()
这个方法的时候,这个方法会返回一个ActivityResultLauncher
对象,这个类也是一个抽象类,这个类中指定了需要实现launch(input I)
方法,这个方法就是我们在真正的页面跳转的时候所使用的。当然,registerForActivityResult()
方法已经帮我们有了一个默认的实现,我们只需要调用其中的launch()
方法即可。
下面的代码演示了我们在页面跳转的时候执行的操作,如上面代码所示,我们在跳转的时候需要携带一个String
类型的参数,因此我们会将这个数据设置到launch(value)
中去,方便createIntent()
能够正确携带参数:
mToOtherActivity.launch("xxx")
如上所言,我们可以指定任意的输入类型,只要的launch()
方法执行的时候成功设置进去即可。
ActivityResultContracts
这个类中定义了很多我们常用的操作的ActivityResultContract
,比如申请权限,打开相册,打开文档等,我们可以根据自己的需要调用其中不同的类,如下所示:
普通的Activity跳转
如上面的例子,如果我们想要在Activity
退出的时候携带一些数据,我们就可以使用其中的StartActivityForResult
类,如下所示:
//注册回调
val toOtherActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
Logs.i(tag,"result code is:${it.resultCode}")
val value = it.data?.getStringExtra("key")
Logs.i(tag,"value is $value")
}
//跳转的时候使用
val intent = Intent(this,AddBillActivity::class.java)
intent.putExtra("key","value")
toOtherActivity.launch(intent)
权限申请
这个类中同样提供了用于权限申请的类型,我们可以使用它执行权限申请,如下所示:
//请求精确定位权限
registerForActivityResult(ActivityResultContracts.RequestPermission()){
Logs.i(tag,"权限请求成功:$it")
}.launch(Manifest.permission.ACCESS_FINE_LOCATION)
当我们在需要位置信息的时候可以通过这个方式去获取定位权限,在ActivityResultCallback
可以直接返回权限是否申请成功。
还有更多的跳转类型可以直接查看这个类中的代码找到。
遇到的问题
我们必须在Activity
的onResume()
方法执行之前设置回调,不能在需要的时候设置回调再去使用,一般情况下在onCreate()
中注册回调,在需要时候调用注册回调的launch()
方法即可,就如同上文中的"普通的Activity跳转"所写的那样。否则可能出现如下的异常:
java.lang.IllegalStateException: LifecycleOwner XXXActivity@7c57d7 is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.