Android 6.0運行時請求權限

aikf0112 8年前發布 | 15K 次閱讀 安卓開發 Android開發 移動開發

Android權限簡介

從Android 6.0開始,部分 危險權限 需要在運行時用戶動態授權,因為一個Android應用默認情況下是不擁有任何權限的。在開發的時候,我們會在AndroidManifest.xml中靜態地聲明相應的權限,如果沒有聲明該權限卻使用了相應的權限,程序會崩潰,拋出異常,例如,如果沒有在程序中聲明網絡權限當我們使用網絡的時候,就會拋出如下異常,而且一般不會try catch該異常:

Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)

但是在Android6.0上面部分權限不但要求開發者在使用之前在AndroidManifest.xml聲明,還需要用戶在使用的時候進行授權才可以使用,例如用戶想使用拍照上傳圖片,雖然已經聲明了使用相機的權限,如果用戶拒絕使用相機權限,還是會拋出異常:

Caused by: java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE cmp=com.android.camera/.Camera }…

這時候如果程序員沒有對Android6.0手機進行額外權限的處理,程序就會直接崩掉了,給用戶帶來很不好的體驗。

剛剛看到一篇新聞,《Android 7.1.1將于12月5日正式登陸Nexus設備》,Android7.x的手機都要問世了,針對Android M以上的手機動態權限確實需要開發者認真研究一下了。

正常權限和危險權限

事實上Android系統對權限聲明有四個等級,主要通過protectionLevel來設置。

<permissionandroid:description="string resource"
 android:icon="drawable resource"
 android:label="string resource"
 android:name="string"
 android:permissionGroup="string"
 android:protectionLevel=["normal" | "dangerous" |
 "signature" | "signatureOrSystem"]/>

簽名相關的兩個權限并不常用,在開發中我們真正需要針對Android6.0以上系統進行特殊處理的都是危險權限。

系統權限分為兩類: 正常權限 和 危險權限

  • 正常權限不會直接給用戶隱私權帶來風險。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。
  • 危險權限會授予應用訪問用戶機密數據的權限。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。如果您列出了危險權限,則用戶必須明確批準您的應用使用這些權限。

之所以在這里列出來 權限 和 危險權限 ,主要就是因為Android6.0在這里做了一個分割線:

  • 如果設備運行的是 Android 5.1 或更低版本, 或者 應用的目標 SDK 為 22 或更低:如果您在清單中列出了危險權限,則用戶必須在安裝應用時授予此權限;如果他們不授予此權限,系統根本不會安裝應用。
  • 如果設備運行的是 Android 6.0 或更高版本, 或者 應用的目標 SDK 為 23 或更高:應用必須在清單中列出權限, 并且 它必須在運行時請求其需要的每項危險權限。用戶可以授予或拒絕每項權限,且即使用戶拒絕權限請求,應用仍可以繼續運行有限的功能。

權限組

所有危險的 Android 系統權限都屬于權限組。如果設備運行的是 Android 6.0(API 級別 23),并且應用的 targetSdkVersion 是 23 或更高版本,則當用戶請求危險權限時系統會發生以下行為:

  • 如果應用請求其清單中列出的危險權限,而應用目前在權限組中沒有任何權限,則系統會向用戶顯示一個對話框,描述應用要訪問的權限組。對話框不描述該組內的具體權限。例如,如果應用請求 READ_CONTACTS 權限,系統對話框只說明該應用需要訪問設備的聯系信息。如果用戶批準,系統將向應用授予其請求的權限。
  • 如果應用請求其清單中列出的危險權限,而應用在同一權限組中已有另一項危險權限,則系統會立即授予該權限,而無需與用戶進行任何交互。例如,如果某應用已經請求并且被授予了 READ_CONTACTS 權限,然后它又請求 WRITE_CONTACTS ,系統將立即授予該權限。

任何權限都可屬于一個權限組,包括正常權限和應用定義的權限。但權限組僅當權限危險時才影響用戶體驗。可以忽略正常權限的權限組。如果設備運行的是 Android 5.1(API 級別 22)或更低版本,并且應用的 targetSdkVersion 是 22 或更低版本,則系統會在安裝時要求用戶授予權限。再次強調,系統只告訴用戶應用需要的權限組,而不告知具體權限。

權限組 權限
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE

動態權限處理邏輯

Android6.0動態權限處理一般有三個步驟:

  1. 檢查權限;
  2. 請求權限;
  3. 處理請求相應回調。

雖然Android6.0已經新增了針對動態權限的相關實現方法,但是為了使用方便我們使用support支持看,這樣免去了判斷Android版本的繁瑣,針對Activity和Fragment支持庫都提供了相應的方法。

int ContextCompat.checkSelfPermission(Contextcontext, String permission)
void ActivityCompat.requestPermissions(Activityactivity, String[] permissions, int requestCode)
boolean ActivityCompat.shouldShowRequestPermissionRationale(Activityactivity, String permission)
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
//fragment
void requestPermissions(String[] permissions, int requestCode)

檢查權限

當我們開發的應用在操作是需要一個危險的權限,在執行操作時應該檢查一下是否具有該權限。檢查是否具有某項權限,可以調用 ContextCompat.checkSelfPermission() 方法。如果應用具有該權限,方法將返回 PackageManager.PERMISSION_GRANTED,并且應用可以繼續操作。如果應用不具有此權限,方法將返回 PERMISSION_DENIED,且應用必須明確向用戶要求權限。

下面是一個調用相機的權限:

int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (permission == PackageManager.PERMISSION_GRANTED) {
 //執行拍照
} else {//請求權限
 ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA }, PHOTO);
}

請求權限

在第一步中我們檢查權限,如果沒有相應的權限即去請求權限,請求權限時,我們可以同時請求多個權限,也可以單個權限執行請求。調用請求權限時系統會彈出來一個標準的Android對話框,開發者不能進行自定義。

當我們同時請求多個權限時,會在系統單個對話框中順序顯示多個權限授權操作,下面代碼是一次請求多個權限:

String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE };
ActivityCompat.requestPermissions(this, permissions, PHOTO);

處理請求相應回調

當我們響應系統請求權限對話框時,系統將調用應用的 onRequestPermissionsResult() 方法,在回調中繼續處理我們的邏輯操作,如果用戶拒絕了我們要求的權限,我們可以在這里進行必要的解釋,告訴用戶為什么需要此權限。當然了不是每條權限都需要解釋,太多的解釋反而會降低用戶體驗。

為了幫助開發者提供需要解釋的情形,Android提供了 shouldShowRequestPermissionRationale()方法,部分手機上面該方法不起作用,主要是由于Android手機深度定制導致的。

  • 如果應用之前請求過此權限但用戶拒絕了請求,此方法將返回 true。
  • 如果用戶在過去拒絕了權限請求,并在權限請求系統對話框中選擇了 Don’t ask again 選項,此方法將返回 false。
  • 如果設備規范禁止應用具有該權限,此方法也會返回 false。

下面是一個對執行拍照請求權限后的回調:

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
 switch (requestCode) {
 case PHOTO:
 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 //拍照
 } else {
 if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
 Manifest.permission.CAMERA)) {
 //提供必要的解釋,為什么需要該權限
 }else{
 Toast.makeText(MainActivity.this, "授權失敗!", 0).show();
 }
 }
 break;
 default:
 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 break;
 }
}

權限拒絕后跳轉至應用的設置界面

在動態請求權限處理時,有一種簡單粗暴的方式,我們一下將應用中涉及到的危險權限一次性全部請求,如果用戶不同意,直接提示用戶需要相應的權限才可以使用該應用,然后跳轉到應用的設置界面,當然了由于Android手機種類多樣,下下面這種方法不一定總是可行,不過在小米、魅族、華為和三星手機上驗證了部分手機都是可行的。

IntentlocalIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", getPackageName(), null));
startActivity(localIntent);

在Android6.0之前手機設置界面基本是這樣的,所有申請的權限如下排列:

在Android6.0之后新增了權限相關的設置信息:

Fragment處理權限請求回調

在使用Fragment處理回調是不要使用ActivityCompat.requestPermissions方法,雖然使用該方法不會報錯,直接使用Fragment中的requestPermissions方法即可,否則就會將回調返回到相應的Activity。

如果Fragment嵌套了子Fragment,在子Fragment中使用requestPermissions方 法,onRequestPermissionsResult不會回調回來,建議使用 getParentFragment().requestPermissions方法,這個方法會回調到父Fragment中的onRequestPermissionsResult,加入以下代碼可以把回調透傳到子Fragment。

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {  
 super.onRequestPermissionsResult(requestCode, permissions, grantResults);  
 List<Fragment> fragments = getChildFragmentManager().getFragments();  
 if (fragments != null) {  
 for (Fragmentfragment : fragments) {  
 if (fragment != null) {  
 fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);  
 }  
 }  
 }  
}

權限的最佳做法

這里所說的最佳做法來自 https://developer.android.com/training/permissions/best-practices.html

  • 考慮使用 intent
  • 僅要求您需要的權限
  • 不要讓用戶感到無所適從
  • 測試兩種權限模式
  •  

另外可以參考google在github上的動態請求權限的示例或者使用第三方的庫來處理權限:

android-RuntimePermissions: https://github.com/googlesamples/android-RuntimePermissions

easypermissions: https://github.com/googlesamples/easypermissions

PermissionsDispatcher: https://github.com/hotchemi/PermissionsDispatcher

RxPermissions: https://github.com/tbruyelle/RxPermissions

Grant: https://github.com/anthonycr/Grant

參考資料

Android M Permission 運行時權限 學習筆記

聊一聊Android 6.0的運行時權限

權限最佳做法

系統權限

在運行時請求權限

安卓6.0新特性在Fragment申請運行時權限

android 6.0權限全面詳細分析和解決方案

Android 6.0 運行時權限處理

 

來自:http://www.sunnyang.com/598.html

 

 本文由用戶 aikf0112 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!