Permission Handler в Android: как работи, обработка на заявки и реализация

Автор: IT Sectr Публикувано: 2026-05-20 Време за четене: 8 мин

Permission Handler е компонент на Android приложение, отговорен за проверка, заявка и обработка на резултати от runtime разрешения. Според Android Developer Guide, 2024, манипулаторът на разрешения централизира логиката на checkSelfPermission, requestPermissions и shouldShowRequestPermissionRationale в един клас или ViewModel. Това опростява поддръжката на кода и подобрява тестването.

Основни точки

  • Permission Handler — специализиран компонент за централизирано управление на runtime разрешения в Android.
  • Капсулира логиката на checkSelfPermission, requestPermissions и shouldShowRequestPermissionRationale.
  • Съвременните реализации се базират на ActivityResultContracts от androidx.activity.
  • Опростява юнит тестването благодарение на инверсия на зависимостите и изолация на платформен код.
  • Най-добрите практики включват един манипулатор на Activity и повторно използване чрез DI контейнер.

Какво е Permission Handler в Android

Permission Handler — е архитектурен шаблон за централизирано управление на runtime разрешения в Android. Вместо разпръснати извиквания на ContextCompat.checkSelfPermission и ActivityCompat.requestPermissions из целия код на приложението, цялата логика на заявки и обработка на резултати се концентрира в един клас. Това намалява дублирането, опростява поддръжката и прави кода по-предвидим.

Необходимостта от Permission Handler възникна с въвеждането на runtime разрешения в Android 6.0. Преди това всички разрешения се изискваха при инсталиране и кодът на приложението можеше да използва всяко API без проверки. След прехода към runtime модел, всяко използване на опасно разрешение изисква тристъпкова проверка: checkSelfPermission, requestPermissions, onRequestPermissionsResult. Разпръскването на тази логика между Activity и Fragment води до вградено дублиране и грешки. Според данни от Google I/O 2019, централизацията на обработката на разрешения намалява броя на грешките, свързани с Permission Denial, средно с 60 процента.

Добрият Permission Handler предоставя чист интерфейс за извикващия код. Activity или Fragment не трябва да знаят детайлите на заявката — те извикват метод като requestCamera(callback), а манипулаторът сам управлява проверката на статуса, показването на rationale, извикването на системния диалог и предаването на резултата на callback. Това реализира принципа на единична отговорност и отделя бизнес логиката от платформения код на разрешенията.

Кога е необходим Permission Handler

Permission Handler става необходим, когато приложението използва 3 или повече опасни разрешения. За прости приложения с едно разрешение (например камера за скенер на QR кодове) може да се използва директно извикване. Но за типично мобилно приложение с камера, геолокация, известия и хранилище — централизиран манипулатор е задължителен за поддръжка.

Архитектура на Permission Handler

Типичен Permission Handler се състои от три нива: интерфейс-договор, реализация с ActivityResultLauncher и слой за ViewModel. Интерфейсът дефинира методи за заявка за всяко разрешение — requestCamera, requestLocation, requestStorage. Реализацията свързва тези методи със съответните договори ActivityResultContracts.RequestPermission.

Ключови компоненти на архитектурата:

  • PermissionHandlerContract — интерфейс с методи за всяко разрешение
  • PermissionHandlerImpl — реализация, свързваща се с ActivityResultRegistry
  • PermissionResult — sealed class със състояния GRANTED, DENIED, NEVER_ASK_AGAIN
  • RationaleHandler — компонент за показване на обяснения преди заявка

Тази архитектура позволява лесна замяна на реализацията в тестове: вместо реален ActivityResultLauncher се използва mock, който връща предварително определен резултат без взаимодействие със системата. Това е критично за юнит тестване на UI логика, където стартирането на Activity за диалог на разрешения е невъзможно.

Управление на жизнения цикъл

Permission Handler трябва да взема предвид жизнения цикъл на Activity и Fragment. Launcher-ите се регистрират в ActivityResultRegistry, който автоматично запазва и възстановява състоянието при завъртане на екрана и повторно създаване на Activity. Handler не трябва да съхранява директни референции към Activity или Fragment — вместо това използвайте WeakReference или предайте registry чрез конструктора. Това предотвратява изтичане на памет и сривове при промени в конфигурацията.

Реализация на Permission Handler в Kotlin

Основната реализация на Permission Handler се гради на ActivityResultContracts.RequestPermission. Handler получава ActivityResultRegistry от ComponentActivity или Fragment и регистрира launcher-и за всяко разрешение. Всеки launcher приема ламбда-callback, който се извиква след отговора на потребителя.

kotlin
sealed class PermissionResult {
    object GRANTED : PermissionResult()
    data class DENIED(
        val shouldShowRationale: Boolean
    ) : PermissionResult()
}

interface PermissionHandler {
    fun requestCamera(
        callback: (PermissionResult) -> Unit
    )
    fun requestLocation(
        callback: (PermissionResult) -> Unit
    )
    fun isPermissionGranted(
        permission: String
    ): Boolean
}

class AndroidPermissionHandler(
    private val registry: ActivityResultRegistry,
    private val context: Context
) : PermissionHandler {

    private var cameraLauncher: ActivityResultLauncher<String>? = null

    fun initialize() {
        cameraLauncher = registry.register(
            "camera_permission",
            ActivityResultContracts.RequestPermission()
        ) { isGranted ->
            if (isGranted) {
                pendingCameraCallback?.invoke(
                    PermissionResult.GRANTED
                )
            } else {
                val rationale = ActivityCompat.shouldShowRequestPermissionRationale(
                    context as Activity,
                    Manifest.permission.CAMERA
                )
                pendingCameraCallback?.invoke(
                    PermissionResult.DENIED(rationale)
                )
            }
        }
    }

    private var pendingCameraCallback:
        ((PermissionResult) -> Unit)? = null

    override fun requestCamera(
        callback: (PermissionResult) -> Unit
    ) {
        if (isPermissionGranted(
                Manifest.permission.CAMERA
        )) {
            callback.invoke(PermissionResult.GRANTED)
            return
        }
        pendingCameraCallback = callback
        cameraLauncher?.launch(
            Manifest.permission.CAMERA
        )
    }

    override fun isPermissionGranted(
        permission: String
    ): Boolean {
        return ContextCompat.checkSelfPermission(
            context, permission
        ) == PackageManager.PERMISSION_GRANTED
    }
}

Инициализация в Activity

Handler се инициализира в onCreate на Activity чрез registerForActivityResult, който предоставя достъп до ActivityResultRegistry. След инициализация манипулаторът е готов да обработва заявки през целия жизнен цикъл на Activity. Важно е да извикате initialize преди първата заявка, в противен случай launcher-ът няма да бъде регистриран.

Permission Handler с ViewModel

Интеграция на Permission Handler с ViewModel е най-напредналият подход. ViewModel управлява състоянието на заявките, а Handler изпълнява само платформени извиквания. ViewModel съдържа StateFlow<PermissionUiState>, където UiState описва кое разрешение се заявява и какъв резултат е получен. Activity се абонира за този StateFlow и делегира заявката на Handler.

kotlin
class PermissionsViewModel : ViewModel() {

    private val _uiState =
        MutableStateFlow<PermissionUiState>(
            PermissionUiState.Idle
        )
    val uiState: StateFlow<PermissionUiState> = _uiState.asStateFlow()

    fun onCameraRequested() {
        _uiState.value = PermissionUiState.RequestingCamera
    }

    fun onPermissionResult(
        permission: String,
        result: PermissionResult
    ) {
        when (result) {
            PermissionResult.GRANTED -> {
                _uiState.value = PermissionUiState.Granted(permission)
            }
            is PermissionResult.DENIED -> {
                _uiState.value = PermissionUiState.Denied(
                    permission,
                    result.shouldShowRationale
                )
            }
        }
    }
}

sealed class PermissionUiState {
    object Idle : PermissionUiState()
    object RequestingCamera : PermissionUiState()
    data class Granted(val permission: String) : PermissionUiState()
    data class Denied(
        val permission: String,
        val shouldShowRationale: Boolean
    ) : PermissionUiState()
}

В този модел Activity проверява isPermissionGranted чрез Handler при стартиране, а ViewModel управлява само състоянието. Ако разрешението не е предоставено — Activity се абонира за uiState, извиква requestCamera от Handler и връща резултата обратно на ViewModel чрез onPermissionResult. Разделянето на платформен код и бизнес логика позволява тестване на ViewModel без Android зависимости.

Тестване на Permission Handler

Юнит тестване на Permission Handler е възможно благодарение на интерфейса PermissionHandler. В тестовете се създава FakePermissionHandler, който симулира различни сценарии: разрешение предоставено, отказано, Never Ask Again. Всеки сценарий се тества независимо. Това е особено важно за тестване на UI логика, която трябва правилно да реагира на всичките три резултата.

kotlin
class FakePermissionHandler : PermissionHandler {

    var cameraResult: PermissionResult =
        PermissionResult.GRANTED
    var grantedPermissions: Set<String> =
        setOf(Manifest.permission.CAMERA)

    override fun requestCamera(
        callback: (PermissionResult) -> Unit
    ) {
        callback.invoke(cameraResult)
    }

    override fun isPermissionGranted(
        permission: String
    ): Boolean {
        return permission in grantedPermissions
    }
}

Fake реализацията позволява тестване на ViewModel без емулатор. Достатъчно е да зададете cameraResult на желаната стойност и да проверите дали ViewModel правилно актуализира UiState. Интеграционните тестове проверяват реалния PermissionHandler с ActivityScenario, но обикновено има 2-3 такива теста за цялото приложение — останалите сценарии са покрити от юнит тестове с fake реализации.

Чести модели и грешки

Чести грешки при работа с Permission Handler включват: липса на проверка на checkSelfPermission преди всяко API извикване, игнориране на shouldShowRequestPermissionRationale, повторно извикване на requestPermissions при Never Ask Again и съхраняване на launcher-и без отчитане на жизнения цикъл на Activity. Нека разгледаме всеки проблем и неговото решение.

Най-честата грешка — извикване на API без проверка на статуса на разрешението. Разработчиците предполагат, че ако разрешение е било предоставено веднъж, то ще остане завинаги. Въпреки това, потребителят може да го оттегли по всяко време чрез настройките. Permission Handler трябва винаги да извиква isPermissionGranted преди изпълнение на чувствителна операция. Втората честа грешка — игнориране на shouldShowRequestPermissionRationale и повторна заявка, която води до незабавен отказ без диалог при Never Ask Again.

Най-добрите практики включват: създаване на един екземпляр на Handler за целия жизнен цикъл на Activity, използване на SharedFlow за предаване на резултати към ViewModel, логване на всички заявки и откази за аналитика, както и показване на персонализиран rationale диалог преди системния при първия отказ. Спазването на тези правила гарантира стабилна работа с разрешения на всички версии на Android.

Често задавани въпроси

Какво е Permission Handler в Android?

Permission Handler — компонент за централизирано управление на runtime разрешения, капсулиращ checkSelfPermission, requestPermissions и shouldShowRequestPermissionRationale. Опростява поддръжката на кода и подобрява тестването.

Кое API да използвам за Handler през 2024?

Препоръчва се ActivityResultContracts.RequestPermission от библиотеката androidx.activity. Той заменя остарелия onRequestPermissionsResult и предоставя чист callback API с Boolean резултат.

Нужен ли е Handler за едно разрешение?

За едно разрешение Handler не е задължителен — можете да използвате директно извикване на RequestPermission launcher в Activity. Handler става необходим при 3 или повече разрешения, за да се избегне дублиране на код.

Как да тествам Permission Handler?

Създайте интерфейс PermissionHandler и негова fake реализация за юнит тестове. Fake връща предварително определени резултати без системни извиквания. Това позволява тестване на ViewModel и UI логика без емулатор.

Как да обработя Never Ask Again в Handler?

След отказ проверете shouldShowRequestPermissionRationale. Ако методът е върнал false — режимът Never Ask Again е активиран. Handler трябва да върне PermissionResult.DENIED(false), а UI да покаже бутон за преход към Настройки.

Обобщение

  • Permission Handler — архитектурен компонент за централизирано управление на runtime разрешения в Android.
  • Базира се на ActivityResultContracts.RequestPermission от библиотеката androidx.activity.
  • Интерфейс с методи за всяко разрешение опростява юнит тестването чрез fake реализации.
  • Интеграция с ViewModel чрез StateFlow разделя платформения код и бизнес логиката.
  • Чести грешки: липса на checkSelfPermission, игнориране на rationale и Never Ask Again.
  • Най-добра практика — един Handler на Activity с регистрация в onCreate.
  • Централизацията намалява броя на Permission Denial грешки с 60 процента в типични проекти.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също