Добро пожаловать в пример разрешений времени выполнения для Android. С появлением Android 6.0 Marshmallow Google изменила способ обработки разрешений приложением. В этом руководстве мы рассмотрим новую модель разрешений времени выполнения для Android и способы их обработки. Неправильная обработка может вызвать сбои приложения.
Что такое разрешения времени выполнения для Android?
С появлением Android 6.0 (SDK 23) пользователи получают запросы на предоставление конкретных разрешений во время выполнения, когда они становятся необходимы для использования. Таким образом, первый вопрос, который приходит в голову, – будут ли старые приложения работать на Android Marshmallow? Ответ – да, если targetSdkVersion меньше или равен 22. Таким образом, поддерживается обратная совместимость разрешений времени выполнения для Android. Однако это не означает, что мы можем использовать старую модель разрешений, установив версию SDK на 22. Пользователь, использующий Marshmallow, может отозвать опасные разрешения (о которых мы поговорим позже) в разделе Настройки->Приложения->Разрешения. В случае, если мы пытаемся вызвать функцию, требующую разрешение, которое пользователь еще не предоставил, функция внезапно вызовет исключение (java.lang.SecurityException
), что приведет к сбою приложения. Поэтому нам необходимо реализовать эту новую модель разрешений Android в нашем приложении.
Опасные и нормальные разрешения Android
Android определяет некоторые разрешения как опасные, а некоторые как нормальные. Общее для обоих типов заключается в том, что они должны быть определены в файле манифеста. Начиная с Android 6.0, только опасные разрешения проверяются во время выполнения, нормальные разрешения – нет. Примером нормального разрешения является android.permission.INTERNET
. Опасные разрешения группируются по категориям, что упрощает понимание пользователю, что он разрешает приложению делать. Если пользователь принимает одно разрешение в группе/категории, он принимает всю группу. Примером опасного разрешения являются android.permission.FINE_LOCATION
и android.permission.COARSE_LOCATION
. Включение любого из разрешений на местоположение включает их все.
Запрос разрешений времени выполнения Android
Метод requestPermissions(String[] permissions, int requestCode);
является публичным методом, который используется для запроса опасных разрешений. Мы можем запрашивать несколько опасных разрешений, передавая массив строк разрешений. Примечание: Разрешения Android, принадлежащие двум разным группам, будут вызывать у пользователя индивидуальный диалог для каждого из них. Если они принадлежат к одной и той же группе, то будет отображен только один диалоговый запрос. Результаты запросов будут переданы в метод onRequestPermissionResult
. Пример: Предположим, мы хотим получить доступ к камере и местоположению в нашем приложении. Оба являются опасными разрешениями. Мы отобразим запрос на доступ к этим разрешениям при запуске приложения. Добавим разрешения в массив строк и вызовем requestPermissions, как показано ниже:
String[] perms = {"android.permission.FINE_LOCATION", "android.permission.CAMERA"};
int permsRequestCode = 200;
requestPermissions(perms, permsRequestCode);
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
switch(permsRequestCode){
case 200:
boolean locationAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;
break;
}
}
Теперь мы не хотим, чтобы пользователь продолжал принимать разрешения, которые он уже принял. Даже если разрешение было предварительно предоставлено, необходимо проверить его снова, чтобы убедиться, что пользователь позже не отозвал это разрешение. Для этого на каждом разрешении нужно вызвать следующий метод.
checkSelfPermission(String perm);
Он возвращает целочисленное значение PERMISSION_GRANTED или PERMISSION_DENIED. Примечание: Если пользователь отклоняет разрешение, которое критично для приложения, то shouldShowRequestPermissionRationale(String permission);
используется для описания пользователю необходимости разрешения. Давайте разработаем приложение, которое проверяет, есть ли разрешение уже в наличии. Если нет, то оно запрашивается во время выполнения.
Структура проекта Android Runtime Permissions
Код разрешений времени выполнения Android
content_main.xml
содержит две кнопки для проверки и запроса разрешений.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.journaldev.runtimepermissions.MainActivity"
tools:showIn="@layout/activity_main">
<Button
android:id="@+id/check_permission"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:text="Check Permission"/>
<Button
android:id="@+id/request_permission"
android:layout_below="@+id/check_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Request Permission"/>
</RelativeLayout>
MainActivity.java
определен следующим образом.
package com.journaldev.runtimepermissions;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int PERMISSION_REQUEST_CODE = 200;
private View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button check_permission = (Button) findViewById(R.id.check_permission);
Button request_permission = (Button) findViewById(R.id.request_permission);
check_permission.setOnClickListener(this);
request_permission.setOnClickListener(this);
}
@Override
public void onClick(View v) {
view = v;
int id = v.getId();
switch (id) {
case R.id.check_permission:
if (checkPermission()) {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view, "Please request permission.", Snackbar.LENGTH_LONG).show();
}
break;
case R.id.request_permission:
if (!checkPermission()) {
requestPermission();
} else {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
}
break;
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getApplicationContext(), ACCESS_FINE_LOCATION);
int result1 = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0) {
boolean locationAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (locationAccepted && cameraAccepted)
Snackbar.make(view, "Permission Granted, Now you can access location data and camera.", Snackbar.LENGTH_LONG).show();
else {
Snackbar.make(view, "Permission Denied, You cannot access location data and camera.", Snackbar.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
showMessageOKCancel("You need to allow access to both the permissions",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{ACCESS_FINE_LOCATION, CAMERA},
PERMISSION_REQUEST_CODE);
}
}
});
return;
}
}
}
}
break;
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
}
Примечание: добавьте разрешения, которые должны быть проверены во время выполнения, в файле манифеста выше тега приложения, как;
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
В вышеуказанном коде проверяются и запрашиваются два разрешения: CAMERA и LOCATION. Импортирование полного имени класса статического разрешения позволяет нам использовать только объект PERMISSION вместо полного квалифицированного пути. Метод checkPermission() вызывает checkSelfPermission для каждого из разрешений. Метод requestPermission() вызывает ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);. Метод onRequestPermissionsResult проверяет, предоставлены ли разрешения или нет. В нашем коде, если оба разрешения не предоставлены, появляется диалоговое окно, указывающее на обязательную необходимость запроса разрешений. Для этого вызывается метод shouldShowRequestPermissionRationale(String permission), который вызывает диалоговое окно, показывающее необходимость разрешений. Разрешения можно отозвать вручную в разделе Настройки->Приложения->Разрешения. Примечание: Специфичные для времени выполнения методы разрешений доступны только с API 23. Поэтому в каждом из методов проверяется следующее условие:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
Результат работы примера приложения с разрешениями Android представлен ниже. Это завершает этот учебник. Вы можете скачать финальный проект Android Runtime Permissions по ссылке ниже.
Скачать проект примера разрешений времени выполнения Android
Ссылка: https://developer.android.com/training/permissions/requesting.html
Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example