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.
  • Поједностављује јединично тестирање захваљујући инверзији зависности и изолацији платформског кода.
  • Најбоље праксе укључују јединствени handler на 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), а handler сам управља провером статуса, приказивањем rationale, позивањем системског дијалога и прослеђивањем резултата у callback. Ово имплементира принцип јединствене одговорности и одваја пословну логику од платформског кода дозвола.

Када је потребан Permission Handler

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

Архитектура 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 прима ламбда-повратни позив који се позива након одговора корисника.

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-ју. Након иницијализације, handler је спреман да обрађује захтеве током целог животног циклуса 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође