ActivityResultLauncher使用,新方式来请求权限和startActivityForResult等等
文章目录
当前基于
androidx.activity的
1.4.0版本
一、这篇文章讲的是什么的?
1. 替换startActivityForResult和现在的权限回调处理
2. 注册必须在Activity#onStart()? 不,我们完全可以在用的时候才创建并使用
先说结论:
-
使用
registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。 -
ActivityResultLauncher的注册实际上是通过ActivityResultRegistry注册获取的。 -
ActivityResultRegistry有2个register方法,其中一个并不需要LifecycleOwner,所以通过这个新的注册方法,我们可以不遵守:必须在Activity必须在onStart及之前执行注册,和在fragment必须在onCreate及以前执行注册。 -
Fragment在使用register的是时候需要注意Activity是不是为空,否则会异常
1. 了解ActivityResultContract
首先我们需要了解有哪些自带的ActivityResultContract
StartActivityForResult使用Intent在activity(fragment)间通信,返回ActivityResultStartIntentSenderForResult使用IntentSenderRequest. builder构造,可以返回带有activity的ActivityResultRequestMultiplePermissions获取多个动态权限,返回Map<String,boolean>数组RequestPermission获取单个动态权限,成功后返回trueTakePicturePreview调用相机,拍照后返回bitmap图片信息TakePicture调用相机,拍照后将图片保存到开发者指定的Uri,返回trueTakeVideo拍摄视频,将拍摄内容保存到开发者指定的Uri,返回缩略图CaptureVideo拍摄视频,将内容保存到开发者指定的Uri后,返回truePickContact弹出手机联系人列表,用户选中其中一个后,返回UriGetContent访问原始数据,返回Uri,例如相册中的单个图片GetMultipleContents功能如上,返回Uri数组,例如相册中的多个图片OpenDocument访问文件数据,打开文件夹,返回单个文件的UriOpenMultipleDocuments功能如上,返回多个文件的UriOpenDocumentTree文件目录CreateDocument创建文件
当然也可以自己定义,不过上面已经基本满足我们的需求了。
eg:
class UserActivityResultContract : ActivityResultContract<String, UserInfo?>() {
override fun createIntent(context: Context, input: String): Intent {
val intent = Intent(context, UserInfoActivity::class.java)
intent.putExtra("url", input)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): UserInfo? {
if (resultCode == Activity.RESULT_OK) {
if (intent?.hasExtra("userInfo") == true) {
if (intent.getSerializableExtra("userInfo") != null) {
return intent.getSerializableExtra("userInfo") as UserInfo
}
}
}
return null
}
}
2. 如何使用
大家一般是在Fragment或者Activity中使用,需要在onCreate中使用registerForActivityResult注册一下得到ActivityResultLauncher,
需要注意的是,通过源码androidx.activity.ComponentActivity#registerForActivityResult:
if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is "
+ "attempting to register while current state is "
+ lifecycle.getCurrentState() + ". LifecycleOwners must call register before "
+ "they are STARTED.");
}
在Activity必须在onStart及之前执行。
androidx.fragment.app.Fragment#registerForActivityResult
if (mState > CREATED) {
throw new IllegalStateException("Fragment " + this + " is attempting to "
+ "registerForActivityResult after being created. Fragments must call "
+ "registerForActivityResult() before they are created (i.e. initialization, "
+ "onAttach(), or onCreate()).");
}
在fragment必须在onCreate及以前执行。
案例:
直接在在Fragment或者Activity中创建ActivityResultLauncher
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
//结果
}
然后在要调用的地方
permissionLauncher.launch(arrayOf(
Manifest.permission.WRITE_CALENDAR,
Manifest.permission.READ_CALENDAR
))
总结:
使用
registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。
二、注册ActivityResultLauncher必须在Activity#onStart()之前?
答案:当然不是
通过源码我可以知道,实际上注册获取ActivityResultLauncher是通过ActivityResultRegistry注册,通过查看源码然后我发现还有一个注册方法是不需要LifecycleOwner。
这样我们就可以在任意一个地方只要有Activity的上下文(context)就可以注册获取ActivityResultLauncher。
首先通过context(需要注意这里的context,要判断是否ComponentActivity)
androidx.activity.ComponentActivity#getActivityResultRegistry获取ActivityResultRegistry
然后通过下面的方法进行注册:

于是添加了下面的扩展方法,进行注册获取ActivityResultLauncher:
Activity:
fun <I, O> ComponentActivity.register(
@NonNull key: String,
@NonNull contract: ActivityResultContract<I, O>,
@NonNull callback: ActivityResultCallback<O>
): ActivityResultLauncher<I> {
return activityResultRegistry.register(key, contract, callback).also {
lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Event) {
if (event == Event.ON_DESTROY) {
it.unregister()
}
}
})
}
}
fragment:
fun <I, O> Fragment.register(
@NonNull key: String,
@NonNull contract: ActivityResultContract<I, O>,
@NonNull callback: ActivityResultCallback<O>
): ActivityResultLauncher<I> {
return requireActivity().activityResultRegistry.register(key, contract, callback).also {
lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Event) {
if (event == Event.ON_DESTROY){
it.unregister()
}
}
})
}
}
使用案例:
fun ComponentActivity.startActivityResult2(
@NonNull key: String = "startActivityResult" + mNextLocalRequestCode.getAndIncrement(),
@NonNull intent: Intent,
@NonNull callback: ActivityResultCallback<ActivityResult>
) {
register(key,ActivityResultContracts.StartActivityForResult(), callback).launch(intent)
}
在ComponentActivity需要的地方进行直接调用就可以了
startActivityResult2(intent = Intent(this,TestActivity::class.java)){
it.resultCode.toString().logI()
}
三、总结:
-
使用
registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。 -
ActivityResultLauncher的注册实际上是通过ActivityResultRegistry注册获取的。 -
ActivityResultRegistry有2个注册方法,其中一个并不需要LifecycleOwner,所以通过这个新的注册方法我们可以不遵守:在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。 -
Fragment在使用register的是时候需要注意Activity是不是为空,否则会异常
四、附上【请求权限封装】和案例
配合上面的扩展函数,进行封装
fun ComponentActivity.startRequestPermissionsLauncher(
@NonNull key: String = "startRequestMultiplePermissions" + mNextLocalRequestCode.getAndIncrement(),
@NonNull callback: ActivityResultCallback<Map<String, Boolean>>
): ActivityResultLauncher<Array<String>> {
return register(key, ActivityResultContracts.RequestMultiplePermissions(), callback)
}
fun ComponentActivity.startRequestPermissionLauncher(
@NonNull key: String = "startRequestPermission" + mNextLocalRequestCode.getAndIncrement(),
@NonNull callback: ActivityResultCallback<Boolean>
): ActivityResultLauncher<String> {
return register(key, ActivityResultContracts.RequestPermission(), callback)
}
fun ComponentActivity.startRequestPermissions(
@NonNull key: String = "startRequestMultiplePermissions" + mNextLocalRequestCode.getAndIncrement(),
@NonNull permissions: Array<String>,
@NonNull callback: ActivityResultCallback<Map<String, Boolean>>
) {
return startRequestPermissionsLauncher(key, callback).launch(permissions)
}
fun ComponentActivity.startRequestPermission(
@NonNull key: String = "startRequestPermission" + mNextLocalRequestCode.getAndIncrement(),
@NonNull permission: String,
@NonNull callback: ActivityResultCallback<Boolean>
) {
return startRequestPermissionLauncher(key, callback).launch(permission)
}
Fragment的封装差不多,这里就不做描述,下面是具体调用方法:
mContent.testEvent.setOnClickListener {
//调用方法获取日历的权限
startRequestPermissions(permissions = arrayOf(
Manifest.permission.WRITE_CALENDAR,
Manifest.permission.READ_CALENDAR
)){
if (it.filter { !it.value }.isEmpty()){
// 获取权限成功
}
}
}
还可以封装更多,比如选择图片,选择文件,拍照等等
更多案例Demo:AndroidX-KTX
1.选择联系人
pickContact {
if (it != null) {
Toast.makeText(this, it.toString(), Toast.LENGTH_LONG).show()
}
}
2.选择多个文件,比如多个图片
selectMultipleFile {
if (it != null) {
Toast.makeText(this, it.toString(), Toast.LENGTH_LONG).show()
}
}
3.拍摄视频
takeVideo {
if (it != null) {
Toast.makeText(this, it.toString(), Toast.LENGTH_LONG).show()
}
}
4.申请权限
startRequestPermission(permission = READ_EXTERNAL_STORAGE) {
if (it != null) {
Toast.makeText(this, it.toString(), Toast.LENGTH_LONG).show()
}
}
5.选择文件
selectFile {
if (it != null) {
Toast.makeText(this, it.toString(), Toast.LENGTH_LONG).show()
}
}
6.拍摄图片
takePicture {
if (it != null) {
Toast.makeText(this, it.toString(), Toast.LENGTH_LONG).show()
}
}

本文介绍了ActivityResultLauncher的使用,它是请求权限和startActivityForResult的新方式。讲解了ActivityResultContract自带功能,包括在activity间通信、获取权限、调用相机等。还探讨了注册是否必须在Activity#onStart()之前,指出新注册方法可在有Activity上下文处进行。最后附上请求权限封装和案例。

被折叠的 条评论
为什么被折叠?



