最容易理解的Android6.0动态权限申请教程
1、前言
这已经是N年前的知识点了,但是我一直以来都有点逃避学习这个,而且印象中很麻烦,后来也不间断有学习过一点,但是一直没认真去用过,所以对这个android6.0的动态权限申请一直是不清楚的状态,而且项目中用到动态权限申请的时候我就使用RxPermission来实现。今天心血来潮整了一下,发现其实原生的动态权限申请也没有想象中的麻烦,还是很简单的,所以这里记录一下,以防不用的话后面又忘了。
2、动态权限申请详解
总的来说,动态权限申请分两步:
- 清单文件中声明需要的权限
- 在代码中动态申请权限
本来感觉在代码中动态申请权限就行了,清单文件里应该就不用声明权限了吧?其实是需要的,如果清单文件中不声明的话,动态申请权限时是申请不到的,为什么需要在清单文件也声明权限呢?答:假如你的app安装在Android6.0以下的手机上,这些手机是没有动态权限申请的,则还是走清单文件申请权限这一套旧规则。
这里着重讲解代码中如何动态申请权限,先画一个流程图,通过流程图可以很简地理解申请权限的流程:
如上流程图写的比较详细,看着好似需要写好多代码,其实并不是,代码特别简单,关键代码如下:
- 判断是否拥有某权限:
ActivityCompat.checkSelfPermission(context, permission)
- 申请权限:
ActivityCompat.requestPermissions(activity, permissions, requestCode)
- 权限申请结果回调函数:
onRequestPermissionsResult(requestCode, permissions, grantResults)
- 判断用户是否选择了不再提示:
ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
完整示例代码如下:
const val PERMISSIONS_CAMERA = 0x1
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestPermission()
}
private fun init() {
// TODO 得到权限后要做的事情
}
private fun requestPermission() {
val hasCameraPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
if (hasCameraPermission) {
init()
} else {
requestCameraPermission()
}
}
private fun requestCameraPermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSIONS_CAMERA)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_CAMERA) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 已经授权了,开始使用功能吧
init()
return
}
// 权限被拒绝
val shouldShowRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
if (shouldShowRequestPermissionRationale) {
// 用户拒绝了摄像头权限,应该解释一下为什么需要此权限
AlertDialog.Builder(this)
.setTitle("解释需要此权限的理由")
.setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")
.setPositiveButton("确定") { dialog, which -> requestCameraPermission() }
.setNegativeButton("取消", null)
.setCancelable(false)
.create()
.show()
} else {
// 用户拒绝了摄像头权限,并且勾选了不再提示
AlertDialog.Builder(this)
.setTitle("打开权限步骤提示")
.setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")
.setPositiveButton("确定", null)
.setCancelable(false)
.create()
.show()
}
}
}
}
第一次运行时,效果如下:
系统会弹出申请权限对话框:
注:第一次申请权限时,没有不再提示的选项哦!
如果用户选择禁止,此时我们就会弹出自定义的对话框,以给用户解释我们为什么需要这个权限,如下:
此时,如果用户点击取消,则我们不做任何操作,如果用户选择确定,则我们再一次申请权限,此时系统会再次弹出系统的申请权限对话框,如下:
注意,此时是第二次申请权限,所以多了一个”禁止后不再提示“的选项。如果用户选择“禁止”按钮,则还是走之前的流程,如果用户选择了“禁止后不再提示”,则弹另一个我们自定义的对话框,以告诉用户如果你还想打开此权限的话只能到设置里去打开了,如下:
此时,你再申请权限时不会再弹出系统的申请权限的对话框了,因为用户选择了不再提示,但是我们自定义的这个对话框还是可以弹出来的,以告诉用户可以在设置中来打开此权限,如果用户到设置中打开了权限,则开开心心,如果用户在设置打开了权限然后想了一下又关掉了,则此时的权限状态恢复到初始化状态,比如你申请权限时,系统会认为你是第一次申请,会弹出系统权限申请对话框,问你是否允许权限,跟开头的流程一模一样的。
3、动态申请权限流程优化
再回顾一下动态申请权限的流程:
这里最前的一步,先判断是否拥有权限,其实可以不判断的,直接申请权限,这样的结果就是,如果有权限,则不弹任何对话框,在获取结果的回调函数中将会得到权限已授权的结果,如果申请权限时该权限没有被授权,则还是走原来的流程。虽然先判断没权限再申请权限好像正规一点、效率也更高一点,但是为了简单,这点效率的影响应该微乎其微。
总结一下动态申请权限的步骤就更简单了:
- 申请权限
- 获取申请权限结果,如果权限已授权,则做该做的事,如果权限没被授权,则判断用户是选择了“禁止”还是选择了“禁止后不再提示”,并据此弹出对应的自定义对话框。
优化后的代码如下:
const val PERMISSIONS_CAMERA = 0x1
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestCameraPermission()
}
private fun init() {
// TODO 得到权限后要做的事情
}
private fun requestCameraPermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSIONS_CAMERA)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSIONS_CAMERA) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 已经授权了,开始使用功能吧
init()
return
}
// 权限被拒绝
val shouldShowRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
if (shouldShowRequestPermissionRationale) { // 应该解释一下为什么需要此权限
// 用户拒绝了摄像头权限
AlertDialog.Builder(this)
.setTitle("解释需要此权限的理由")
.setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")
.setPositiveButton("确定") { dialog, which -> requestCameraPermission() }
.setNegativeButton("取消", null)
.setCancelable(false)
.create()
.show()
} else {
// 用户拒绝了摄像头权限,并且勾选了不再提示
AlertDialog.Builder(this)
.setTitle("打开权限步骤提示")
.setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")
.setPositiveButton("确定", null)
.setCancelable(false)
.create()
.show()
}
}
}
}
4、RxPermissions的使用
了解了动态权限的申请之后,我们就会发现,每次申请权限,要写一些模板代码在Activity中,能不能对申请权限的代码进行封装呢?答案是可以的,RxPermissions库就实现了这样的功能,它的原理是给当前Activity添加一个*面的Fragment,在这个Fragment中申请权限,并在这个Fragment中接收申请权限的结果,所以可以在这个Fragment中封装申请权限的逻辑。并且该库使用RxJava进行封装,使我们更容易的拿到申请权限的结果。
RxPermissions官网:https://github.com/tbruyelle/RxPermissions
添加RxPermissions依赖
在项目的根目录下的build.gradle添加如下配置:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
在module目录下的build.gradle添加如下配置:
dependencies {
implementation 'com.github.tbruyelle:rxpermissions:0.12'
}
我们知道RxPermissions是用到了RxJava的,通过查看依赖传递,我们发现rxpermissions中有依赖RxJava,但是并没有依赖RxAndroid,我们知道在Android中使用RxJava必须要依赖RxAndroid的,所以还需要添加RxAndroid依赖,如下:
dependencies {
implementation 'com.github.tbruyelle:rxpermissions:0.12'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
}
使用RxPermissions完成前面的例子
接下来我们使用RxPermissions来完成前面的例子,看看是不是更简洁呢,代码如下:
class MainActivity : AppCompatActivity() {
private val mRxPermissions = RxPermissions(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestCameraPermission()
}
private fun init() {
// TODO 得到权限后要做的事情
}
private fun requestCameraPermission() {
mRxPermissions
.requestEach(Manifest.permission.CAMERA)
.subscribe { permission ->
when {
permission.granted -> {
// 已经授权了,开始使用功能吧
init()
}
permission.shouldShowRequestPermissionRationale -> {
// 用户拒绝了摄像头权限,应该解释一下为什么需要此权限
AlertDialog.Builder(this)
.setTitle("解释需要此权限的理由")
.setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")
.setPositiveButton("确定") { dialog, which -> requestCameraPermission() }
.setNegativeButton("取消", null)
.setCancelable(false)
.create()
.show()
}
else -> {
// 用户拒绝了摄像头权限,并且勾选了不再提示
AlertDialog.Builder(this)
.setTitle("打开权限步骤提示")
.setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")
.setPositiveButton("确定", null)
.setCancelable(false)
.create()
.show()
}
}
}
}
}
可以看到,使用RxPermission后代码更紧凑了,当然了,用法上还有更多的好处,可以查看RxPermission官网的说明教程。
封装RxPermissions
这里虽然使用RxPermission简单了许多,但是代码还是入侵了Activity,一个申请权限的代码没什么技术含量,但是代码也不算少,影响阅读Activity的逻辑,所以这个权限申请可以封装到工具类中,或者写到Presenter中都是可以的,例如我们封装到工具类中,示例如下,会看到Activity就清爽多了:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
PermissionsHelper.requestCameraPermission(this, ::init)
}
private fun init() {
// TODO 得到权限后要做的事情
}
}
object PermissionsHelper {
fun requestCameraPermission(activity: FragmentActivity, callback: () -> Unit) {
RxPermissions(activity)
.requestEach(Manifest.permission.CAMERA)
.subscribe { permission ->
when {
permission.granted -> {
// 已经授权了,开始使用功能吧
callback()
}
permission.shouldShowRequestPermissionRationale -> {
// 用户拒绝了摄像头权限,应该解释一下为什么需要此权限
AlertDialog.Builder(activity)
.setTitle("解释需要此权限的理由")
.setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")
.setPositiveButton("确定") { _, _ -> requestCameraPermission(activity, callback) }
.setNegativeButton("取消", null)
.setCancelable(false)
.create()
.show()
}
else -> {
// 用户拒绝了摄像头权限,并且勾选了不再提示
AlertDialog.Builder(activity)
.setTitle("打开权限步骤提示")
.setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")
.setPositiveButton("确定", null)
.setCancelable(false)
.create()
.show()
}
}
}
}
}
本文地址:https://blog.csdn.net/android_cai_niao/article/details/109637253