Android 運行時權限處理
引言
Android 6.0 (API 23) 開始引入了運行時權限檢查 (Permissions at Run Time),用戶不需要在安裝時同意授予應用權限,而是在應用運行時動態去申請所需要的權限,由用戶決定是否授予權限,這樣可以讓用戶更靈活的控制授予應用的權限,而不是必需在應用安裝時就授予或者不授予應用請求的所有權限。例如一個應用同時申請了使用相機和定位服務的權限,用戶可以只授該應用訪問相機的權限,而不授予定位的權限。
權限的分類
不是所有權限都必須動態申請的,系統權限被分為兩類:普通權限 (Normal Permissions),敏感權限(Dangerous permissions)。
- 普通權限:不涉及到用戶敏感信息,應用只需要在
AndroidManifest聲明,用戶同意安裝應用,系統就會自動授予相應的權限,和 API 23 以前版本保持一致。 - 敏感權限:能夠訪問用戶的敏感信息,如使用相機、聯系人等權限。申請敏感權限時不僅需要在
AndroidManifest聲明,還需要在運行時申請,用戶同意后才能獲取到該權限。
不同 Android 版本對權限的處理
對于普通權限,Android 6.0 的處理方式是一樣,即只要在 AndroidManifest 聲明,用戶安裝了應用就會獲取到權限。但對于敏感權限的處理就有所不同了,由于運行時權限是在 Android 6.0 才開始引入的,所以那些運行在老版本上的應用是沒有對運行時權限做處理的,為了兼容舊版本的應用,Android 6.0 必須做不同處理。
- 在 Android 5.1 或者版本更低的設備上,Android 對普通和敏感權限的處理都是一樣的,必須在
AndroidManifest聲明,用戶也必須在安裝時就授予相應的權限。 - 在 Android 6.0 或者更高版本的設備上,要看應用的
target SDK版本,如果應用的target SDK版本是 23 或者更高,那么應用就必須對運行時權限做處理,如果要申請敏感權限在AndroidManifest中聲明的同時,還需要在運行時動態請求相應的敏感權限,否則將無法獲取敏感權限;如果應用的target SDK版本是 22 或者更低,并且在AndroidManifest中聲明了敏感權限,那么用戶必須在安裝是就授予權限,運行時 Android 會默認應用擁有敏感權限,但這并不是說 target SDK 22 以下的應用就不用處理運行時敏感權限了,用戶還是隨時可以在應用的權限管理界面關掉相應的權限的,如果權限被用戶手動關掉,那么運行時應用是沒有權限的,所以應用在運行時還是要判斷是否獲得了所需要的權限。
如何檢查應用是否有所需的敏感權限
如果應用需要敏感權限,那么在應用每次運行的時候都需要檢查是否擁有權限,因為用戶可以隨時在權限管理界面關掉權限,運行以下代碼檢查應用是否具有權限:
int permission = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果 permission 等于 PackageManager.PERMISSION_GRANTED 則代表應用已經擁有權限了,反之如果 permission 等于 PackageManager.PERMISSION_DENIED 則代表應用沒有獲得權限,需要再次申請。
如何在運行時申請敏感權限
這里以讀取聯系人權限為例:
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// 是否要顯示問什么要獲取權限的解釋界面
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,Manifest.permission.READ_CONTACTS)) {
// 顯示解釋權限用途的界面,然后再繼續請求權限
} else {
// 沒有權限,直接請求權限
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
}
}</code></pre>
代碼執行時會彈出系統默認的請求權限對話框

請求權限對話框.png
上面一段有幾處需要注意的:
shouldShowRequestPermissionRationale 函數是用來判斷是否需要顯示解釋為什么需要該權限的界面,該函數在應用第一次安裝的時候會返回 false,因此你可以直接請求任何需要的權限。如果用戶以前拒絕了一個請求,這個方法將返回 true,那樣的話你應該考慮在再次觸發權限對話框之前顯示一個解釋請求用途之類的信息。當應用完全沒有機會被授權的時候,該函數也會返回 false,比如用戶在權限對話框中選擇了"不再顯示”,結果為 false 說明用戶明確不想授權,再彈解釋界面也是沒用。
ActivityCompat.requestPermissions 這里我們調用的是 ActivityCompat 的 requestPermissions,用戶選擇完成后會回調該 Activity 的 onRequestPermissionsResult,但如果在 Fragment 處理請求最好調用 FragmentCompat. requestPermissions (需要 android.support.v13),這樣處理結果會回調到 Fragment 的 onRequestPermissionsResult。
MY_PERMISSIONS_REQUEST_READ_CONTACTS 是一個 int 型值,是權限請求的標志,會在 onRequestPermissionsResult 中返回,用來標志該回調是對應哪個請求的。
處理權限請求結果
如上面所說的,用戶選擇之后的結果會回調到 onRequestPermissionsResult 函數,根據 requestCode 和 grantResults 判斷結果:
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 權限申請通過!
} else {
// 悲劇了,用戶不給權限
}
return;
}
}
}
來自:http://www.jianshu.com/p/9604d48178ce