В этой статье мы рассмотрим работу с получением разрешений во время выполнения, которые были добавлены в Android 6 (API 23).
Runtime Permissions в Android API 23. Теория
Одним из основных изменений в Android API 23 является новая система разрешений. В более ранних версиях разрешения объявляли в файле AndroidManifest.xml
и больше никаких действий не требовалось. Но с Android 6 мы должны запрашивать разрешения из категории «Опасных» во время выполнения.
В документации по работе с разрешениями указано, что их следует получать не все сразу, а по мере необходимости той или иной функции в процессе работы приложения.
Прежде чем запрашивать разрешения, нам нужно описать их в файле AndroidManifest.xml
.
Процесс получения разрешений:
1). Узнать, получено ли в приложении необходимое разрешение. Например, так:
1 2 3 4 5 6 |
private boolean isPermissionGranted(String permission) { // проверяем разрешение - есть ли оно у нашего приложения int permissionCheck = ActivityCompat.checkSelfPermission(this, permission); // true - если есть, false - если нет return permissionCheck == PackageManager.PERMISSION_GRANTED; } |
2). Если разрешения нет, то запросить его с помощью метода requestPermissions()
:
1 2 3 4 5 |
private void requestPermission(String permission, int requestCode) { // запрашиваем разрешение ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } |
Сразу несколько разрешений можно запросить так:
1 2 3 4 5 6 7 8 |
public void requestMultiplePermissions() { ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.READ_PHONE_STATE Manifest.permission.READ_SMS }, PERMISSION_REQUEST_CODE); } |
3). Обработать результат запроса на получение разрешений можно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { // проверка по запрашиваемому коду if (requestCode == REQUEST_READ_PHONE_STATE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // разрешение успешно получено } else { // разрешение не получено } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } |
Обратите внимание на массив grantResults[]
— здесь хранятся результаты по каждому из запрашиваемых разрешений. Если в методе requestPermissions()
мы запрашивали 1 разрешение, то обрабатываем только 1 значение массива результатов: grantResults[0]
.
Некоторые разрешения перекочевали в раздел настроек: мы все также описываем их в файле манифеста, однако обрабатывать их нужно уже не внутри приложения, а на странице настроек приложения.
Давайте на примере посмотрим на разные способы получения разрешений.
Runtime Permissions в Android API 23. Практика
В нашем тестовом приложении мы будем получать данные телефона, поэтому нам нужно запрашивать разрешение на получение Manifest.permission.READ_PHONE_STATE
.
В коде ниже у нас описан весь процесс обработки так называемых «Опасных» разрешений, которые требуют взаимодействия с пользователем. Для начала создайте новый проект в Android Studio.
Идем в файл AndroidManifest.xml
и добавляем разрешение на работу со звонками:
1 |
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> |
Теперь идем в MainActivity.java и описываем процесс получения разрешений и обработку результатов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
package com.javadevblog.runtimepermissionsapp; import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private static final int REQUEST_READ_PHONE_STATE = 10001; // объявляем разрешение, которое нам нужно получить private static final String READ_PHONE_STATE_PERMISSION = Manifest.permission.READ_PHONE_STATE; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // проверяем разрешения: если они уже есть, // то приложение продолжает работу в нормальном режиме if (isPermissionGranted(READ_PHONE_STATE_PERMISSION)) { Toast.makeText(this, "Разрешения есть, можно работать", Toast.LENGTH_SHORT).show(); } else { // иначе запрашиваем разрешение у пользователя requestPermission(READ_PHONE_STATE_PERMISSION, REQUEST_READ_PHONE_STATE); } } private boolean isPermissionGranted(String permission) { // проверяем разрешение - есть ли оно у нашего приложения int permissionCheck = ActivityCompat.checkSelfPermission(this, permission); return permissionCheck == PackageManager.PERMISSION_GRANTED; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { if (requestCode == REQUEST_READ_PHONE_STATE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(MainActivity.this, "Разрешения получены", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Разрешения не получены", Toast.LENGTH_LONG).show(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void requestPermission(String permission, int requestCode) { // запрашиваем разрешение ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } } |
Если запустить приложение, то получим такой результат:
На первом экране мы запрашиваем разрешение. 2 — успешное их получение. 3 — Разрешение не получено
Задумка с разрешениями сводится к тому, что если пользователь не разрешит приложению доступ к чему-то, то мы должны отключить эту функциональность в приложении и работать с остальным функционалом приложения. А как же быть, если запрашиваемое разрешение является критическим для нашего приложения?
Например, работа со звонками является основой приложения, а пользователь запретил доступ. В этом случае при следующем запуске пользователю следует давать возможность изменить свой выбор, иначе — приложение закрывается. Давайте реализуем эту функциональность в нашем приложении:
Новую функциональность я специально выделил:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
package com.javadevblog.runtimepermissionsapp; import android.Manifest; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private static final int REQUEST_READ_PHONE_STATE = 10001; // объявляем разрешение, которое нам нужно получить private static final String READ_PHONE_STATE_PERMISSION = Manifest.permission.READ_PHONE_STATE; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // проверяем разрешения: если они уже есть, // то приложение продолжает работу в нормальном режиме if (isPermissionGranted(READ_PHONE_STATE_PERMISSION)) { Toast.makeText(this, "Разрешения есть, можно работать", Toast.LENGTH_SHORT).show(); } else { // иначе запрашиваем разрешение у пользователя requestPermission(READ_PHONE_STATE_PERMISSION, REQUEST_READ_PHONE_STATE); } } private boolean isPermissionGranted(String permission) { // проверяем разрешение - есть ли оно у нашего приложения int permissionCheck = ActivityCompat.checkSelfPermission(this, permission); return permissionCheck == PackageManager.PERMISSION_GRANTED; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { if (requestCode == REQUEST_READ_PHONE_STATE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(MainActivity.this, "Разрешения получены", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Разрешения не получены", Toast.LENGTH_LONG).show(); showPermissionDialog(MainActivity.this); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void requestPermission(String permission, int requestCode) { // запрашиваем разрешение ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } private void showPermissionDialog(Context context) { AlertDialog.Builder builder = new AlertDialog.Builder(context); String title = getResources().getString(R.string.app_name); builder.setTitle(title); builder.setMessage(title + " требует разрешение на доступ к звонкам"); String positiveText = "Настройки"; builder.setPositiveButton(positiveText, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { openAppSettings(); } }); String negativeText = "Выход"; builder.setNegativeButton(negativeText, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { finish(); } }); AlertDialog dialog = builder.create(); // display dialog dialog.show(); } private void openAppSettings() { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_READ_PHONE_STATE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_READ_PHONE_STATE) { requestApplicationConfig(); } } private void requestApplicationConfig() { if (isPermissionGranted(READ_PHONE_STATE_PERMISSION)) { Toast.makeText(MainActivity.this, "Теперь уже разрешения получены", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Пользователь снова не дал нам разрешение", Toast.LENGTH_LONG).show(); requestPermission(READ_PHONE_STATE_PERMISSION, REQUEST_READ_PHONE_STATE); } } } |
В коде выше мы создали свой AlertDialog, в котором просим пользователя пойти в настройки и все-таки включить нужное разрешение, иначе приложение просто закончит работу.
Запустим приложение на устройстве с Android 6 (API 23) и посмотрим результат:
Результат здесь
Пользователь отклоняет запрос на получение разрешение, после чего приложение просит его перейти в настройки и все-таки дать разрешения — иначе оно просто закрывается
Исходный код приложения здесь.
Обновляйте свои приложения до API 23 и выше с механизмом Android Runtime Permissions. Подписывайтесь на новые статьи по разработке под Android и Java программированию!
Спасибо, очень полезная статья!
Превосходная статья. Не то, что у этих рукожопов из гугла…