本文為菜鳥窩作者蔣志碧的連載。“從 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