Android基于EasyPermission封装实现快速权限申请

1,619 阅读7分钟

1. 前言

本篇是 Android 快速开发框架 ardf的第三篇,将主要介绍在 Android 开发中对权限申请的封装使用,随着 Android 系统的不断升级,Google 对权限的使用越来越严格,用户也越来越重视权限的授权,开发中很多权限都需要动态申请并取得用户授权后才能正常使用,这就导致开发中对权限申请的操作越来越频繁,那么一个对权限申请的好的封装就能大大的节省开发者的工作量、提升开发效率。

ardf 中则是基于 Google 提供的 EasyPermission 库进行二次封装,将权限申请的复杂处理进行简化,使用时只需关注要申请的权限以及申请成功、申请失败后的业务处理即可。

EasyPermission 是一个简化基本的系统权限逻辑的库,可用于在Android M或者更高版本上。

关于 EasyPermission 更多介绍见:EasyPermission

ardf更多文章:

Android基于DataBinding封装RecyclerView实现快速列表开发

Android基于DataBinding+Koin实现MVVM模式页面快速开发框架

2. 使用

按照惯例,首先还是来看看封装后的使用效果。

2.1 项目配置

在项目 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 配置生效后即可进行代码开发。

2.2 权限申请

ardf提供的 BaseActivity 子类中调用 requestPermissions方法即可申请权限,代码如下:

// 要申请的权限数组
val permissions = arrayOf(
    Manifest.permission.CALL_PHONE, 
    Manifest.permission.CAMERA)
//申请权限
requestPermissions(permissions) {
    // 权限申请成功后的业务处理
    toast("权限申请成功")
}

只需调用 requestPermissions传入要申请的权限即可,在回调中进行权限申请成功后的业务处理,运行效果如下:

除了继承 BaseActivity 外,还可以继承 BaseBindingActivityBaseBindingViewModelActivityBaseFragmentBaseBindingFragemntBaseBindingViewModelFragment

关于其他几个基类的使用见:Android基于DataBinding+Koin实现MVVM模式页面快速开发框架

2.3 申请失败处理

只需在请求时传入 onDenied回调即可进行权限申请失败的处理,如下:

// 要申请的权限列表
val permissions = arrayOf(
    Manifest.permission.CALL_PHONE, 
    Manifest.permission.CAMERA)

// 权限申请失败回调
val onDenied = {
    toast("权限申请失败")
}
//申请权限
requestPermissions(permissions, onDenied = onDenied) {
    toast("权限申请成功")
}

运行效果如下:

2.4 用户多次拒绝处理

用户在多次拒绝同一权限后,再次申请权限将不再弹出用户允许权限的弹框,此时只能引导用户在系统的应用设置里手动开启应用的相关权限,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>

3. 源码解析

在 Android 开发中权限申请一般是在 Activity 或 Fragment 进行调用,然后在回调或 Activity 返回结果里判断权限申请是否被允许再进行对应的业务逻辑处理。基于此可以封装 BaseActivity/ BaseFragment对权限申请和回调进行统一处理。

整体结构如下:

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,然后判断申请的权限是否已授权,然后进行授权成功和失败的处理。

4. 总结

本文主要介绍了 ardf(Android 快速开发框架)中基于EasyPermission 的权限请求的封装使用方法,并通过源码解析详细介绍了其实现原理,进一步提高 Android 开发的效率。

源码地址:ardf

mavenCentral:com.loongwind.ardf:base:1.1.0

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿