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