ActivityResultLauncher使用,新方式来请求权限和startActivityForResult等等

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ActivityResultLauncher使用,新方式来请求权限和startActivityForResult等等


当前基于 androidx.activity1.4.0版本

一、这篇文章讲的是什么的?

1. 替换startActivityForResult和现在的权限回调处理
2. 注册必须在Activity#onStart()? 不,我们完全可以在用的时候才创建并使用

先说结论:

  1. 使用registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。

  2. ActivityResultLauncher的注册实际上是通过ActivityResultRegistry注册获取的。

  3. ActivityResultRegistry有2个register方法,其中一个并不需要LifecycleOwner,所以通过这个新的注册方法,我们可以不遵守:必须在Activity必须在onStart及之前执行注册,和在fragment必须在onCreate及以前执行注册。

  4. Fragment在使用register的是时候需要注意Activity是不是为空,否则会异常

1. 了解ActivityResultContract

首先我们需要了解有哪些自带的ActivityResultContract

  • StartActivityForResult 使用Intent在activity(fragment)间通信,返回ActivityResult
  • StartIntentSenderForResult 使用IntentSenderRequest. builder构造,可以返回带有activity的ActivityResult
  • RequestMultiplePermissions 获取多个动态权限,返回Map<String,boolean>数组
  • RequestPermission 获取单个动态权限,成功后返回true
  • TakePicturePreview 调用相机,拍照后返回bitmap图片信息
  • TakePicture 调用相机,拍照后将图片保存到开发者指定的Uri,返回true
  • TakeVideo 拍摄视频,将拍摄内容保存到开发者指定的Uri,返回缩略图
  • CaptureVideo 拍摄视频,将内容保存到开发者指定的Uri后,返回true
  • PickContact 弹出手机联系人列表,用户选中其中一个后,返回Uri
  • GetContent 访问原始数据,返回Uri,例如相册中的单个图片
  • GetMultipleContents 功能如上,返回Uri数组,例如相册中的多个图片
  • OpenDocument 访问文件数据,打开文件夹,返回单个文件的Uri
  • OpenMultipleDocuments 功能如上,返回多个文件的Uri
  • OpenDocumentTree 文件目录
  • 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()
            }

三、总结:

  1. 使用registerForActivityResult注册得到ActivityResultLauncher,在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。

  2. ActivityResultLauncher的注册实际上是通过ActivityResultRegistry注册获取的。

  3. ActivityResultRegistry有2个注册方法,其中一个并不需要LifecycleOwner,所以通过这个新的注册方法我们可以不遵守:在Activity必须在onStart及之前执行,在fragment必须在onCreate及以前执行。

  4. 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()
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TieJun~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值