Сервисы Google Play и разрешения во время выполнения

Начиная с 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

После того как вы объявили API, которые хотите использовать, в файле AndroidManifest.xml , убедитесь, что у вас есть необходимое разрешение, прежде чем вызывать API. Это можно сделать с помощью метода checkSelfPermission класса ActivityCompat или ContextCompat .

Если вызов возвращает 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, вам следует создать пользовательский интерфейс для отображения дополнительного контекста для разрешения.

Например, ваш код может выглядеть так:

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 проверяют наличие у него всех необходимых разрешений. connect() завершается с ошибкой, если отсутствуют какие-либо группы разрешений, необходимые самим сервисам Google Play.

Если вызов 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;
    }
}

Новые вызовы API на основе GoogleApi автоматически отображают либо диалоговое окно (если экземпляр клиента создается с помощью Activity ), либо уведомление на панели задач (если экземпляр клиента создается с помощью Context ), которые пользователь может нажать, чтобы начать намерение разрешения разрешений. Вызовы будут поставлены в очередь и повторены после получения разрешения.