本篇是 Android 快速开发框架 ardf
的第三篇,将主要介绍在 Android 开发中对权限申请的封装使用,随着 Android 系统的不断升级,Google 对权限的使用越来越严格,用户也越来越重视权限的授权,开发中很多权限都需要动态申请并取得用户授权后才能正常使用,这就导致开发中对权限申请的操作越来越频繁,那么一个对权限申请的好的封装就能大大的节省开发者的工作量、提升开发效率。 在 ardf
中则是基于 Google 提供的 EasyPermission
库进行二次封装,将权限申请的复杂处理进行简化,使用时只需关注要申请的权限以及申请成功、申请失败后的业务处理即可。
EasyPermission 是一个简化基本的系统权限逻辑的库,可用于在Android M或者更高版本上。关于 EasyPermission 更多介绍见:EasyPermission[1]
ardf
更多文章: Android基于DataBinding封装RecyclerView实现快速列表开发 Android基于DataBinding+Koin实现MVVM模式页面快速开发框架
按照惯例,首先还是来看看封装后的使用效果。
在项目 Module 的 build.gradle 中添加依赖,如下:
dependencies {
implementation 'com.loongwind.ardf:base:1.1.0'
}
因 ardf
有用到 DataBinding ,需要开启 DataBinding,启用方式如下:
android {
...
buildFeatures {
dataBinding true
}
}
同时在插件中添加 kotlin-kapt
的插件,如下:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
// 添加 kotlin-kapt 插件
id 'kotlin-kapt'
}
配置完成后,点击 Sync Now
同步 build.gradle 配置生效后即可进行代码开发。
在 ardf
提供的 BaseActivity
子类中调用 requestPermissions
方法即可申请权限,代码如下:
// 要申请的权限数组
val permissions = arrayOf(
Manifest.permission.CALL_PHONE,
Manifest.permission.CAMERA)
//申请权限
requestPermissions(permissions) {
// 权限申请成功后的业务处理
toast("权限申请成功")
}
只需调用 requestPermissions
传入要申请的权限即可,在回调中进行权限申请成功后的业务处理,运行效果如下:
除了继承
BaseActivity
外,还可以继承BaseBindingActivity
、BaseBindingViewModelActivity
、BaseFragment
、BaseBindingFragemnt
、BaseBindingViewModelFragment
关于其他几个基类的使用见:Android基于DataBinding+Koin实现MVVM模式页面快速开发框架
只需在请求时传入 onDenied
回调即可进行权限申请失败的处理,如下:
// 要申请的权限列表
val permissions = arrayOf(
Manifest.permission.CALL_PHONE,
Manifest.permission.CAMERA)
// 权限申请失败回调
val onDenied = {
toast("权限申请失败")
}
//申请权限
requestPermissions(permissions, onDenied = onDenied) {
toast("权限申请成功")
}
运行效果如下:
用户在多次拒绝同一权限后,再次申请权限将不再弹出用户允许权限的弹框,此时只能引导用户在系统的应用设置里手动开启应用的相关权限,ardf
也对该场景做了封装,只需在申请时传入 showPermanentlyDeniedDialog
参数即可弹出引导用户去设置界面授权的弹框,使用如下:
// 要申请的权限列表
val permissions = arrayOf(Manifest.permission.CALL_PHONE, Manifest.permission.CAMERA)
// 权限申请失败回调
val onDenied = {
toast("权限申请失败")
}
//申请权限
requestPermissions(
permissions,
// 弹出引导设置界面的弹框
showPermanentlyDeniedDialog = true,
onDenied = onDenied) {
toast("权限申请成功")
}
运行效果如下:
弹出框的提示可以通过配置字符串资源进行修改,对应字符串资源如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="ardf_permission_setting_title">权限设置</string>
<string name="ardf_permission_setting_describe">应用需要该功能,请在设置中赋予其权限</string>
</resources>
在 Android 开发中权限申请一般是在 Activity 或 Fragment 进行调用,然后在回调或 Activity 返回结果里判断权限申请是否被允许再进行对应的业务逻辑处理。基于此可以封装 BaseActivity
/ BaseFragment
对权限申请和回调进行统一处理。 整体结构如下:
permission.drawio.png
ardf
创建了一个 PermissionHelper
,请求和回调都代理到 PermissionHelper
中进行处理,在 PermissionHelper
再调用 EasyPermission
库的相关方法。 BaseActivity
源码如下:
open class BaseActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks,
EasyPermissions.RationaleCallbacks {
/**
* 权限申请
* @param permissions 要申请的权限列表
* @param showPermanentlyDeniedDialog 是否引导去应用设置赋予权限
* @param onDenied 授权拒绝回调
* @param onGranted 授权成功回调
*/
fun requestPermissions(
permissions: Array<out String>,
showPermanentlyDeniedDialog: Boolean = false,
onDenied: (() -> Unit)? = null,
onGranted: () -> Unit
) {
PermissionHelper.requestPermissions(this, permissions, showPermanentlyDeniedDialog, onDenied, onGranted)
}
/**
* 权限申请结果回调
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}
/**
* 权限申请失败
*/
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
PermissionHelper.onPermissionsDenied(this, requestCode, perms)
}
/**
* 权限申请成功
*/
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
PermissionHelper.onGranted(requestCode, perms)
}
/**
* 跳转设置界面授权后返回结果处理
*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
PermissionHelper.onActivityResult(this, requestCode, resultCode, data)
}
/**
* 权限申请提示弹出框点击拒绝
*/
override fun onRationaleDenied(requestCode: Int) {
PermissionHelper.onRationaleDenied(requestCode)
}
/**
* 权限申请提示框点击确定
*/
override fun onRationaleAccepted(requestCode: Int) {
}
}
在 BaseActivity
中创建了 requestPermissions
方法并实现了 EasyPermission 的相关回调接口以及 onActivityResult
方法,在这些方法中都直接调用了 PermissionHelper
中的对应方法。 requestPermissions
方法:
fun requestPermissions(
activity: Activity,
permissions: Array<out String>,
showPermanentlyDeniedDialog : Boolean,
onDenied: (() -> Unit)?,
onGranted: () -> Unit
) {
//判断是否有权限
if (EasyPermissions.hasPermissions(activity, *permissions)) {
// 已有权限直接调用 onGranted
onGranted()
} else {
// 根据传入参数构建 PermissionRequestModel
val requestModel = PermissionRequestModel(
mutableListOf(*permissions),
showPermanentlyDeniedDialog,
onDenied ?: { defaultDeniedHandle(activity) },
onGranted
)
// 将 PermissionRequestModel 添加到 list 中
addPermissionRequest(requestModel)
// 调用 EasyPermissions 请求权限
EasyPermissions.requestPermissions(activity, activity.getString(R.string.ardf_permission_request_hint), requestModel.requestCode, *permissions)
}
}
实现逻辑如下:
onGranted
方法;PermissionRequestModel
并调用 addPermissionRequest
方法将其添加到 list 中;EasyPermissions.requestPermissions
请求授权;addPermissionRequest
方法实现如下:
private val permissionsRequests = ArrayList<PermissionRequestModel>()
private fun addPermissionRequest(permissionRequestModel: PermissionRequestModel){
permissionsRequests.add(permissionRequestModel)
}
PermissionRequestModel
是用于封装请求授权参数的,定义如下:
data class PermissionRequestModel(
val permissions: MutableList<String>,
val showPermanentlyDeniedDialog : Boolean,
val onDenied:()->Unit,
val onGranted:()->Unit
) {
companion object{
// 权限申请的请求 Code
private var permissionsRequestCode = 20212
}
// 请求 Code 初始化时获取 permissionsRequestCode 后为其加 1
val requestCode = permissionsRequestCode ++
}
除了封装请求参数以外还定义了请求的 Code ,并且每次创建后都会递加 ,保证每次请求的 Code 不会重复,在调用 EasyPermissions.requestPermissions
时传的 requestCode
参数就是通过 PermissionRequestModel
获取的。 onGranted
用于处理用户授权成功后的逻辑,代码如下:
fun onGranted(requestCode: Int, perms: List<String>) {
// 根据 requestCode 找到请求的 Model
val requestModel = getRequestModel(requestCode)
requestModel?.let {
// 调用成功回调方法
it.onGranted.invoke()
// 从列表中移除请求 Model
permissionsRequests.remove(it)
}
}
如果用户拒绝授权则会进入 onPermissionsDenied
方法:
fun onPermissionsDenied(activity: Activity, requestCode: Int, perms: MutableList<String>) {
// 根据 requestCode 获取请求的Model
getRequestModel(requestCode)?.let {
//用户选择了不再提醒或多次拒绝且 showPermanentlyDeniedDialog 为 true,则引导用户去设置界面开启权限
if (EasyPermissions.somePermissionPermanentlyDenied(activity, perms) && it.showPermanentlyDeniedDialog) {
AppSettingsDialog.Builder(activity)
.setRequestCode(requestCode)
.setTitle(R.string.ardf_permission_setting_title)
.setRationale(R.string.ardf_permission_setting_describe)
.build().show()
} else {
// 权限获取失败处理
onDenied(requestCode, perms)
}
}
}
如果用户选择了不再提醒或者多次拒绝且 showPermanentlyDeniedDialog 传入的为 true 则弹出引导框引导用户去设置界面授权,否则调用 onDenied
进行失败的处理:
/**
* 权限拒绝的处理
*/
private fun onDenied(requestCode: Int, perms: List<String>) {
// 根据 requestCode 获取请求的 Model
val requestModel = getRequestModel(requestCode)
requestModel?.let {
// 调用失败的回调
it.onDenied.invoke()
// 移除请求的 Model
permissionsRequests.remove(it)
}
}
如果用户在设置界面授权后,返回则会进入onActivityResult
方法:
/**
* 用户选择了拒绝不再提醒后引导去设置界面开启权限后返回界面的处理
*/
fun onActivityResult(context: Context, requestCode: Int, resultCode: Int, data: Intent?) {
//通过 requestCode 找到请求 Model
getRequestModel(requestCode)?.let{
// 判断是否已授权权限
if (EasyPermissions.hasPermissions(context, *it.permissions.toTypedArray())) {
onGranted(requestCode, it.permissions)
} else {
onDenied(requestCode)
}
}
}
同样是现在找到请求的 Model,然后判断申请的权限是否已授权,然后进行授权成功和失败的处理。
本文主要介绍了 ardf
(Android 快速开发框架)中基于EasyPermission 的权限请求的封装使用方法,并通过源码解析详细介绍了其实现原理,进一步提高 Android 开发的效率。
源码地址:ardf[2] mavenCentral:
com.loongwind.ardf:base:1.1.0
[1]
EasyPermission: https://github.com/googlesamples/easypermissions
[2]
ardf: https://github.com/loongwind/ardf