從 Android 6.0(API 級別 23)開始,用戶開始在應用運行時向其授予權限,而不是在應用安裝時授予。此方法能夠簡化應用安裝過程,由於用戶在安裝或更新應用時不須要授予權限。它還讓用戶能夠對應用的功能進行更多控制;例如,用戶能夠選擇爲相機應用提供相機訪問權限,而不提供設備位置的訪問權限。用戶能夠隨時進入應用的「Settings」屏幕調用權限。html
系統權限分爲兩類:正常權限和危險權限:java
如需瞭解詳細信息,請參閱正常權限和危險權限。android
在全部版本的 Android 中,您的應用都須要在其應用清單中同時聲明它須要的正常權限和危險權限,如聲明權限中所述。不過,該聲明的影響因系統版本和應用的目標 SDK 級別的不一樣而有所差別:app
注:從 Android 6.0(API 級別 23)開始,用戶能夠隨時從任意應用調用權限,即便應用面向較低的 API 級別也能夠調用。不管您的應用面向哪一個 API 級別,您都應對應用進行測試,以驗證它在缺乏須要的權限時行爲是否正常。框架
本課將介紹如何使用 Android 支持庫來檢查和請求權限。Android 框架從 Android 6.0(API 級別 23)開始提供相似方法。不過,使用支持庫更簡單,由於在調用方法前,您的應用不須要檢查它在哪一個版本的 Android 上運行。異步
若是您的應用須要危險權限,則每次執行須要這一權限的操做時您都必須檢查本身是否具備該權限。用戶始終能夠自由調用此權限,所以,即便應用昨天使用了相機,它不能假設本身今天仍具備該權限。async
要檢查您是否具備某項權限,請調用 ContextCompat.checkSelfPermission()
方法。例如,如下代碼段顯示瞭如何檢查 Activity 是否具備在日曆中進行寫入的權限:ide
// Assume thisActivity is the current activity int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);
若是應用具備此權限,方法將返回 PackageManager.PERMISSION_GRANTED
,而且應用能夠繼續操做。若是應用不具備此權限,方法將返回 PERMISSION_DENIED
,且應用必須明確向用戶要求權限。測試
若是您的應用須要應用清單中列出的危險權限,那麼,它必需要求用戶授予該權限。Android 爲您提供了多種權限請求方式。調用這些方法將顯示一個標準的 Android 對話框,不過,您不能對它們進行自定義。ui
圖 1. 提示用戶授予或拒絕權限的系統對話框。
在某些狀況下,您可能須要幫助用戶瞭解您的應用爲何須要某項權限。例如,若是用戶啓動一個攝影應用,用戶對應用要求使用相機的權限可能不會感到吃驚,但用戶可能沒法理解爲何此應用想要訪問用戶的位置或聯繫人。在請求權限以前,不妨爲用戶提供一個解釋。請記住,您不須要經過解釋來講服用戶;若是您提供太多解釋,用戶可能發現應用使人失望並將其移除。
您能夠採用的一個方法是僅在用戶已拒絕某項權限請求時提供解釋。若是用戶繼續嘗試使用須要某項權限的功能,但繼續拒絕權限請求,則可能代表用戶不理解應用爲何須要此權限才能提供相關功能。對於這種狀況,比較好的作法是顯示解釋。
爲了幫助查找用戶可能須要解釋的情形,Android 提供了一個實用程序方法,即 shouldShowRequestPermissionRationale()
。若是應用以前請求過此權限但用戶拒絕了請求,此方法將返回 true
。
注:若是用戶在過去拒絕了權限請求,並在權限請求系統對話框中選擇了 Don't ask again 選項,此方法將返回 false
。若是設備規範禁止應用具備該權限,此方法也會返回 false
。
若是應用尚無所需的權限,則應用必須調用一個 requestPermissions()
方法,以請求適當的權限。應用將傳遞其所需的權限,以及您指定用於識別此權限請求的整型請求代碼。此方法異步運行:它會當即返回,而且在用戶響應對話框以後,系統會使用結果調用應用的回調方法,將應用傳遞的相同請求代碼傳遞到 requestPermissions()
。
如下代碼能夠檢查應用是否具有讀取用戶聯繫人的權限,並根據須要請求該權限:
// Here, thisActivity is the current activity if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) { // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant. The callback method gets the // result of the request. } }
注:當您的應用調用 requestPermissions()
時,系統將向用戶顯示一個標準對話框。您的應用沒法配置或更改此對話框。若是您須要爲用戶提供任何信息或解釋,您應在調用 requestPermissions()
以前進行,如解釋應用爲何須要權限中所述。
當應用請求權限時,系統將向用戶顯示一個對話框。當用戶響應時,系統將調用應用的 onRequestPermissionsResult()
方法,向其傳遞用戶響應。您的應用必須替換該方法,以瞭解是否已得到相應權限。回調會將您傳遞的相同請求代碼傳遞給 requestPermissions()
。例如,若是應用請求 READ_CONTACTS
訪問權限,則它可能採用如下回調方法:
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // contacts-related task you need to do. } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return; } // other 'case' lines to check for other // permissions this app might request } }
系統顯示的對話框說明了您的應用須要訪問的權限組;它不會列出具體權限。例如,若是您請求 READ_CONTACTS
權限,系統對話框只顯示您的應用須要訪問設備的聯繫人。用戶只須要爲每一個權限組授予一次權限。若是您的應用請求該組中的任何其餘權限(已在您的應用清單中列出),系統將自動授予應用這些權限。當您請求此權限時,系統會調用您的 onRequestPermissionsResult()
回調方法,並傳遞 PERMISSION_GRANTED
,若是用戶已經過系統對話框明確贊成您的權限請求,系統將採用相同方式操做。
注:您的應用仍須要明確請求其須要的每項權限,即便用戶已嚮應用授予該權限組中的其餘權限。此外,權限分組在未來的 Android 版本中可能會發生變化。您的代碼不該依賴特定權限屬於或不屬於相同組這種假設。
例如,假設您在應用清單中列出了 READ_CONTACTS
和 WRITE_CONTACTS
。若是您請求 READ_CONTACTS
且用戶授予了此權限,那麼,當您請求 WRITE_CONTACTS
時,系統將當即授予您該權限,不會與用戶交互。
若是用戶拒絕了某項權限請求,您的應用應採起適當的操做。例如,您的應用可能顯示一個對話框,解釋它爲何沒法執行用戶已經請求但須要該權限的操做。
當系統要求用戶授予權限時,用戶能夠選擇指示系統再也不要求提供該權限。這種狀況下,不管應用在何時使用 requestPermissions()
再次要求該權限,系統都會當即拒絕此請求。系統會調用您的 onRequestPermissionsResult()
回調方法,並傳遞 PERMISSION_DENIED
,若是用戶再次明確拒絕了您的請求,系統將採用相同方式操做。這意味着當您調用 requestPermissions()
時,您不能假設已經發生與用戶的任何直接交互。