Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Обновлено 20.08.2024, 14 октября 2025
Одно разрешение
Несколько разрешений
Начиная с Android 6.0 Marshmallow, радикально изменилась модель разрешений для доступа к важным системным настройкам.
Разрешения представляют собой набор прав, которые приложения должны запрашивать у пользователей, чтобы получить доступ к различным функциям и данным на устройстве. Эти разрешения охватывают широкий спектр возможностей, начиная от доступа к камере и микрофону и заканчивая чтением контактов и получением информации о местоположении. Система разрешений предназначена для защиты конфиденциальной информации пользователей и поддержания целостности операционной системы.
Старая система, когда пользователь скачивал приложение и перед установкой видел диалоговое окно, в котором перечислялись используемые разрешения, ушла в прошлое. Пользователя ставили перед фактом, и он никак не мог повлиять на настройки.
Теперь пользователи могут контролировать доступ приложений к ресурсам устройства. Запросы будут появляться прямо во время работы приложения и пользователи смогут выбрать, давать разрешение или нет. А также смогут управлять правами приложений через системное меню настроек (Settings | Apps | Any AppName). Данное изменение потребует от разработчиков создавать приложения таким образом, чтобы запросы на доступ появлялись, когда это требуется, и учитывать, что некоторые разрешения на доступ не будут получены. Но краха приложений не будет, система попытается подставить пустые значение.
Например, каждый раз, когда приложение обращается с запросом на местоположение пользователя, мы должны проверить, есть ли у приложения разрешение от пользователя на это действие. Если есть — обращаемся к нужным нам системным ресурсам, если нет — запрашиваем. Так же пользователь может навсегда приложению запретить доступ, тогда единственный наш шанс — это попросить его самого зайти в настройки и снять запрет, показав ему объясняющее сообщение, зачем нам нужен доступ.
Разрешения делятся на два типа:
Нормальные разрешения автоматически предоставляются системой во время установки. Когда приложению требуется нормальное разрешение, оно должно быть объявлено в файле AndroidManifest.xml. В процессе установки система Android проверяет разрешения, запрашиваемые приложением, и автоматически предоставляет их без какого-либо вмешательства со стороны пользователя.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
...>
<activity android:name=".MainActivity">
...
</activity>
</application>
</manifest>
В отличие от обычных нормальных разрешений, которые предоставляются автоматически, опасные разрешения требуют явного согласия пользователя. Это означает, что, когда приложение запрашивает опасное разрешение, пользователь должен одобрить его в соответствующем диалоговом окне. Все опасные разрешения мы должны постоянно проверять, так как пользователь может в любой момент их запретить. Да и при первом старте доступа у приложения к ним нет. Последовательность наших шагов:
Чтобы проверить доступность разрешения, вызываем ContextCompat.checkSelfPermission (Context context, String permission). Метод требует контекст и название разрешения. Метод возвращает константу PackageManager.PERMISSION_GRANTED (если разрешение есть) или PackageManager.PERMISSION_DENIED (если разрешения нет). Если разрешение есть, значит мы ранее его уже запрашивали, и пользователь подтвердил его.
Если разрешения нет, то переходим к следующему шагу. Чтобы запросить разрешения, показав системный диалог, вызываем ActivityCompat.requestPermissions(). Мы вызываем метод, передаём ему данные и request code, а ответ потом получаем в методе onResult (см. ниже).
Результат запроса придёт в асинхронный колбэк onRequestPermissionsResult(), в нём мы узнаем решение пользователя по каждому из запрошенных разрешений.
Запрашивать следует лишь те разрешения, которые действительно нужны.
Если есть возможность, вместо запроса воспользуйтесь Intent. Например, для фото или видео часто нет смысла встраивать камеру в приложение, гораздо проще воспользоваться внешним приложением.
Запрашивайте разрешение, только перед тем, когда оно понадобится. Запрашивать при старте приложения все разрешения нелогично (из тех, которые нам нужны), их смысл как раз в том, что мы запрашиваем их в контексте их использования. Например, пользователю становится понятно зачем его банковскому клиенту доступ к контактам — чтобы выбрать одного из них, чтобы поделиться информацией по ФИО.
Поясняйте пользователю, для чего запрашивается разрешение. Если пользователь все же запретил приложению доступ, а без него оно не может, оно должно максимально понятно объяснить, что без этого разрешения оно работать дальше не будет.
Нужно разобраться с алгоритмом обработки требуемого разрешения.

Первое - сначала нужно убедиться, что у приложения есть разрешения для выполнения задачи.
Система проверяет, получило ли приложение необходимое разрешение через метод checkSelfPermission().
Если разрешение имеется, то обрабатываем результат и продолжаем работу. В противном случае система проверяет, спрашивали ли мы у пользователя о предоставлении разрешения и выводит стандартное диалоговое окно, если такого запроса не было. Если пользователь ни разу не был спрошен, то выводится схожее диалоговое окно с дополнительным флажком. Если флажок будет отмечен, то результат будет возвращён в метод onRequestPermissionsResult() и соответствующим образом обработан.
Ничего страшного, если пользователь отклонит разрешение в первый раз. В нужный момент вы всегда можете снова обратиться к пользователю, убеждая его в том, что без запрашиваемого разрешения приложение не сможет предоставить ему нужную функциональность. Здесь нам поможет метод shouldShowRequestPermissionRationale().
Допустим, наше приложение должно уметь определять местоположение пользователя. Для этого требуется разрешение android.permission.ACCESS_FINE_LOCATION. Пропишем его как и раньше в манифесте (вдобавок студия попросит дополнительное разрешение).
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Далее напишем код для запроса на получение разрешения.
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.permission
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
class MainActivity : AppCompatActivity() {
private val REQUEST_LOCATION = 100
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
requestSinglePermission()
}
}
private fun requestSinglePermission() {
val locationPermission = Manifest.permission.ACCESS_FINE_LOCATION
val hasPermission = ActivityCompat.checkSelfPermission(this, locationPermission)
val permissions = arrayOf(locationPermission) // массив разрешений
if (hasPermission != PackageManager.PERMISSION_GRANTED) {
// Выводим диалоговое окно
ActivityCompat.requestPermissions(this, permissions, REQUEST_LOCATION)
} else {
// У нас уже было разрешение
println("У нас есть есть разрешение на определение местоположения")
}
}
}
Могут быть и другие варианты для разрешений, например Manifest.permission.SEND_SMS и т.д.
Метод checkSelfPermission() проверяет разрешение. Если вы забудете прописать в манифесте нужное разрешение, то метод вернёт значение PERMISSION_DENIED. Если вы прописали разрешение в манифесте, то метод вернёт PERMISSION_GRANTED. В таком случае вы можете вывести диалоговое окно при помощи метода requestPermissions(). Обратите внимание, что в методе используется массив разрещений permissions. В нашем случае достаточно одного элемента массива.

У пользователя будет два варианта - принять или отклонить запрашиваемое разрешение. Если пользователь выберет вариант Запретить, то вернётся константа PERMISSION_DENIED. Это означает, что при следующем нажатии на кнопку диалоговое окно больше не появится. Тут вы бессильны. Чтобы вернуться к первоначальным настройкам, нужно зайти в Настройки Приложения - Ваше приложение - раздел Разрешения и вручную включить разрешения.
Теперь рассмотрим другой вариант - пользователь вам доверяет и выбрал вариант Только в этот раз. Приложение получило разрешение на получение определения местоположения. Повторные нажатия на кнопку уже не будут выводить диалоговое окно - разрешение-то уже имеется. И мы можем выполнить какой-то код. Не забываем, что пользователь может опять зайти в настройки и отозвать разрешение.
Прекрасно, мы разобрались, как работает система. Но как определить выбранный вариант пользователя? Ответ от диалогового окна поступает в метод onRequestPermissionsResult().
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode == REQUEST_LOCATION && grantResults.isNotEmpty()){
for(i in grantResults.indices){
if(grantResults[i] == PackageManager.PERMISSION_GRANTED){
println("${permissions[i]} granted")
// ваш код после получения разрешения
} else {
println("Пользователь отклонил запрос")
}
}
}
}
Разрешений может быть несколько, поэтому используется массив grantResults. Проверяем первое и единственное разрешение в этом примере и выясняем выбор пользователя. Если пользователь отклонит разрешение, то мы можем вывести сообщение, что не сможем сделать необходимую работу, например, определить его местоположение.
Рассмотрим вариант, когда запрашиваем сразу несколько разрешений.
private val REQUEST_PERMISSIONS = 101
private fun requestMultiplePermissions() {
val locationPermission: String = Manifest.permission.ACCESS_FINE_LOCATION
val calendarPermission: String = Manifest.permission.WRITE_CALENDAR
val hasLocPermission = ContextCompat.checkSelfPermission(this, locationPermission)
val hasCalPermission = ContextCompat.checkSelfPermission(this, calendarPermission)
val permissions: MutableList<String> = ArrayList()
if (hasLocPermission != PackageManager.PERMISSION_GRANTED) {
permissions.add(locationPermission)
}
if (hasCalPermission != PackageManager.PERMISSION_GRANTED) {
permissions.add(calendarPermission)
}
if (permissions.isNotEmpty()) {
ActivityCompat.requestPermissions(this, permissions.toTypedArray(),
REQUEST_PERMISSIONS)
} else {
println("У нас уже есть разрешения")
}
}
Мы запрашиваем два разрешения одновременно - определение местоположения и доступ к календарю. Добавляем их в списочный массив и выводим диалоговое окно. На этот раз в диалоговом окне пользователь увидит счётчик разрешений. Пользователю придётся последовательно ответить на вопросы несколько раз по отдельности.
Ниже представлены старые варианты диалоговых окон. Надеюсь, вы разберётесь в изменениях.

Соответственно, если пользователь даст согласие на одно разрешение и запретит другое, то в следующий раз будет запрашиваться доступ только к отклонённому ранее разрешению.
В манифесте можно указать разрешение, которое будет действительно только для устройств с Marshmallow:
<uses-permission-sdk-m android:name="android.permission.ACCESS_FINE_LOCATION"/>
В начале статьи я показал, как можно посмотреть на разрешения у конкретной программы. Можно также посмотреть наоборот, какие программы используют то или иное разрешение. Для этого идём снова в Настройки | Программы и щёлкаем на значке шестерёнки. Выбираем пункт Разрешения приложений. Вы увидите список разрешений и количество программ, которые используют данное разрешение. Вы можете зайти в нужное разрешение, чтобы увидеть детальную информацию о программах, которые используют разрешение и отключить/включить при желании.
Старые приложения, написанные до Android 6.0, будут работать в прежнем режиме с небольшими изменениями. Пользователь может отозвать ненужные на его взгляд разрешения после установки приложения. В этом случае будет выводится сообщение, что программа была разработана для старых устройств и может работать некорректно. Если упрямый пользователь тем не менее запретит доступ к каким-то разрешениям, то программа будет поставлять какие-то левые данные. Например, это могут быть нулевые значения для сенсоров. При работе с контактами будет выводиться сообщение, что нет контактов. Для определения местоположения будет выводиться сообщение, что определение недоступно и т.д. В этом случае программа продолжает свою работу без аварийного завершения, но доверять показаниям уже нет смысла.
В процессе отладки часто приходится включать/отключать разрешения. Заходить для этого каждый раз в настройки приложения не очень удобно. Можно подавать команды через adb:
adb shell pm grant <app package> <permission name>
adb shell pm revoke <app package> <permission name>
adb shell pm reset-permissions
adb shell pm list permission-groups
adb shell pm list permissions
Позже в Android появился новый способ запрашивать разрешения через RequestPermission.
Разрешение android.permission.RECORD_AUDIO
Пример с определением местоположения
Разрешение ACCESS_FINE_LOCATION для карты
Диктофон - делаем запись с микрофона