行业知识
Android Permission权限总结
1.权限Permission
权限是安卓的一种安全机制,主要用来保护用户,应用的数据安全。
1.1、版本差异
Android权限机制有一个分水岭,就是Android 6.0 API 23,对于危险权限的申请有所不同。Android开发头疼的一点就是适配兼容,说不定以后所有的权限都要动态申请!
①targetSdkVersion < 23 & API < 6.0 :安装后就有权限,无需授权,无法取消授权
②targetSdkVersion < 23 & API ≥ 6.0 :安装获得权限,但是可以被取消授权。
③targetSdkVersion ≥ 23 & API < 6.0 :安装后就有权限,无需授权,无法取消授权
④targetSdkVersion ≥ 23 & API ≥ 6.0 :这种是趋势。需要动态申请,也可取消授权
1.2、框架预设权限
Android框架预先定义了很多权限,它们在框架的Manifest中。开发者不需要都记住,只需要知道常用的,比如我们熟知了日历读写、联系人读写、网络访问权限等等。手机厂商定制的ROM系统还会有很多新增的权限。
1.3、adb命令查看权限
①通过adb命令可以查看手机中的所有权限:
| 1 | adb shell pm list permissions |
2、权限级别
Android系统将权限分为三个保护级别:普通、危险和签名权限。
2.1、普通权限
普通权限就是在AndroidManifest.xml中注册即可,不需要动态申请。在应用安装后就会授予,用户无法撤销应用的这些权限。(禁止应用访问网络,可不是禁止了应用的网络权限)
| 1 2 3 |
... |
2.2、危险权限
危险权限也得在AndroidManifest.xml中注册,不过用到的时候需要动态申请:运行时检测有没有该权限,没有就提示用户授权。
| 1 2 |
|
adb命令 -d 查看手机中的所有危险权限:
| 1 | adb shell pm list permissions -d -g |
2.3、签名权限
在尝试使用某权限的应用签名证书,和定义该权限的应用证书一致时才会授予。一般自定义权限会采用这种签名的机制,防止其它三方应用也申请自定义的权限。比如腾讯系、阿里系的应用有相同的证书,保证内部自定义的权限不会被“外族”使用。
3、权限分组
Android系统将权限根据功能特性进行分组。adb命令 -g 查看手机中的命令分组情况:
| 1 | adb shell pm list permissions -g |
3.1、分组说明
①一组权限要么一个都没有授权,要么只要授权其中一个全部权限自动获取(以后可能会有变化,埋个坑)。
②前面提到的危险权限都有自己的分组,也可以将普通权限放到对应分组里。
③不要假定系统可以自动授予同组权限。以短信权限为例,短信权限组中的权限都和短信相关:
| SMS (短信) | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
以前开发的时候需要短信的权限,图省事,我们只申请READ_CONTACTS权限,同组的其它权限也会自动获取,这时不仅拥有读取联系人的权限,写联系人的权限也有了。但是Google官网给出了警告,这样是不可靠的,因为将来的Android系统可能会修改权限组规则:比如WRITE_CONTACTS如果移出了短信组,那么光申请READ_CONTACTS权限还以为自动拥有了WRITE_CONTACTS权限就会出错。
4、权限检测
对于targetSdkVersion ≥ 23而言,应用的某些操作需要的权限是动态申请的,在操作前一定要检测是否有该权限。
4.1、检测方法
通过ContextCompat的checkSelfPermission(Context context,String permission)方法检测传入的permission权限:
| 1 | ContextCompat.checkSelfPermission(context, permission); |
或者如下调用形式,ActivityCompat继承自ContextCompat。
| 1 | ActivityCompat.checkSelfPermission(context, permission); |
或者通过AndroidX库中的PermissionChecker检测
| 1 | PermissionChecker.checkSelfPermission(context, permission); |
最终还是通过Context去检测权限,权限检测流程源码分析详见Android权限检测申请源码流程分析。
| 1 2 3 4 5 6 | public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) { if (permission == null) { throw new IllegalArgumentException("permission is null"); } return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid()); } |
4.2、返回值
检测结果以 int 类型返回,PackageManager.PERMISSION_GRANTED有该权限,PackageManager.PERMISSION_DENIED无该权限。
5、权限申请
前面检测完权限,如果有权限就继续操作。若无相应的权限就需要动态申请。
5.1、shouldShowRequestPermissionRationale()
在阅读官方指南的时候,发现官方推荐在动态申请权限的时候调用这个方法,是否需要在动态申请权限之前给用户一个友好的提示:告诉用户为什么我的应用需要申请这个权限,而不是不管三七二十一就向用户申请权限。对于一个厨房APP还需要手机联系人读写权限的就很不理解。
| 1 2 3 4 5 6 | /** * Gets whether you should show UI with rationale before requesting a permission. */ public boolean shouldShowRequestPermissionRationale(@NonNull String permission) { return getPackageManager().shouldShowRequestPermissionRationale(permission); } |
5.2、动态申请
如果检测出来没有权限,使用requestPermissions(String[] permissions, int requestCode)动态申请权限。用法也很简单。
在Activity、Fragment中都有请求权限的方法,在Activity和Fragment中都可以调用requestPermissions(String[] permissions, int requestCode)方法,虽然请求权限的方法名字相同,但是实现不同。
5.3、权限申请回调
用户响应系统权限对话框后,系统就会调用应用的onRequestPermissionsResult()方法。系统会传入用户对权限对话框的响应以及开发者自定义的请求代码:
| 1 2 3 4 | @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } |
6、自定义权限
应用可以自定义自己的权限。
6.1、使用自定义权限
举个例子:友商的应用要跳转我们的Activity,Activity必须要设置exported为true。但是这样又不安全,这意味着其它任意应用都可以启动暴露的Activity。这时可以在AndroidManifest.xml中给Activity加上权限:
| 1 2 3 4 5 |
...
|
将我们自定义的权限给三方,三方应用只需要在自己的AndroidManifest中添加声明即可(遇到特别坑的情况,三方自行修改了权限也不告诉一声那就没办法,所以启动Activity的时候都必须异常捕获)。
| 1 2 3 4 5 6 |
... |
7、工具方法
项目上往往将重复的方法抽取出来,成为工具方法。这里的权限检测有很多地方会用到,所以一般也会抽取。简单的示例如下,后面我会单独介绍一个更方便的权限检测库Permission-dispatch。
7.1、检查单个权限
返回true表示已授权,返回false表示未获得权限。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // 检查某个权限。返回true表示已启用该权限,返回false表示未启用该权限 public static boolean checkPermission(Activity act, String permission, int requestCode) { Log.d(TAG, "checkPermission: " + permission); boolean result = true; // 只对Android6.0及以上系统进行校验 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 检查当前App是否开启了名称为permission的权限 int check = ContextCompat.checkSelfPermission(act, permission); if (check != PackageManager.PERMISSION_GRANTED) { // 未开启该权限,则请求系统弹窗,好让用户选择是否立即开启权限 ActivityCompat.requestPermissions(act, new String[]{permission}, requestCode); result = false; } } return result; } |
7.2、检查多个权限
一次性检查多个权限,有一个没用授权就不通过。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 检查多个权限。返回true表示已完全启用权限,返回false表示未完全启用权限 public static boolean checkMultiPermission(Activity act, String[] permissions, int requestCode) { boolean result = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { int check = PackageManager.PERMISSION_GRANTED; // 通过权限数组检查是否都开启了这些权限 for (String permission : permissions) { check = ContextCompat.checkSelfPermission(act, permission); if (check != PackageManager.PERMISSION_GRANTED) { break; } } if (check != PackageManager.PERMISSION_GRANTED) { // 未开启该权限,则请求系统弹窗,好让用户选择是否立即开启权限 ActivityCompat.requestPermissions(act, permissions, requestCode); result = false; } } return result; } |
7.3、检查是否允许修改系统设置
检查是否允许修改系统设置。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 检查是否允许修改系统设置 public static boolean checkWriteSettings(Activity act, int requestCode) { Log.d(TAG, "checkWriteSettings:"); boolean result = true; // 只对Android6.0及Android7.0系统进行校验 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT |
上一篇:软件接不上服务器怎么解决?
下一篇:瑞科关闭服务器怎么关闭?