前言:
在Android系统不断的升级过程中,Android应用的开发也有不同的变化,对于权限,Android6.0以上的系统中,引入了运行时权限检查,运行时权限分为正常权限和危险权限,当我们的App调用了需要危险权限的api时,需要向系统申请权限,系统会弹出一个对话框让用户感知,只有当用户授权以后,App才能正常调用api。
目前所有的权限类别可参考https://developer.android.google.cn/reference/android/Manifest.permission?hl=zh-cn
如果您确定您的应用必须访问受限数据或执行受限操作才能实现某个用例,请声明相应的权限。有些权限是用户安装应用时自动授予的权限,称为安装时权限。其他权限则需要应用在运行时进一步请求权限,此类权限称为运行时权限。
图 1. 在 Android 中使用权限的概要工作流示意图。
权限分类
- 安装时权限
图 2. 某应用商店中显示的某个应用的安装时权限列表。
图3显示了一个弹出式对话框,其中包含 2 个选项:允许和拒绝。
安装时权限授予应用对受限数据的受限访问权限,并允许应用执行对系统或其他应用只有最低影响的受限操作。如果您在应用中声明了安装时权限,系统会在用户安装您的应用时自动授予应用相应权限。应用商店会在用户查看应用详情页面时向其显示安装时权限通知,如图 2 所示。
Android 提供多个安装时权限子类型,包括普通权限和签名权限。
a. 普通权限
此类权限允许访问超出应用沙盒的数据和执行超出应用沙盒的操作。但是,这些数据和操作对用户隐私及对其他应用的操作带来的风险非常小。
系统会为普通权限分配“normal”保护级别,如权限 API 参考文档页面中所示。
b. 签名权限
当应用声明了其他应用已定义的签名权限时,如果两个应用使用同一证书进行签名,系统会在安装时向前者授予该权限。否则,系统无法向前者授予该权限。
- 运行时权限
一个弹出式对话框,其中包含 2 个选项:允许和拒绝。
运行时权限也称为危险权限,此类权限授予应用对受限数据的额外访问权限,并允许应用执行对系统和其他应用具有更严重影响的受限操作。因此,您需要先在应用中请求运行时权限,然后才能访问受限数据或执行受限操作。当应用请求运行时权限时,系统会显示运行时权限提示,如图 3 所示。
图 3. 当应用请求运行时权限时显示的系统权限提示。
许多运行时权限会访问用户私有数据,这是一种特殊的受限数据,其中包含可能比较敏感的信息。例如,位置信息和联系信息就属于用户私有数据。
系统会为运行时权限分配“dangerous”保护级别,如权限 API 参考文档页面中所示。
本篇即主要说明的是运行时权限的申请。
- 特殊权限
特殊权限与特定的应用操作相对应。只有平台和原始设备制造商 (OEM) 可以定义特殊权限。此外,如果平台和 OEM 想要防止有人执行功能特别强大的操作(例如通过其他应用绘图),通常会定义特殊权限。
系统设置中的特殊应用访问权限页面包含一组用户可切换的操作。其中的许多操作都以特殊权限的形式实现。
每项特殊权限都有自己的实现细节。如需查看使用每项特殊权限的说明,请访问权限 API 参考文档页面。系统会为特殊权限分配“appop”保护级别。
申请权限
基本原则
在运行时请求权限的基本原则如下:
- 当用户开始与需要相关权限的功能互动时,在具体使用情境下请求权限。 不要阻止用户使用应用。始终提供选项供用户取消与权限相关的指导界面流程。
- 如果用户拒绝或撤消某项功能所需的权限,请适当降级您的应用以便让用户可以继续使用您的应用(可能通过停用需要权限的功能来实现)。
- 不要对系统行为做任何假设。例如,假设某些权限会出现在同一个权限组中。权限组的作用只是在应用请求密切相关的多个权限时,帮助系统尽可能减少向用户显示的系统对话框数量。
请求权限
应用权限基于系统安全功能,最佳做法是将运行时权限与特定操作相关联,尽可能往后推迟到在应用的用例流程中请求权限。例如,如果应用允许用户向他人发送语音消息,请等到用户已导航到消息屏幕并已按下发送语音消息按钮后再请求权限。待用户按下该按钮后,应用再请求麦克风使用权限。
注册权限
如需声明应用可能请求的权限,请在应用的清单文件中添加相应的 元素。例如,需要访问相机的应用应在清单中添加以下代码行:
<manifest ...>
<uses-permission android:name="android.permission.CAMERA"/>
<application ...>
...
</application>
</manifest>
申请流程
-
在应用的清单文件中,声明应用可能需要请求的权限。
-
设计应用的用户体验,使应用中的特定操作与特定运行时权限相关联。应当让用户知道哪些操作可能会要求他们向您的应用授予访问其私人数据的权限。
-
等待用户调用应用中需要访问特定用户私人数据的任务或操作。届时,您的应用可以请求访问相应数据所需的运行时权限。
-
检查用户是否已授予应用所需的运行时权限。如果已授权,那么您的应用可以访问用户私人数据。如果没有,请继续执行下一步。 每次执行需要该权限的操作时,您都必须检查自己是否具有该权限。
-
检查您的应用是否应向用户显示理由,说明您的应用需要用户授予特定运行时权限的原因。如果系统确定您的应用不应显示理由,请继续直接执行下一步,无需显示界面元素。
不过,如果系统确定您的应用应该显示一个理由,请在界面元素中向用户显示理由,明确说明您的应用试图访问哪些数据,以及应用获得运行时权限后可为用户提供哪些好处。用户确认理由后,请继续执行下一步。 -
请求您的应用访问用户私人数据所需的运行时权限。系统会显示运行时权限提示,例如权限概览页面上显示的提示。
-
检查用户的响应,他们可能会选择同意或拒绝授予运行时权限。
-
如果用户向您的应用授予权限,您就可以访问用户私人数据。如果用户拒绝授予该权限,请适当降低应用体验,使应用在未获得受该权限保护的信息时也能向用户提供功能。
图 4 说明了与此过程相关的工作流和决策组
原生申请方法
Android系统为我们Android开发提供了基础的权限申请方法,包括
- 确定应用是否已获得权限
使用系统方法 ContextCompat.checkSelfPermission() 并传入摇检查的权限。根据您的应用是否具有相应权限,此方法会返回 PERMISSION_GRANTED 或 PERMISSION_DENIED。
- 说明为何需要获取权限
如果 ContextCompat.checkSelfPermission() 方法返回 PERMISSION_DENIED,可调用shouldShowRequestPermissionRationale()。此方法在activity和fragment中直接调用。如果此方法返回 true,请向用户显示指导界面,在此界面中说明用户希望启用的功能为何需要特定权限。
- 请求权限
用户查看指导界面后或者 shouldShowRequestPermissionRationale() 的返回值表明您这次不需要显示指导界面后,您可以请求权限。用户会看到系统权限对话框,并可在其中选择是否向您的应用授予特定权限。
ActivityCompat.requestPermissions(activity, permissions, requestCode);
按照历来的做法,您可以在权限请求过程中自行管理请求代码,并将此请求代码包含在您的权限回调逻辑中。
开源权限框架
由于Android动态权限申请复杂,代码较多,所以出现了很多开源框架来帮助我们开发,我们可以学习开源框架的使用。
- EasyPermission
为什么首先提这个库呢,其github地址为:https://github.com/googlesamples/easypermissions。
官方的,最为致命。其代码示例demo如下所示。其实还是很多代码的,但是由于官方考虑的比较多,流程上肯定是没问题的。而且充分考虑了android support 和androidx的兼容,但可定制性没那么高了。由于原生方法较多,依赖onActivityResult和onRequestPermissionsResult,导致代码也多。
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks,EasyPermissions.RationaleCallbacks{
private static final int RC_CAMERA_PERM = 123;
private static final int RC_LOCATION_CONTACTS_PERM = 124;
@AfterPermissionGranted(RC_CAMERA_PERM)
public void cameraTask() {
EasyPermissions