本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/OQRHEufCUXBA3d3DMZXMKQ

导语

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

  1. Android 权限背景知识;
  2. 权限检查及权限兼容;
  3. 跳转到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)

需要使用反射 :

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

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

API >= 23 才可以使用 :

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

在实测中发现,api 低于23时 ,OP_READ_PHONE_STATE =51 找不到,导致反射失败。

仔细察看了一下 6.0 (API 23 )_NUM_OP = 62,如下,为何找不到51 了

6.0 (API 23 )_NUM_OP = 62

难不成 每个版本还不一样,查看其他版本,验证了这个想法:

**5.1.1 (API 22 )_NUM_OP = 48**

5.1.1

Op个数限制 (需要找到对应源码说明**)详情 参看 :

查看Android 对应版本 _NUM_OP (目前最高支持5.1.1版本)

此时,OP_READ_PHONE_STATE = 51 在6.0(API 23)以下,通过反射是找不到的,因此对于READ_PHONE_STATE权限检查仅限于6.0及6.0以上。

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

public class AppOpsManager

extends Object

API for interacting with "application operation" tracking.

This API is not generally intended for third party application developers; most features are only available to system applications. Obtain an instance of it through Context.getSystemService with Context.APP_OPS_SERVICE.

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

  1. PermissionsDispatcher

  2. 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"  

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

更为详细的堆栈信息

详细的堆栈信息

方法二:使用Activity Tracer工具

使用方法:可参见我之前的文章 :Android开发—— 小工具,大效率

使用Activity Tracer查看权限管理页面对应的activity

文中引用链接:

  1. Working with System Permissions
    https://developer.android.com/training/permissions/index.html
  2. 查看Android 对应版本 _NUM_OP
    http://grepcode.com/search?query=android.app.AppOpsManager&n=
  3. PermissionsDispatcher
    https://github.com/hotchemi/PermissionsDispatcher
  4. AndPermission
    https://github.com/yanzhenjie/AndPermission
  5. Android各大手机品牌手机跳转到权限管理界面
    http://www.jianshu.com/p/b5c494dba0bc
  6. Android开发—— 小工具,大效率
    http://www.jianshu.com/p/672d64fdc486

更多精彩内容欢迎关注腾讯 Bugly的微信公众账号:

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

Android 开发者必知必会的权限管理知识的更多相关文章

  1. Android程序员必知必会的网络通信传输层协议——UDP和TCP

    1.点评 互联网发展至今已经高度发达,而对于互联网应用(尤其即时通讯技术这一块)的开发者来说,网络编程是基础中的基础,只有更好地理解相关基础知识,对于应用层的开发才能做到游刃有余. 对于Android ...

  2. 迈向高阶:优秀Android程序员必知必会的网络基础

    1.前言 网络通信一直是Android项目里比较重要的一个模块,Android开源项目上出现过很多优秀的网络框架,从一开始只是一些对HttpClient和HttpUrlConnection简易封装使用 ...

  3. Android必知必会-使用okhttp的PUT方式上传文件

    注:如果移动端排版有问题,请看 简书版 (<-点击左边),希望CSDN能更好的支持移动端. 背景 公司的文件上传接口使用PUT协议,之前一直用的都是老项目中的上传类,现在项目中使用了okhttp ...

  4. Android必知必会-获取视频文件的截图、缩略图

    背景 公司最近要求给我负责的APP加上视频录制和发布的功能,我简单的完成了基本的录制和视频压缩功能,后来发现发布接口需要上传视频的截图,网上搜索了一下资料,在这里整理一下. 代码实现 /** * 获取 ...

  5. 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

    本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...

  6. H5系列之History(必知必会)

    H5系列之History(必知必会)   目录 概念 兼容性 属性 方法 H5方法       概念     理解History Api的使用方式 目的是为了解决哪些问题   作用:ajax获取数据时 ...

  7. 读书笔记--SQL必知必会18--视图

    读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...

  8. 《MySQL 必知必会》读书总结

    这是 <MySQL 必知必会> 的读书总结.也是自己整理的常用操作的参考手册. 使用 MySQL 连接到 MySQL shell>mysql -u root -p Enter pas ...

  9. 2015 前端[JS]工程师必知必会

    2015 前端[JS]工程师必知必会 本文摘自:http://zhuanlan.zhihu.com/FrontendMagazine/20002850 ,因为好东东西暂时没看懂,所以暂时保留下来,供以 ...

  10. [ 学习路线 ] 2015 前端(JS)工程师必知必会 (2)

    http://segmentfault.com/a/1190000002678515?utm_source=Weibo&utm_medium=shareLink&utm_campaig ...

随机推荐

  1. 自定义jsp标签

    1.类: package ===============================; import javax.servlet.jsp.JspTagException; import javax ...

  2. GDB调试汇编栈堆过程的学习

    前期调试 我的程序代码是: 首先,用gcc g gdb.c -o gdb -m32产生32位汇编. 输入gdb gdb进入gdb调试器 在main函数处设置一个断点:b main 用disassemb ...

  3. 单向和双向tvs管

    tvs管器件按极性可分为单极性和双极性两种,即单向tvs管和双向tvs管.    单向tvs管保护器件仅能对正脉冲或者负脉冲进行防护,而双向tvs管保护器件一端接要保护的线路,一端接地,无论来自反向还 ...

  4. (四)CodeMirror - API

    内容相关 cm.getValue() cm.setValue() cm.getRange() editor.getRange({line:1},{line:2}) // 获取内容块字符 cm.repl ...

  5. LeetCode OJ 109. Convert Sorted List to Binary Search Tree

    Given a singly linked list where elements are sorted in ascending order, convert it to a height bala ...

  6. SpriteBuilder给节点添加effect在32设备上发生crash

    环境为 Xcode 6.4 , cocos2D 3.0.4 , SpriteBuilder 1.4.9 在给某一节点添加Effect后,运行在真机iphone4s上发生崩溃,显示为: 可以看到整个堆栈 ...

  7. 插入排序-C#实现

    插入排序包括:直接插入排序和希尔排序. 具体代码如下: 直接插入排序: /// <summary> /// 直接插入排序 /// 适用于少量元素的排序 /// 稳定性:稳定 /// 时间复 ...

  8. TCP和UDP的优缺点

    TCP: 优点: 全双工的可靠连接,使得发送的数据有序.不重复.无差错.不丢失,提供的是可靠的服务: 提供确认重传机制.流量控制和拥塞控制,保证网络的稳定可靠性: 缺点: 每次通信都要建立连接,占用系 ...

  9. python学习笔记(5)-time库的使用

    import time 一.时间获取函数 time(), ctime(),gmtime() >>> import time >>> time.time() 1524 ...

  10. 比较不错的几款开源的WPF Charts报表控件

    UWP Community Toolkit 1. ModernUIChart Code: http://modernuicharts.codeplex.com/ Available Charts Co ...