本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
一、前言
Android 6.0 版本(Api 23) 及其以上系统引入运行时权限 — 默认所有涉及用户隐私的权限都被关闭,我们在 AndroidManifest.xml 中申请权限之后,依然还需要动态申请权限,不然每次安装完APP后,就需要在「设置 -> 应用 -> APP」中打开所需权限。
Android 系统包含默认的授权提示框,但是仍需要我们设置自己的页面,原因的系统提供的授权框会有不再提示选项。如果用户选择,则无法触发授权提示,使用自定义的提示页面,可以给予用户手动修改授权的指导。
统一授权
如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在用户安装应用时要求用户授予权限。如果将新权限添加到更新的应用版本,系统会在用户更新应用时要求授予该权限。用户一旦安装应用,他们撤销权限的唯一方式是卸载应用。
运行时权限
如果设备运行的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则应用在运行时向用户请求权限。用户可随时调用权限,因此应用在每次运行时均需检查自身是否具备所需的权限。
二、权限分组
Android 6.0 引入运行时权限,所有权限仍然需要在 AndroidManifest 中声明,但是当访问需要的权限时,活动或片段必须验证权限已被授予或使用支持库的调用请求缺少的权限。
权限分为三类:
正常(Normal Protection)权限
危险(Dangerous)权限
特殊(Particular)权限
其它权限(一般很少用到)
正常权限
「正常权限」涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。
正常权限具有如下特点
1、对用户隐私没用较大影响或者不会带来安全问题
2、安装之后就被赋予这些权限,不需要显示提醒用户,用户也不能取消这些权限
危险权限
危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。
权限组
所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:
如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。
任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。
如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。
特殊权限
有许多权限其行为方式与正常权限及危险权限都不同。SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 特别敏感,因此大多数应用不应该使用它们。如果某应用需要其中一种权限,必须在清单中声明该权限,并且发送请求用户授权的 intent。系统将向用户显示详细管理屏幕,以响应该 intent。
SYSTEM_ALERT_WINDOW 设置悬浮窗
WRITE_SETTINGS 修改系统设置
SYSTEM_ALERT_WINDOW
允许应用使用类型 TYPE_APPLICATION_OVERLAY 创建窗口,该窗口显示在所有其他应用程序的顶端。极少的应用程序应该使用这个权限,这些窗口用于与用户的系统级交互。
Note:如果是 API 23 及其以上版本,用户必须通过权限管理向应用程显示权限。 应用程序通过发送 ACTION_MANAGE_OVERLAY_PERMISSION action 的隐式 intent 请求用户的批准。 该应用程序可以通过调用 Settings.canDrawOverlays() 来检查是否具有此授权。
请求 SYSTEM_ALERT_WINDOW 权限
private static final int REQUEST_CODE_SYSTEM_ALERT_WINDOW = 1;
private void requestAlertWindowPermission() {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_SYSTEM_ALERT_WINDOW);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_SYSTEM_ALERT_WINDOW) {
if (Settings.canDrawOverlays(this)) {
Log.i(TAG, "ACTION_MANAGE_OVERLAY_PERMISSION granted");
}
}
}
注意:
使用 Action 的 Settings.ACTION_MANAGE_OVERLAY_PERMISSION 启动隐式 Intent。
使用 "package:" + getPackageName() 携带 APP 包名信息
使用 Settings.canDrawOverlays(this) 判断授权结果
WRITE_SETTINGS
允许应用程序读取或写入系统设置。
Note:如果是 API 23 及其以上版本,则用户必须通过权限管理向应用程序显式授予该权限。 应用程序通过发送 ACTION_MANAGE_WRITE_SETTINGS 的 action 的隐式 Intent 来请求用户的批准。 该应用程序可以通过调用 Settings.System.canWrite() 来检查是否具有此授权。
请求 WRITE_SETTINGS 权限
private static final int REQUEST_CODE_WRITE_SETTINGS = 2;
private void requestWriteSettings() {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
if (Settings.System.canWrite(this)) {
Log.i(LOGTAG, "WRITE_SETTINGS granted" );
}
}
}
注意:
使用 Action 的 Settings.ACTION_MANAGE_WRITE_SETTINGS 启动隐式 Intent。
使用 "package:" + getPackageName() 携带 APP 包名信息
使用 Settings.canDrawOverlays(this) 判断授权结果
关于这两个特殊权限,一般不建议应用申请。
三、申请权限的主要方法
Android 6.0 运行时权限申请位于 package android.support.v4.app 包中。主要方法有三个:
**1、权限通过 ActivityCompat 类的 checkSelfPermission() 方法判断是否有所需权限。
**
**2、权限请求是通过 ActivityCompat 类中的 requestPermissions() 方法,在OnRequestPermissionsResultCallback # onRequestPermissionsResult() 方法中回调。
**
3、应用程序可以提供一个额外的合理的使用权限调用 Activitycompat # shouldShowRequestPermissionRationale() 方法。Android 原生系统中,如果第二次弹出权限申请的对话框,会出现「以后不再弹出」的提示框,如果用户勾选了,你再申请权限,则 shouldShowRequestPermissionRationale() 返回 true,意思是说要给用户一个解释,告诉用户为什么要这个权限。
四、运行时权限示例
以照相机权限为例。
1、在 AndroidManifest.xml 文件中申请所需要的权限。
<uses-permission android:name="android.permission.CAMERA"/>
2、在 gradle 中修改 targetSdkVersion 大于或等于 23。
3、开始申请权限。
权限检查
PublishPresenter.java # checkPublishPermission()
@Override
public boolean checkPublishPermission(Activity activity) {
//1、ActivityCompat.checkSelfPermission() 判断权限
if (Build.VERSION.SDK_INT >= 23) {
List<String> permissions = new ArrayList<>();
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)) {
permissions.add(Manifest.permission.CAMERA);
}
//2、ActivityCompat.requestPermissions() 申请权限
if (permissions.size() != 0) {
ActivityCompat.requestPermissions(activity
, permissions.toArray(new String[0]),
Constants.WRITE_PERMISSION_REQ_CODE);
return false;
}
}
return true;
}
方法回调
PublishActivity.java # onRequestPermissionsResult()
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case Constants.WRITE_PERMISSION_REQ_CODE:
for (int ret:grantResults){
if (ret != PackageManager.PERMISSION_GRANTED){
return;
}
}
mPermission = true;
break;
default:
break;
}
}
五、运行效果
当进入该界面,系统就会判断是否具有照相机权限,没有则会弹出对话框提示用户添加权限。
详情请转至 GitHub
参考:
http://www.what21.com/article/a_3_1483187432030.html
http://droidyue.com/blog/2016/01/17/understanding-marshmallow-runtime-permission/
https://developer.android.com/guide/topics/security/permissions.html?hl=zh-cn#normal-dangerous
撸这个项目的一半,你就是大神 , 戳http://mp.weixin.qq.com/s/ZagocTlDfxZpC2IjUSFhHg