Permission Handler w Android: jak działa, obsługa żądań i implementacja

Autor: IT Sectr Opublikowano: 2026-05-20 Czas czytania: 8 min

Permission Handler to komponent aplikacji Android odpowiedzialny za sprawdzanie, żądanie i obsługę wyników uprawnień runtime. Według Android Developer Guide, 2024, procedura obsługi uprawnień centralizuje logikę checkSelfPermission, requestPermissions i shouldShowRequestPermissionRationale w jednej klasie lub ViewModel. Upraszcza to utrzymanie kodu i poprawia testowanie.

Najważniejsze

  • Permission Handler — wyspecjalizowany komponent do scentralizowanego zarządzania uprawnieniami runtime w Android.
  • Hermetyzuje logikę checkSelfPermission, requestPermissions i shouldShowRequestPermissionRationale.
  • Nowoczesne implementacje bazują na ActivityResultContracts z androidx.activity.
  • Upraszcza testy jednostkowe dzięki odwracaniu zależności i izolacji kodu platformy.
  • Najlepsze praktyki obejmują jeden handler na Activity i ponowne wykorzystanie przez kontener DI.

Czym jest Permission Handler w Android

Permission Handler — to wzorzec architektoniczny do scentralizowanego zarządzania uprawnieniami runtime w Android. Zamiast rozproszonych wywołań ContextCompat.checkSelfPermission i ActivityCompat.requestPermissions w całym kodzie aplikacji, cała logika żądań i obsługi wyników koncentruje się w jednej klasie. Zmniejsza to powielanie, upraszcza utrzymanie i czyni kod bardziej przewidywalnym.

Potrzeba Permission Handler powstała wraz z wprowadzeniem uprawnień runtime w Android 6.0. Wcześniej wszystkie uprawnienia były żądane podczas instalacji, a kod aplikacji mógł korzystać z dowolnych API bez sprawdzania. Po przejściu na model runtime każde użycie niebezpiecznego uprawnienia wymaga trzystopniowej weryfikacji: checkSelfPermission, requestPermissions, onRequestPermissionsResult. Rozproszenie tej logiki pomiędzy Activity i Fragment prowadzi do powielania inline i błędów. Według danych Google I/O 2019, centralizacja obsługi uprawnień zmniejsza liczbę błędów związanych z Permission Denial średnio o 60 procent.

Dobry Permission Handler zapewnia czysty interfejs dla wywołującego kodu. Activity lub Fragment nie muszą znać szczegółów żądania — wywołują metodę typu requestCamera(callback), a handler sam zarządza sprawdzaniem statusu, wyświetlaniem rationale, wywołaniem systemowego dialogu i przekazaniem wyniku do callback. Realizuje to zasadę pojedynczej odpowiedzialności i oddziela logikę biznesową od kodu platformy uprawnień.

Kiedy potrzebny jest Permission Handler

Permission Handler staje się niezbędny, gdy aplikacja używa 3 lub więcej niebezpiecznych uprawnień. W przypadku prostych aplikacji z jednym uprawnieniem (np. kamera do skanera kodów QR) można obejść się bezpośrednim wywołaniem. Jednak w typowej aplikacji mobilnej z kamerą, geolokalizacją, powiadomieniami i pamięcią masową — scentralizowany handler jest obowiązkowy dla utrzymania kodu.

Architektura Permission Handler

Typowy Permission Handler składa się z trzech poziomów: interfejs-kontrakt, implementacja z ActivityResultLauncher i warstwa dla ViewModel. Interfejs określa metody żądań dla każdego uprawnienia — requestCamera, requestLocation, requestStorage. Implementacja łączy te metody z odpowiednimi kontraktami ActivityResultContracts.RequestPermission.

Kluczowe komponenty architektury:

  • PermissionHandlerContract — interfejs z metodami dla każdego uprawnienia
  • PermissionHandlerImpl — implementacja komunikująca się z ActivityResultRegistry
  • PermissionResult — sealed class ze stanami GRANTED, DENIED, NEVER_ASK_AGAIN
  • RationaleHandler — komponent do wyświetlania wyjaśnień przed żądaniem

Taka architektura pozwala łatwo podmieniać implementację w testach: zamiast rzeczywistego ActivityResultLauncher używany jest mock, który zwraca predefiniowany wynik bez interakcji z systemem. Jest to krytyczne dla testów jednostkowych logiki UI, gdzie uruchomienie Activity dla dialogu uprawnień jest niemożliwe.

Zarządzanie cyklem życia

Permission Handler musi uwzględniać cykl życia Activity i Fragment. Launchery są rejestrowane w ActivityResultRegistry, który automatycznie zapisuje i przywraca stan przy obrocie ekranu i odtworzeniu Activity. Handler nie powinien przechowywać bezpośrednich referencji do Activity lub Fragment — zamiast tego używaj WeakReference lub przekazuj registry przez konstruktor. Zapobiega to wyciekom pamięci i crashom przy zmianach konfiguracji.

Implementacja Permission Handler w Kotlin

Podstawowa implementacja Permission Handler opiera się na ActivityResultContracts.RequestPermission. Handler otrzymuje ActivityResultRegistry z ComponentActivity lub Fragment i rejestruje launchery dla każdego uprawnienia. Każdy launcher przyjmuje lambdę-callback, która jest wywoływana po odpowiedzi użytkownika.

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
    }
}

Inicjalizacja w Activity

Handler jest inicjalizowany w onCreate Activity przez registerForActivityResult, który zapewnia dostęp do ActivityResultRegistry. Po inicjalizacji handler jest gotowy do obsługi żądań przez cały cykl życia Activity. Ważne jest, aby wywołać initialize przed pierwszym żądaniem, w przeciwnym razie launcher nie zostanie zarejestrowany.

Permission Handler z ViewModel

Integracja Permission Handler z ViewModel to najbardziej zaawansowane podejście. ViewModel zarządza stanem żądań, a Handler wykonuje tylko wywołania platformowe. ViewModel zawiera StateFlow<PermissionUiState>, gdzie UiState opisuje, które uprawnienie jest żądane i jaki wynik uzyskano. Activity subskrybuje ten StateFlow i deleguje żądanie do 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()
}

W tym modelu Activity sprawdza isPermissionGranted przez Handler przy starcie, a ViewModel zarządza tylko stanem. Jeśli uprawnienie nie zostało przyznane — Activity subskrybuje uiState, wywołuje requestCamera z Handler i przekazuje wynik z powrotem do ViewModel przez onPermissionResult. Rozdzielenie kodu platformy i logiki biznesowej pozwala testować ViewModel bez zależności Androida.

Testowanie Permission Handler

Testy jednostkowe Permission Handler są możliwe dzięki interfejsowi PermissionHandler. W testach tworzony jest FakePermissionHandler, który imituje różne scenariusze: uprawnienie przyznane, odrzucone, Never Ask Again. Każdy scenariusz jest testowany niezależnie. Jest to szczególnie ważne dla testowania logiki UI, która musi poprawnie reagować na wszystkie trzy wyniki.

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
    }
}

Implementacja Fake pozwala testować ViewModel bez emulatora. Wystarczy ustawić cameraResult na odpowiednią wartość i sprawdzić, czy ViewModel poprawnie aktualizuje UiState. Testy integracyjne sprawdzają rzeczywisty PermissionHandler z ActivityScenario, ale zwykle takich testów jest 2-3 na całą aplikację — pozostałe scenariusze są pokryte testami jednostkowymi z fake'ami.

Typowe wzorce i błędy

Typowe błędy podczas pracy z Permission Handler obejmują: brak sprawdzenia checkSelfPermission przed każdym wywołaniem API, ignorowanie shouldShowRequestPermissionRationale, ponowne wywołanie requestPermissions przy Never Ask Again oraz przechowywanie launcherów bez uwzględnienia cyklu życia Activity. Przyjrzyjmy się każdemu problemowi i rozwiązaniu.

Najczęstszym błędem jest wywołanie API bez sprawdzenia statusu uprawnienia. Deweloperzy zakładają, że jeśli uprawnienie zostało raz przyznane, pozostanie na zawsze. Jednak użytkownik może je cofnąć w ustawieniach w dowolnym momencie. Permission Handler powinien zawsze wywoływać isPermissionGranted przed wykonaniem wrażliwej operacji. Drugim popularnym błędem jest ignorowanie shouldShowRequestPermissionRationale i ponowne żądanie, które prowadzi do natychmiastowej odmowy bez dialogu przy Never Ask Again.

Najlepsze praktyki obejmują: utworzenie jednej instancji Handler na cały cykl życia Activity, używanie SharedFlow do przekazywania wyników do ViewModel, logowanie wszystkich żądań i odmów dla analityki, a także wyświetlanie niestandardowego dialogu rationale przed systemowym przy pierwszej odmowie. Przestrzeganie tych zasad gwarantuje stabilną pracę z uprawnieniami na wszystkich wersjach Androida.

Często zadawane pytania

Czym jest Permission Handler w Android?

Permission Handler — to komponent do scentralizowanego zarządzania uprawnieniami runtime, hermetyzujący checkSelfPermission, requestPermissions i shouldShowRequestPermissionRationale. Upraszcza utrzymanie kodu i poprawia testowanie.

Jakiego API użyć dla Handler w 2024?

Zalecany jest ActivityResultContracts.RequestPermission z biblioteki androidx.activity. Zastępuje on przestarzały onRequestPermissionsResult i zapewnia czyste callback API z wynikiem Boolean.

Czy Handler jest potrzebny dla jednego uprawnienia?

Dla jednego uprawnienia Handler nie jest obowiązkowy — można użyć bezpośredniego wywołania RequestPermission lauchera w Activity. Handler staje się niezbędny przy 3 i więcej uprawnieniach, aby uniknąć powielania kodu.

Jak testować Permission Handler?

Utwórz interfejs PermissionHandler i jego fake-implementację do testów jednostkowych. Fake zwraca predefiniowane wyniki bez wywołań systemowych. Pozwala to testować ViewModel i logikę UI bez emulatora.

Jak obsłużyć Never Ask Again w Handler?

Po odmowie sprawdź shouldShowRequestPermissionRationale. Jeśli metoda zwróciła false — aktywowano tryb Never Ask Again. Handler powinien zwrócić PermissionResult.DENIED(false), a UI — wyświetlić przycisk przejścia do Ustawień.

Podsumowanie

  • Permission Handler — komponent architektoniczny do scentralizowanego zarządzania uprawnieniami runtime w Android.
  • Bazuje na ActivityResultContracts.RequestPermission z biblioteki androidx.activity.
  • Interfejs z metodami dla każdego uprawnienia upraszcza testy jednostkowe przez fake-implementacje.
  • Integracja z ViewModel przez StateFlow oddziela kod platformy od logiki biznesowej.
  • Typowe błędy: brak checkSelfPermission, ignorowanie rationale i Never Ask Again.
  • Najlepsza praktyka — jeden Handler na Activity z rejestracją w onCreate.
  • Centralizacja zmniejsza liczbę błędów Permission Denial o 60 procent w typowych projektach.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również