標籤:
原文Google
原文地址:http://developer.android.com/intl/zh-cn/training/permissions/requesting.html
原文著作權:Creative Commons 2.5 Attribution License
譯文Jianan - [email protected]
版本資訊:本文基於2016-04-27版本翻譯
譯文著作權:CC BY-NC-ND 4.0,允許複製轉載,但必須保留譯文作者署名及譯文連結,不得演繹和用於商業用途
前言
從android 6.0(API 23)開始,安裝app時不需要對app的許可權申請進行授權,而是在app啟動並執行時候,使用者才需要對app進行授權。這種流程精簡了app的安裝過程,使用者不需要在安裝或者升級app的時候進行授權操作。這同樣也給了使用者更多對app功能的控制能力;例如,使用者可以選擇給一個照相app訪問網路攝影機的許可權,但不給它訪問裝置地理位置的許可權。使用者也可以通過app的設定介面,隨時撤銷對app授與權限。
系統許可權可以被分為兩大類,普通許可權和危險許可權:
- 普通許可權不會引起使用者隱私泄露的風險。如果你的app在manifest檔案中列舉了一個普通許可權,系統會自動對申請的普通許可權直接授權。
- 危險許可權允許app訪問使用者的機密資訊。如果的你app在它的manifest中列舉了一個普通許可權,系統會自動授權。但如果你列舉的是一個危險許可權,使用者就必須明確處理是否授權給你的app。
更多相關資訊,請參考普通許可權和危險許可權。
無論是哪個android版本,你的app都必須在它的manifest檔案上聲明它需要的所有普通許可權和危險許可權,詳情請參考聲明許可權。只不過,聲明許可權的結果因系統版本和app的target SDK不同而不同:
- 如果裝置運行在android 5.1或者更低版本的系統上,或者app的target SDK是22或者更低:如果你的app在它的manifest檔案中聲明了一個危險許可權,那麼使用者在安裝app的時候就必須授權這個危險許可權給你的app;如果使用者不同意授予這個許可權,系統就拒絕安裝這個app。
- 如果裝置運行在android 6.0或者更高的系統版本上,並且app的target SDK是23或者更高:app必須在manifest檔案中聲明所有需要的許可權,並且在app啟動並執行過程中必須請求使用者對每個使用到的危險許可權進行授權。使用者可以選擇授權或者拒絕每一個許可權,如果使用者拒絕了授權,app同樣可以繼續運行,只是對應的功能無法使用。
注意: 從android 6.0(API 23)開始,不管app的target SDK是不是低版本的SDK,使用者都可以隨時取消任意app已經授權過的許可權。不管你的app是什麼target SDK版本,當你的app缺少一個需要的許可權時,你都應該測試的app功能是否正常。
本章節內容將探討如何使用android support library來檢查和請求許可權。android的framework在android 6.0(API 23)版本上提供了類似的方法。不過,相對來說使用support library的方法會比較簡單,因為這樣你的app在調用這些方法之前就不需要檢查當前啟動並執行android系統版本是多少。
檢查許可權
如果你的app需要一個危險許可權,那麼你每次執行需要這個危險許可權的操作時,你都必須檢查是否已經申請到這個許可權。使用者隨時都可以撤銷授予過的許可權,因此,儘管一個app昨天可以正常使用網路攝影機,但不代表今天仍然擁有使用網路攝影機的許可權。
檢查你的app是否擁有過一個許可權,需要調用ContextCompat.checkSelfPermission()方法。例如,下面的程式碼片段檢查了activity是否擁有寫日曆的許可權:
// Assume thisActivity is the current activityint permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);
如果app擁有這個許可權,方法會返回PackageManager.PERMISSION_GRANTED,app可以繼續執行相關操作。如果app不具備該許可權,方法會返回PERMISSION_DENIED,app需要明確請求使用者授予該許可權。
申請許可權
如果你的app需要使用一個在它的manifest檔案中聲明的危險許可權,那麼在使用之前它必須請求使用者授予該許可權。android提供了一系列方法供你申請許可權。請求這些方法會喚起一個標準的android對話方塊,這個對話方塊你不能自訂。
解釋app為什麼需要許可權
在一些情況下,你可能想要協助使用者理解為什麼你的app需要一個許可權。例如,如果使用者啟動一個攝影app,使用者應該對app請求使用網路攝影機的許可權不會感到驚訝,但是使用者可能無法理解為什麼app還要求訪問裝置的地理位置和通訊錄。在你申請一個許可權之前,你應該考慮給使用者提供一個解釋。但是要注意不要用解釋過多騷擾使用者;如果你提供了太多的解釋,使用者可能會厭煩你的app並且卸載掉。
只有在使用者已經拒絕了許可權請求的情況下,你才有可能需要向使用者提供一個解釋。如果使用者繼續嘗試使用需要申請許可權的功能,但是這個許可權之前已經被使用者拒絕,這就說明使用者可能不清楚為什麼app的這個功能需要申請許可權。類似這樣的情況,那麼顯示一個許可權申請解釋就是一種很好的解決方案。
為了協助判斷使用者在什麼情況下需要一個許可權解釋,android提供了一個工具方法,ActivityCompat.shouldShowRequestPermissionRationale()。如果app之前申請過一個許可權,但這個許可權被使用者拒絕了,這個方法就會返回true。
注意: 如果使用者在過去拒絕許可權申請的同時還勾選了系統許可權申請對話方塊中的不再詢問選項,這個方法也會返回false。如果是在裝置使用權限設定中禁止了app使用的許可權,用這個方法判斷這些許可權同樣也會返回false。
申請你需要的許可權
如果你的app目前不具備它所需要的許可權,app必須調用requestPermissions()多個重載方法中的一個來申請適當的許可權。這些方法中,你的app除了將所需要的許可權作為參數傳遞進去之外,還需要傳遞一個整型值作為本次許可權請求的標識碼。這些方法都是非同步:方法會直接返回,等到使用者響應對話方塊之後,系統才會回調app的回調方法把結果傳遞迴來,結果中包括app調用requestPermissions()方法時傳遞進來的請求碼。
下面的代碼檢查app是否擁有讀取使用者連絡人的許可權,如果沒有,在需要的時候會申請許可權:
// Here, thisActivity is the current activityif (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. }}
注意: 當你的app調用requestPermissions()方法時,系統會彈出一個標準的對話方塊給使用者。你的app無法配置或者修改這個對話方塊。如果你需要向使用者提供一些的資訊或者解釋,你應該在調用requestPermissions()方法之前進行這些操作,請參考上面小節解釋app為什麼需要許可權。
處理許可權請求的響應
當你的app申請許可權之後,系統會顯示一個對話方塊給使用者。當使用者響應之後,系統會回調你的app的onRequestPermissionsResulet()方法,將響應結果傳遞過來。你的app必須重寫這個方法來判斷申請的許可權是否被授予了。回調方法會接收到與你傳遞到requestPermissions()方法中相同的請求碼。例如,如果一個app請求READ_CONTACTS存取權限,它可能需要以下的響應代碼:
@Overridepublic 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 }}
這個系統顯示的對話方塊會描述你的app需要訪問的許可權組,它不會列舉具體使用到的哪一個許可權。例如,如果你請求READ_CONTACTS許可權,系統對話方塊只會描述你的app需要訪問裝置的通訊錄。使用者只需要對每一個許可權組授權一次。一旦對一個許可權組授權之後,如果你的app請求同一個許可權組中的其它許可權(同樣也要在app的manifest檔案聲明),系統會自動授予這些許可權。當你請求這些同個許可權組中的其它許可權時,系統也會回調你的onRequestPermissionsResult()方法並傳遞PERMISSION_GRANTED參數,回調的結果就跟使用者已經通過系統對話方塊明確同意你的許可權申請一樣。
注意: 你的app仍然需要明確請求每一個它需要的許可權,不過使用者是否已經同意授權同個許可權組中的其它許可權。此外,每個許可權組包含的許可權在未來android的發布版中也有可能會變化。你的代碼邏輯不應該建立在使用到的幾個許可權是否被包含在同一個許可權組中的假設之上。
例如,假設你在app的manifest中聲明了READ_CONTACTS和WRITE_CONTACTS許可權。如果你已經請求過READ_CONTACTS許可權並且使用者通過授權申請,當你再請求WRITE_CONTACTS許可權時,系統會立即把這個許可權也授權給app,不需要再與使用者進行授權互動。
如果使用者拒絕了你的許可權申請,你的app應該採取適當的響應操作。例如,你的app可能需要顯示一個對話方塊來解釋為什麼你的app無法繼續執行下去的原因是需要被使用者拒絕的許可權。
當系統請求使用者授權時,使用者可以選擇告訴系統需要再次請求這個許可權。在這種情況下,無論app調用多少次requestPermissions()方法來申請許可權,系統都會直接決絕申請。系統會回調你的onRequestPermissionsResult()回調方法並且傳遞PERMISSION_DENIED參數值,整個回調過程就跟使用者已經直接明確拒絕你的許可權申請一樣。這意味著當你調用requestPermissions()方法時,你不能假設與使用者直接互動的操作一定會發生。
app在android 6.0或以上平台版本運行過程中請求許可權