美文网首页Android
Android 开发者必知必会的权限管理知识(转载)

Android 开发者必知必会的权限管理知识(转载)

作者: CysionLiu | 来源:发表于2017-08-08 10:49 被阅读63次

    导语

    感觉分析的挺全面,故转载,有小细节删改,原文请点击:
    原文在这里

    本文主要讲解了Android 权限管理方面几个点:

    • Android 权限背景知识;
    • 权限检查及权限兼容;
    • 跳转到app管理权限页面

    一、Android 权限背景知识

    提到Android 权限管理,业内人士都知道Google 在Android 6.0时提出了运行时权限管理机制,在Android 6.0之前,所申请的权限只需要在AndroidManifest.xml列举就可以,从而容易导致一些安全隐患,因此,在Android 6.0 时,Google 为了更好的保护用户隐私提出了新的权限管理机制(官网 :Working with System Permissions),同时将其分为两大类:

    (1)Normal Permissions

    Normal Permissions 一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;

    (2)Dangerous Permission

    Dangerous Permission一般是涉及到用户隐私的,需要用户进行授权(动态申请),比如读取SIM卡状态、访问通讯录、SD卡读写等。

    通过 adb shell pm list permissions -d -g 可以查看 Dangerous Permission (以权限组形式)

    Dangerous Permission group

    如上图所示 :Dangerous Permission 一般以 Permission group 形式存在,只要 Permission group中某一个 permission 被Granted,则整个Permission group下的权限均被Granted (目前是这样,以后规则说不定会变)。

    二、权限检查及权限兼容

    本节主要介绍介绍如何进行权限检查及权限兼容,主要分为以下几类:

    (1)targetSdkVersion>=23,终端设备是6.0(api 23)以上系统;

    安装的时候不会获得权限,在运行时向用户申请对应权限。这部分权限检查比较简单,不涉及权限兼容,使用官方方案就可以 ,使用 Context::checkSelfPermisson ,建议使用ContextCompat::checkSelfPermisson检查权限 即可 ,一般检查流程 如下:

    1 判断是否有对应权限
    (ContextCompat::checkSelfPermisson)

    2 判断是否需要解释对应权限用途(ActivityCompat::shouldShowRequestPermissionRationale)
    如果需要解释,则现实自定义权限界面即可

    3 不需要解释的话,直接请求对应权限
    (ActivityCompat::requestPermissions)

    上述情况较为简单,在此不再赘述。

    (2)targetSdkVersion<23,终端设备是6.0(api 23)以上系统;

    使用的是老的权限机制,在app 安装时会询问AndroidManifest.xml文件中的权限,但是用户可以在设置列表中关闭相关权限,这种情况可能会对app正常运行造成一定影响。

    (3) 终端设备系统小于6.0(api 23)

    大家可能要问,终端设备系统小于6.0情况还需要考虑吗,肯定是用的老的权限管理机制,在app 安装时会询问AndroidManifest.xml文件中的权限,用户关闭不了,真的是这样吗 ?

    答案是否定的,在实测中发现,目前有不少国产Rom 手机在6.0之前就有关闭权限的开关。这种情况也是我们兼容的对象。

    下面将会以自己开发过程中遇到的问题进行展开 ,目前企鹅FM支持免流了,需要使用READ_PHONE_STATE权限 (读取SIM卡状态),由于之前未对改权限是否关闭没有进行相关判断,因此收到了很多例因为上述权限关闭,导致免流失败的情况。

    适配过程如下 :

    (1)使用 try catch 来检查权限是否关闭

    想法很简单,如果改权限被用户禁止了,那肯定会异常,因此可以在catch 中做文章,结果发现这一招根本没有用,为啥了 ?因为使用 READ_PHONE_STATE 权限的方法内部已经try catch ,外面无法捕获,因此该方法失效。

    (2)ContextCompat::checkSelfPermisson

    既然在6.0 可以使用Context::checkSelfPermisson进行权限检查,那能否使用support v4 中的ContextCompat::checkSelfPermisson 方法了,试一下,发现在api 23 以下失效,为了探究原因,查看了ActivityCompat::requestPermissons 内部实现,如下

    ActivityCompat::requestPermissons

    内部权限检查方法在api 23 以下,使用的是 PackageManager::checkPermission,再去查看PackageManager::checkPermission方法,如下:发现只要权限在AndroidManifest.xml中注册过,均会认为该权限granted ,因此上述方法在api 23 以下也失效。

    PackageManager::checkPermission

    查阅相关资料和请教组内同事,发现Support V4 下面有一个专门检查权限的工具类PermissionChecker。

    (3)PermissionChecker

    查看PermissionChecker源码发现 ,PermissionChecker内部实际上使用的是AppOpsManagerCompt,而AppOpsManager是在api 19 加入进入的(AppOpsManager后面介绍)

    PermissionChecker::checkPermission

    进而查看AppOpsManagerCompat 内部实现

    AppOpsManagerCompat::permissionToOp

    IMPL实现如下:

    IMPL实现

    从上图可以看出:在api 23以下, AppOpsManagerImpl::permissionToOp 直接返回为null ,这直接导致api 23以下权限检查将会返回 granted ,因此,该方法在api 23 下,权限检查方法也会失效。

    (4)AppOpsManager

    API 19以上 ,Google 官方提供了 AppOpsManager 类来检查权限,看到这个api 时,脑海浮现出 “天无绝人之路啊”,里面有两个比较重要的方法 :AppOpsManager::checkOp(int op ,int uid ,String packageName) (hide方法)和AppOpsManager::checkOp(String op,int uid ,String packageName)(public 方法 ,api 23 以上可用),不经思考,直接写出了如下两个方法

    1)AppOpsManager::checkOp(int op ,int uid ,String packageName)

    需要使用反射

    2)AppOpsManager::checkOp(String op,int uid ,String packageName)

    API >= 23 才可以使用 :

    同时也仔细看了一下AppOpsManager 类介绍,并不是为开发者设计的,不过其他权限兼容可以使用这种方法,前提是 要看OP_*是在什么版本才有的,需做兼容方案 。

    (5)最后查看了几个第三方权限库(暂未看完)

    PermissionsDispatcher

    AndPermission

    三、跳转到app管理权限页面

    既然在这里讲解跳转到app 管理权限页面的方法,可想而之,事情绝对不太简单。Android 碎片化不仅在存在于ui适配 ,同样也存在于这里,导致我们无法使用同一种方式跳转到app管理权限页面(适配,Android 开发永远的痛)。

    那有没有办法可以简化适配工作,减少开发量,方法当然有,不过需要我们自己去总结和探索的,目前已有方法:

    (1)直接跳转到系统设置页

    Intent intent =newIntent();
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.fromParts("package",getPackageName(), null));
    
    try{
        startActivity(intent);
    }catch(Exception exception) {
        exception.printStackTrace();
    }
    

    记得要添加上 try catch ,不加可能会crash。这种方式就不需要适配各个厂商的不同版本rom,缺点是,用户只能跳转到系统设置页,然后去找对应app 的权限管理(总会有一些用户找不到)

    (2)站在前人的肩上

    引用前人经验:Android各大手机品牌手机跳转到权限管理界面 (未一一验证,毕竟没那么多手机)

    那是不是前人经验一定对了,那就不一定了,在当时可能是对的,在现在可能就行不通了,现在以MIUI跳转到app 权限管理页面为例进行说明。

    1)MIUI 6/7

    Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
    localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
    localIntent.putExtra("extra_pkgname", context.getPackageName());
    
    try{
        startActivity(intent);
    }catch(Exception exception) {
        exception.printStackTrace();
    }
    

    2)MIUI 8

    Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
    localIntent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
    localIntent.putExtra("extra_pkgname", context.getPackageName());
    
    try{
        startActivity(intent);
    }catch(Exception exception) {
        exception.printStackTrace();
    }
    

    对比1)和2)发现,在MIUI 6/7 和MIUI 8 上面,权限管理页面的activity名字不一样了,因此使用MIUI6/7的方法在MIUI8上就会失效,如果没有加上try catch ,就会直接crash。

    对于上述变化,作为一个开发者一般都是不知道的,即便通过反馈发现了这个问题,也有可能不知道对应的activity是什么,此刻要么搜索网上有没有类似解决方案,要么求助于对应rom 开发厂商的开发者论坛 (有时解决回应速度相当慢),那有没有更好的办法了,方法详见(3)部分。

    (3)查看某个ROM的某个版本的权限管理页面的activity

    这里以华为p8为例简要说明,详细步骤如下:

    1)通过设置找到对应app的权限管理页面,如下:

    企鹅fm在华为p8上的权限管理页面

    2)找到对应页面的activity

    方法一:通过add 工具查看栈顶Activity

    adb shell dumpsys activity | grep "mFocusedActivity"

    详细的堆栈信息见命令窗口

    方法二:使用Activity Tracer工具

    相关文章

      网友评论

        本文标题:Android 开发者必知必会的权限管理知识(转载)

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