Android权限最佳实践

作者: 光源_Android | 来源:发表于2016-05-20 20:25 被阅读8564次

    提示:这篇文章写的比较早了,一直没时间“翻新”,现在看来存在一些问题,等后面会重新写一篇。读者简单参考即可。

    前言

    大家好我是光源

    从 Android6.0 开始Android的权限模式有了一番更改,从安装时一股脑列给用户,到运行时动态申请权限。对于 Android开发者而言,这是一个重要的变更。

    讨论这个问题之前,我们得先检查一下项目的 target version。如果是 23及以上,则必须得适配新权限模式;如果是 23之下,则还是统一在安装时全部申请权限——即便如此,使用Android 6.0及以上系统的用户还是可以在设置中去关闭你的某些权限。当权限被关闭时,不会导致你的应用直接崩溃,但是会导致你获取到的返回值为null 或者 0,这个是需要注意的地方。

    以下是 Android 官网给出的最佳实践,草草翻译,有不妥之处还请斧正。

    正文

    app很容易用海量的权限请求淹没用户。如果用户发现 app 影响使用或者担忧 app 滥用用户个人信息,他们可能不再使用或者完全删除这个app。以下最佳实践能帮助你避免这种糟糕的用户体验。

    优先使用 Intent

    在很多情况下,你都可以选择两种方式来为你的app完成任务。你可以让你的app去请求权限来自己完成操作,或者你也可以让 app 利用 intent 让其他 app 进行这项工作。

    举个例子假如你的 app 需要用设备中的相机来拍照,你的 app 可以请求 CAMERA 权限 以直接使用相机组件,然后你的 app 将使用相机组件接口去控制相机并拍照。这个方法使你的 app 有完整的摄影进程控制权,让你将相机UI纳入app中【译者注:即可以完全自定义相机UI】。

    然而,如果你不需要这么完整的控制,你可以使用 ACTION_IMAGE_CAPTURE intent 去请求图片。当你发送这个intent,系统让用户选择一个相机 app(如果还没有默认的相机 app)。 用户用选中的相机 app 拍照后,它将照片反回给你的 app 的 onActivityResult() 方法。

    同样得,如果你需要打电话、读写用户的通讯录等等,你可以通过创建合适的 intent 来实现或者请求权限后直接访问合适的组件。二者各有利弊。

    如果你使用权限:

    • 你的 app 在执行该操作时对用户体验有完整的掌控。但是这么宽泛的控制增加了任务的复杂度,因为你需要自行设计合适的UI。
    • 不管在运行时还是刚安装时,用户倾向于只进行一次权限操作,此后,你的 app 就可以进行操作无需用户额外授权。但是,如果用户没有允许权限或者后来取消了,你的 app 将一直无法进行该操作。

    如果你使用 intent:

    • 你不需要为该操作设计UI。处理 intent 的 app 提供了UI。然而,这意味着你无法掌控这部分用户体验。用户可能跟你一无所知的 app 打交道。
    • 如果用户没有处理该操作的默认应用,系统会让用户选择一个。如果用户没有指定一个默认处理,每次进行该操作都会弹出额外的选择对话框。

    只�申请必须的权限

    你每次申请权限都在迫使用户做决定,所以你应该减少发出这些请求的次数。如果用户使用的是 Android 6.0 及之后的系统,每次用户使用某些 app 中需要权限的特性,app 就必须因申请权限而打断用户。如果用户使用 6.0之前的系统,在安装 app 时用户必须得允许每一个权限。如果权限清单太长或者看起来不合适,用户可能会决定停止安装你的 app。因为以上种种,你最好减少申请的权限数量。

    不要压垮用户

    如果用户使用 Android6.0及之后的系统,在运行 app时需要授权。如果你把一大堆权限申请一次性抛给用户,你会把他们压垮进而删除你的应用。因而你应该只在必要时才去请求权限。

    在某些情况下,对你的 app 而言一个或多个权限是完全必要的,在 app 启动时就去请求这些权限是有意义的。比如你做了一个摄影 app,它需要使用相机。当用户第一次启动该 app 时,他们自然不会惊讶请求相机的权限。但是如果这个app还有通过用户的通讯录分享图片的功能的话,你就不应该在第一次启动时获取 READ_CONTACTS 权限。而应该等到用户使用”分享“功能时再请求权限。

    解释你为何需要权限

    当你调用[requestPermissions()](https://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int)) 表明你的 app 需要什么权限时系统会显示权限对话框,但是没有说为什么。在某些情况下,用户会感到困惑。在调用[requestPermissions()](https://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int)) 前向用户解释你的 app 为何需要这些权限是个好主意。

    比如,一个拍照 app 可能会需要定位服务以便给图片打上地理位置标签。一个普通用户可能无法理解一张图片可以包含位置信息,然后会困惑为什么拍照 app 会需要知道他的位置。所以在这种情况下,app 在调用[requestPermissions()](https://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int)) 前告诉用户这个特性是个好主意。

    告知用户的一个不错的方法是把这些权限请求合并进 app 引导中。app 引导可以依次介绍 app 的特性,在做这些的同时,可以解释需要哪些权限。例如,拍照 app的引导可以示范”通过通讯录分享照片“的功能,告知用户他们需要授权 app 查看用户的通讯录。然后该 app 可以调用[requestPermissions()](https://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int)) 请求读取通讯录。当然,不是所有用户都会跟从引导,所以你依然需要检查确认并且在 app 的日常使用中请求权限。

    测试两种权限模式

    从 Android 6.0 开始,用户在应用运行时允许或者拒绝权限而不是在应用安装时,这导致你需要测试各种条件下app 的表现。

    在 Android 6.0之前,你可以合理假定你的 app 一直可运行,它拥有所有在 app manifest中声明的权限。在新的权限模式下,你不能这么认为了。

    以下提示将帮助你验证 Android 6.0及以上系统的权限相关代码问题。

    • 验证 app 当前权限以及相关代码路径。

    • 测试用户通过权限保护服务和数据。

    • 测试多种权限分别被允许、拒绝的组合情况。比如,相机 app 可能在 manifest 文件中声明了CAMERAREAD_CONTACTSACCESS_FINE_LOCATION 权限。你应该测试每个权限打开和关闭的情况来确保 app 可以优雅地处理所有权限配置情况。记住,从 Android 6.0开始用户可以打开或关闭任意一个app的权限,即便是 targets API 在 22及以下的。

    • 使用 adb 工具通过命令行管理权限:

      • 以组的形式列出权限和状态:

        $ adb shell pm list permissions -d -g
        
      • 允许或拒绝一个或多个权限:

        $ adb shell pm [grant|revoke] <permission-name> 
        
    • 为使用权限的服务分析 app

    后记

    以上是 Android 官网给出的最佳实践。个人觉得这篇文章主要从用户体验角度来阐述权限的最佳实践,还缺乏代码层面的实践,因此扩展一下。

    权限相关代码最佳实践

    第零步,在manifest 文件中声明所需的权限。

    第一步,检测是否拥有权限。

    API 23 or above all version
    checkSelfPermission ContextCompat.checkSelfPermission()

    被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED

    第二步,检测是否需要显示申请权限对话框。如果第一步返回没有权限的话,则我们需要去申请权限,但是申请权限之前,需要显示查看用户是否已经禁止了申请权限对话框,如果禁止的话,我们用第三步的代码将没有任何效果。

    API 23 or above all version
    shouldShowRequestPermissionRationale ActivityCompat.shouldShowRequestPermissionRationale / FragmentCompat.shouldShowRequestPermissionRationale

    返回为 truefalse ,在 6.0以下恒定为 false ,这个不用多解释了吧,在 6.0以下还没有系统默认对话框存在,肯定不弹了。

    第三步,显示提示框去申请权限。

    API 23 or above all version
    requestPermissions ActivityCompat.requestPermissions/ FragmentCompat.requestPermissions()

    发出请求后,会在 onRequestPermissionsResult 方法中获得回调结果。

    以目前 Android系统的占有率分布,最低建议支持4.0,所以我们可以直接使用兼容库中的三个方法以兼容所有版本。下面是一个简单的需求,向SD卡中写入数据:

        /**
         * 向SD卡插入数据
         */
        private void insertDataToSDCard() {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                //拥有权限
                //TODO 向SD卡写数据
            } else {
                //没有权限,判断是否会弹权限申请对话框
                boolean shouldShow = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
                if (shouldShow) {
                    //申请权限
                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_REQUEST_PERMISSION);
                } else {
                    //被禁止显示弹窗
                    //TODO 显示对话框告知用户必须打开权限
                }
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            if (requestCode == REQUEST_CODE_REQUEST_PERMISSION) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //permission granted
                    //TODO 向SD卡写数据
                } else {
                    //permission denied
                    //TODO 显示对话框告知用户必须打开权限
                }
            }
        }
    

    可见,通过兼容库去做权限模式兼容还是挺简单明了的。考虑到扩展性,完全可以�把权限判断逻辑写成一个通用的工具类。

    最后,推荐一个第三方库hotchemi’s PermissionsDispatcher,这个也是参考资料中看到的一个不错的第三方库,有兴趣可以看看。

    参考资料

    1. Permissions Best Practices
    2. Android M 新的运行时权限开发者需要知道的一切

    相关文章

      网友评论

      • 1024HOPE:如果targetSDKVersion < 23,只要在manifest里面注册了权限,checkSelfPermissions的时候始终放回true,而且也没有相应的api来判断用户是否禁止、获取还是询问权限的方式;------------有三种手机操作系统:ios, Android,中国android
        光源_Android:@open_your_eyes 这篇文章还是老早前写的,后面一直没时间写新的出来。不过“不一样”的地方能否指出是哪里?
        拾大花sdf:有些地方和安卓开发者上不一样,大家看下
        https://developer.android.com/training/permissions/requesting.html?hl=zh-cn
        光源_Android:的确如此。。不过厂商在低版本自定义的权限控制跟本文所说的权限不是一个体系。而在高版本定制的一些权限弹窗特性(比如小米和华为就对权限弹窗做了默认项的修改),只能一一适配了。
      • Jessewo:ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED)
        检测通讯录权限, 不论是授权还是禁止, 结果都为true. 检测失败
      • aef9b848dcba:赞一个,有机会上7.0的话试一下

      本文标题:Android权限最佳实践

      本文链接:https://www.haomeiwen.com/subject/wgvnrttx.html