從 Android 6.0 Marshmallow 開始,Android 使用權限模型簡化應用程式安裝和自動更新程序。系統會在執行階段 (而不是在安裝應用程式前) 要求權限。此外,使用者也可以選擇拒絕特定權限。 為了讓使用者享有這樣的彈性,您必須確保使用者啟用或停用特定權限時,應用程式能正常運作。
Google Play 服務本身俱備執行階段權限,使用者可選擇拒絕授予應用程式明確要求的權限。Google Play 服務會自動取得支援其 API 所需的所有權限。不過,即使使用者拒絕 Google Play 服務應用程式所用 API 所需的權限,應用程式仍應視需要檢查及要求執行階段權限,並以適當方式處理錯誤。
建議您管理使用者的期望,設定執行階段所需的權限。以下最佳做法有助於避免潛在問題。
必要條件
您需要在 AndroidManifest.xml
檔案中宣告權限。例如:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
規範
先驗證權限再呼叫 API
宣告要在 AndroidManifest.xml
檔案中使用的 API 後,請務必先取得必要的權限,再呼叫 API。您可以使用 ActivityCompat
或 ContextCompat
的 checkSelfPermission
方法完成這項操作。
如果呼叫傳回 false,表示未授予權限,而您應使用 requestPermissions
要求權限。此回應會透過回呼傳回,您將於下一步看到。
例如:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Check Permissions Now
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_LOCATION);
} else {
// permission has been granted, continue as usual
Task<Location> locationResult = LocationServices
.getFusedLocationProviderClient(this /** Context */)
.getLastLocation();
}
實作要求權限回呼
如果使用者尚未授予應用程式需要的權限,則應呼叫 requestPermissions
方法,請求使用者授予權限。系統會以 onRequestPermissionsResult
回呼擷取使用者的回應。應用程式應實作此方法,並一律檢查傳回值,因為要求可能會遭拒或取消。您也可以一次要求及檢查多項權限,下列範例只會檢查單一權限。
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults) {
if (requestCode == REQUEST_LOCATION) {
if(grantResults.length == 1
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// We can now safely use the API we requested access to
Task<Location> locationResult = LocationServices
.getFusedLocationProviderClient(this /** Context */)
.getLastLocation();
} else {
// Permission was denied or request was cancelled
}
}
}
顯示權限原因
如果您的應用程式要求應用程式核心功能所需的權限,而且使用者先前曾經拒絕權限要求,則應用程式應在再次要求權限前,顯示額外說明。如果使用者瞭解權限的原因和立即的好處,就更可能授予權限。
在這種情況下,應在呼叫 requestPermissions
之前呼叫 shouldShowRequestPermissionRationale
。如果傳回 true,您應建立一些 UI 來顯示該權限的其他背景資訊。
舉例來說,程式碼可能如下所示:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// Check Permissions Now
private static final int REQUEST_LOCATION = 2;
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
// Display UI and wait for user interaction
} else {
ActivityCompat.requestPermissions(
this, new String[]{Manifest.permission.LOCATION_FINE},
ACCESS_FINE_LOCATION);
}
} else {
// permission has been granted, continue as usual
Task<Location> locationResult = LocationServices
.getFusedLocationProviderClient(this /** Context */)
.getLastLocation();
}
處理連線失敗
如果您的應用程式使用已淘汰的 GoogleApiClient
,當您呼叫 connect()
時,Google Play 服務會驗證應用程式是否具備所有必要權限。缺少 Google Play 服務本身所需的任何權限群組時,connect()
會失敗。
如果呼叫 connect()
失敗,請確認應用程式正確處理連線失敗。如果 Google Play 服務本身缺少權限,您可以叫用 startResolutionForResult()
來啟動使用者流程修正問題。
例如:
@Override
public void onConnectionFailed(ConnectionResult result) {
if (mResolvingError) {
// Already attempting to resolve an error.
return;
} else if (result.hasResolution()) {
try {
mResolvingError = true;
result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
} catch (SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect();
}
} else {
// Show dialog using GooglePlayServicesUtil.getErrorDialog()
showErrorDialog(result.getErrorCode());
mResolvingError = true;
}
}
較新的 GoogleApi
型 API 呼叫會自動顯示對話方塊 (如果用戶端使用 Activity
執行個體化) 或系統匣通知 (如果用戶端已透過 Context
執行個體化),使用者可以輕觸通知來啟動權限解決意圖。取得權限後,系統會將呼叫排入佇列和重試。