Google Play-Dienste und Laufzeitberechtigungen

Seit Android 6.0 Marshmallow verwendet Android ein Berechtigungsmodell, das die App-Installation und die automatischen Updates optimiert. Berechtigungen werden zur Laufzeit und nicht vor der App-Installation angefordert. Außerdem können Nutzer bestimmte Berechtigungen verweigern. Damit Nutzer diese Flexibilität erhalten, müssen Sie dafür sorgen, dass sich Ihre App erwartungsgemäß verhält, wenn ein Nutzer eine bestimmte Berechtigung aktiviert oder deaktiviert.

Die Google Play-Dienste selbst haben Laufzeitberechtigungen, die Nutzer separat von den von Ihrer App angeforderten Berechtigungen ablehnen können. Die Google Play-Dienste erhalten automatisch alle Berechtigungen, die zur Unterstützung der APIs erforderlich sind. Ihre App sollte jedoch weiterhin Laufzeitberechtigungen prüfen und bei Bedarf anfordern und Fehler angemessen beheben, wenn ein Nutzer Google Play-Diensten eine Berechtigung verweigert hat, die für eine von Ihrer App verwendete API erforderlich ist.

Es hat sich bewährt, die vom Nutzer erwarteten Berechtigungen zu berücksichtigen, die für die Laufzeit möglicherweise erforderlich sind. Die folgenden Best Practices helfen Ihnen, potenzielle Probleme zu vermeiden.

Voraussetzungen

Sie müssen die Berechtigungen in der Datei AndroidManifest.xml deklarieren. Beispiel:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Richtlinien

Berechtigungen vor dem Aufrufen von APIs prüfen

Nachdem Sie die APIs, die Sie verwenden möchten, in der Datei AndroidManifest.xml deklariert haben, müssen Sie prüfen, ob Sie die erforderliche Berechtigung haben, bevor Sie eine API aufrufen. Dazu können Sie die Methode checkSelfPermission von ActivityCompat oder ContextCompat verwenden.

Wenn der Aufruf „false“ zurückgibt, wurden die Berechtigungen nicht gewährt und Sie sollten sie mit requestPermissions anfordern. Die Antwort wird in einem Callback zurückgegeben, das im nächsten Schritt angezeigt wird.

Beispiel:

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();
}

Callback für die Berechtigungsanfrage implementieren

Wenn der Nutzer die Berechtigung, die Ihre App benötigt, nicht erteilt hat, sollte die Methode requestPermissions aufgerufen werden, um den Nutzer aufzufordern, sie zu gewähren. Die Antwort des Nutzers wird im onRequestPermissionsResult-Callback erfasst. Ihre Anwendung sollte dies implementieren und immer die Rückgabewerte prüfen, da die Anfrage abgelehnt oder abgebrochen werden könnte. Sie können auch mehrere Berechtigungen gleichzeitig anfordern und prüfen. Im folgenden Beispiel wird nur für eine einzelne Berechtigung geprüft.

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
        }
    }
}

Begründung für die Berechtigung anzeigen

Wenn die von Ihrer App angeforderten Berechtigungen für die Hauptfunktionen der App erforderlich sind und der Nutzer die Berechtigungsanfrage zuvor abgelehnt hat, sollte in Ihrer App eine zusätzliche Erklärung angezeigt werden, bevor die Berechtigung noch einmal angefordert wird. Nutzer erteilen Berechtigungen eher, wenn sie verstehen, warum die Berechtigung benötigt wird und welchen unmittelbaren Nutzen sie haben.

In diesem Fall sollten Sie vor dem Aufrufen von requestPermissions shouldShowRequestPermissionRationale aufrufen. Wenn der Rückgabewert „true“ lautet, sollten Sie eine UI erstellen, um zusätzlichen Kontext für die Berechtigung anzuzeigen.

Ihr Code könnte beispielsweise so aussehen:

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();
}

Verbindungsfehler beheben

Wenn Ihre App das verworfene GoogleApiClient verwendet, prüfen die Google Play-Dienste beim Aufruf von connect(), ob sie alle erforderlichen Berechtigungen hat. connect() schlägt fehl, wenn Berechtigungsgruppen fehlen, die von den Google Play-Diensten selbst benötigt werden.

Wenn der Aufruf von connect() fehlschlägt, prüfen Sie, ob Ihre Anwendung Verbindungsfehler korrekt verarbeitet. Wenn den Google Play-Diensten selbst Berechtigungen fehlen, können Sie startResolutionForResult() aufrufen, um den User Flow zu starten und das Problem zu beheben.

Beispiel:

@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;
    }
}

Bei neueren GoogleApi-basierten API-Aufrufen wird automatisch entweder ein Dialogfeld (wenn der Client mit einem Activity instanziiert wird) oder eine Benachrichtigung in der Taskleiste (wenn der Client mit einer Context instanziiert wird) angezeigt, auf die der Nutzer tippen kann, um den Intent zur Auflösung von Berechtigungen zu starten. Aufrufe werden in die Warteschlange eingereiht und wiederholt, sobald die Berechtigung gewährt wurde.