Службы 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) {
            // Request Permissions Now
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
    } else {
        // permission has been granted, continue as usual
        val locationResult = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }

Ява

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Request Permissions Now
        ActivityCompat.requestPermissions(
            this,
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            REQUEST_LOCATION_PERMISSION_CODE);
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }

Реализовать обратный вызов запроса разрешения

Если необходимое вашему приложению разрешение не предоставлено пользователем, следует вызвать метод requestPermissions , чтобы запросить его. Ответ пользователя фиксируется в обратном вызове onRequestPermissionsResult . Ваше приложение должно реализовать эту функцию и всегда проверять возвращаемые значения, поскольку запрос может быть отклонён или отменён. Вы также можете запросить и проверить наличие нескольких разрешений одновременно — в следующем примере проверяется только одно разрешение.

Котлин

    fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if (grantResults.singleOrNull() == PackageManager.PERMISSION_GRANTED) {
               // We can now safely use the API we requested access to
               val locationResult: Task = LocationServices
                    .getFusedLocationProviderClient(this /* Context */)
                    .lastLocation // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }

Ява

    public void onRequestPermissionsResult(int requestCode,
                                            String[] permissions,
                                            int[] grantResults) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if(grantResults.length == 1
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // We can now safely use the API we requested access to
                Task locationResult = LocationServices
                    .getFusedLocationProviderClient(this /** Context */)
                    .getLastLocation(); // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }

Показать обоснование разрешения

Если запрашиваемые вашим приложением разрешения необходимы для работы основных функций, а пользователь ранее отклонил запрос, ваше приложение должно отображать дополнительное объяснение перед повторным запросом. Пользователи с большей вероятностью предоставят разрешения, когда понимают, зачем они нужны и какую непосредственную выгоду это им принесёт.

В этом случае перед вызовом requestPermissions необходимо вызвать shouldShowRequestPermissionRationale . Если метод возвращает true, необходимо создать пользовательский интерфейс для отображения дополнительного контекста для разрешения.

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

Котлин

    private const val REQUEST_LOCATION_PERMISSION_CODE = 2
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        if ActivityCompat.shouldShowRequestPermissionRationale(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) {
            // Display UI and wait for user interaction
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
        }
    } else {
        // Permission has already been granted, continue as usual
        val locationResult: Task = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }

Ява

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        private static final int REQUEST_LOCATION_PERMISSION_CODE = 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},
                REQUEST_LOCATION_PERMISSION_CODE);
        }
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }

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