文章目录
首先介绍一下其作用:
- 它可以取代
startActivityForResult
方法,获取另一个Activity的返回结果。 - 它可以取代
requestPermissions
方法,请求运行时权限。
并且,RegisterForActivityResult
的实现方式比传统的方式更加便捷,合理。
1. 添加引用库
要在项目中使用该方法,需要添加两个官方的库:
implementation 'androidx.activity:activity-ktx:1.2.1'
implementation 'androidx.fragment:fragment-ktx:1.3.1'
添加之后支持在Activity或者Fragment里调用 RegisterForActivityResult
方法。比较奇怪的一点是,如果只添加 activity-ktx
而不添加 fragment-ktx
,即使只在Activity里面调用该方法,App仍然会崩溃报如下错误:
java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode
添加了Fragment支持库之后,问题就消失了。
2. 相关类和方法介绍
registerForActivityResult
方法完整代码如下:
public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull ActivityResultContract<I, O> contract,
@NonNull ActivityResultCallback<O> callback) {
return registerForActivityResult(contract, mActivityResultRegistry, callback);
}
//另一个文件
public abstract class ActivityResultLauncher<I> {
public void launch(I input) {
launch(input, null);
}
}
- 它需要传入
ActivityResultContract
- 它需要传入一个回调
ActivityResultCallback
- 它返回一个
ActivityResultLauncher
对象 - 调用
ActivityResultLauncher
的launch
方法可以跳转到另一个Activity或者发起权限请求
下面我们分别来了解这些参数的作用:
2.1 ActivityResultContract
ActivityResultContract
是一个抽象类,如下:
public abstract class ActivityResultContract<I, O> {
public abstract Intent createIntent( Context context, I input);
public abstract O parseResult(int resultCode, Intent intent);
... ...
}
其中:
-
I
表示传入的参数类型,O
表示结果参数类型。这两个类型用来规范入参和返回结果。
比如要跳转到另一个Activity,那么入参应该是一个Intent类型,包含要跳转的目标Activity信息。返回结果类型是希望从该Activity获取的数据的类型。 -
createIntent
方法用来新建一个Intent,以发起请求或跳转。其中的input
参数是由ActivityResultLauncher.launch(I input)
方法传入的。 -
parseResult
方法用来解析返回结果。
2.2 ActivityResultCallback
这个接口里只有一个方法,如下:
public interface ActivityResultCallback<O> {
void onActivityResult( O result);
}
ActivityResultContract
的 parseResult
方法返回的结果会回传到 onActivityResult
方法。
3. 示例
下面来写一个实例,实现 startActivityForResult
的功能。
这个例子功能很简单,我们有ActivityA
和 ActivityB
,以及一个数据类 TestBean
,我们要做的就是从 ActivityA
跳转到 ActivityB
,并从 ActivityB
获取一个 TestBean
并返回给 ActivityA
。
首先,定义 ActivityResultContract
,要跳转到另一个Activity,入参显然得是一个Intent,返回的结果是一个TestBean,那么可以定义如下:
private val activityResultContract = object : ActivityResultContract<Intent, TestBean>(){
override fun createIntent(context: Context, input: Intent): Intent {
//这个intent由resultLauncher调用launch方法时传入
return input
}
override fun parseResult(resultCode: Int, intent: Intent?): TestBean? {
if(resultCode == Activity.RESULT_OK){
return intent?.getParcelableExtra("testBean") ?: TestBean.EMPTY
}
return TestBean.EMPTY
}
}
然后,创建 ActivityResultLauncher
private val resultLauncher = registerForActivityResult(activityResultContract){
tvResult.text = it.toString()
}
最后,发起调用:
resultLauncher.launch(Intent(ActivityA.this, ActivityB::class.java))
在ActivityB
中,调用如下方法:
val resultIntent = Intent().apply{ putExtra("testBean", testBean) }
setResult(Activity.RESULT_OK, resultIntent)
finish()
ActivityA就能获取到ActivityB传过来的TestBean对象。
4. 官方的封装
4.1 实现startActivityForResult
从上面的示例来看,用起来还是有点麻烦,需要自定义 ActivityResultContract
。其实,谷歌已经针对不同的使用场景封装好了相应的ActivityResultContract
。全部都放在 ActivityResultConstracts 类中, 包括运行时权限请求,选取图片,拍照获取uri,选取联系人等等。就不一一介绍了。我们试着用官方封装好的类来实现第三节示例中的功能。
//创建ActivityResultLauncher
val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
if(it.resultCode == Activity.RESULT_OK){
val result = it.data?.getStringExtra("testBean") ?: TestBean.EMPTY
tvResult.text = result.toString()
}
}
//跳转到ActivityB
resultLauncher.launch(Intent(ActivityA.this, ActivityB::class.java))
使用已经封装好的 ActivityResultContract
, 实现和 3 的示例中完全相同的功能,代码量减少了一半以上。
4.2 实现requestPermissions
//创建ActivityResultLauncher
private val permissionResult = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){
val resultCamera = it[Manifest.permission.CAMERA] ?: false
if(resultCamera){
//Camera权限请求成功
}
val storageResult = it[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
if(storageResult){
//存储权限请求成功
}
}
//发起请求
permissionResult.launch(arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE
))
请求运行时权限从未如此轻松简单。
以上。