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