Android 6.0運行時請求權限
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動態權限處理一般有三個步驟:
- 檢查權限;
- 請求權限;
- 處理請求相應回調。
雖然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 運行時權限 學習筆記
來自:http://www.sunnyang.com/598.html