Permission Handler is een component van een Android-app die verantwoordelijk is voor het controleren, aanvragen en verwerken van resultaten van runtime-machtigingen. Volgens Android Developer Guide, 2024, centraliseert de machtigingshandler de logica van checkSelfPermission, requestPermissions en shouldShowRequestPermissionRationale in één klasse of ViewModel. Dit vereenvoudigt het onderhoud van de code en verbetert het testen.
Belangrijkste punten
Permission Handler — is een architectuurpatroon voor gecentraliseerd beheer van Android runtime-machtigingen. In plaats van verspreide aanroepen van ContextCompat.checkSelfPermission en ActivityCompat.requestPermissions door de hele app-code, wordt alle logica voor aanvragen en resultaatverwerking in één klasse geconcentreerd. Dit vermindert duplicatie, vereenvoudigt onderhoud en maakt de code voorspelbaarder.
De behoefte aan Permission Handler ontstond met de introductie van runtime-machtigingen in Android 6.0. Daarvoor werden alle machtigingen bij installatie aangevraagd en kon de app-code elke API zonder controles gebruiken. Na de overgang naar het runtime-model vereist elk gebruik van een gevaarlijke machtiging een driestapscontrole: checkSelfPermission, requestPermissions, onRequestPermissionsResult. Het verspreiden van deze logica over Activity en Fragment leidt tot inline-duplicatie en fouten. Volgens Google I/O 2019 vermindert centralisatie van machtigingsverwerking het aantal bugs gerelateerd aan Permission Denial met gemiddeld 60 procent.
Een goede Permission Handler biedt een schone interface voor de aanroepende code. Activity of Fragment hoeven de details van de aanvraag niet te kennen — ze roepen een methode aan zoals requestCamera(callback), en de handler beheert zelf de statuscontrole, weergave van rationale, aanroep van het systeemdialoog en het doorgeven van het resultaat aan de callback. Dit implementeert het principe van enkele verantwoordelijkheid en scheidt bedrijfslogica van de platformcode voor machtigingen.
Permission Handler wordt noodzakelijk wanneer de app 3 of meer gevaarlijke machtigingen gebruikt. Voor eenvoudige apps met één machtiging (bijvoorbeeld camera voor een QR-codescanner) kan men volstaan met een directe aanroep. Maar voor een typische mobiele app met camera, geolocatie, meldingen en opslag — is een gecentraliseerde handler verplicht voor onderhoud.
Een typische Permission Handler bestaat uit drie niveaus: interface-contract, implementatie met ActivityResultLauncher en een laag voor ViewModel. De interface definieert aanvraagmethoden voor elke machtiging — requestCamera, requestLocation, requestStorage. De implementatie koppelt deze methoden aan de bijbehorende ActivityResultContracts.RequestPermission-contracten.
Belangrijke componenten van de architectuur:
Deze architectuur maakt het eenvoudig om de implementatie in tests te vervangen: in plaats van een echte ActivityResultLauncher wordt een mock gebruikt die een vooraf gedefinieerd resultaat retourneert zonder interactie met het systeem. Dit is kritisch voor unittesten van UI-logica, waar het starten van een Activity voor een machtigingsdialoog onmogelijk is.
Permission Handler moet rekening houden met de levenscyclus van Activity en Fragment. Launchers worden geregistreerd in ActivityResultRegistry, dat automatisch de status opslaat en herstelt bij schermrotatie en het opnieuw aanmaken van de Activity. Handler mag geen directe verwijzingen naar Activity of Fragment bewaren — gebruik in plaats daarvan WeakReference of geef de registry door via de constructor. Dit voorkomt geheugenlekken en crashes bij configuratiewijzigingen.
De basisimplementatie van Permission Handler is gebouwd op ActivityResultContracts.RequestPermission. Handler krijgt ActivityResultRegistry van ComponentActivity of Fragment en registreert launchers voor elke machtiging. Elke launcher accepteert een lambda-callback die wordt aangeroepen na het antwoord van de gebruiker.
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
}
}
Handler wordt geïnitialiseerd in onCreate van Activity via registerForActivityResult, dat toegang geeft tot ActivityResultRegistry. Na initialisatie is de handler klaar om aanvragen te verwerken gedurende de volledige levenscyclus van de Activity. Het is belangrijk om initialize aan te roepen voor de eerste aanvraag, anders wordt de launcher niet geregistreerd.
Integratie van Permission Handler met ViewModel is de meest geavanceerde benadering. ViewModel beheert de status van aanvragen en Handler voert alleen platformaanroepen uit. ViewModel bevat StateFlow<PermissionUiState>, waarbij UiState beschrijft welke machtiging wordt aangevraagd en welk resultaat is verkregen. Activity abonneert zich op deze StateFlow en delegeert de aanvraag aan Handler.
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()
}
In dit model controleert Activity bij het opstarten isPermissionGranted via Handler en ViewModel beheert alleen de status. Als de machtiging niet is verleend — abonneert Activity zich op uiState, roept requestCamera van Handler aan en geeft het resultaat terug aan ViewModel via onPermissionResult. Het scheiden van platformcode en bedrijfslogica maakt het mogelijk ViewModel te testen zonder Android-afhankelijkheden.
Unittesten van Permission Handler is mogelijk dankzij de interface PermissionHandler. In tests wordt een FakePermissionHandler gemaakt die verschillende scenario's simuleert: machtiging verleend, geweigerd, Never Ask Again. Elk scenario wordt onafhankelijk getest. Dit is vooral belangrijk voor het testen van UI-logica die correct moet reageren op alle drie de uitkomsten.
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-implementatie maakt het mogelijk ViewModel te testen zonder emulator. Het volstaat om cameraResult op de gewenste waarde in te stellen en te controleren of ViewModel UiState correct bijwerkt. Integratietests controleren de echte PermissionHandler met ActivityScenario, maar meestal zijn er 2-3 van dergelijke tests voor de hele app — de overige scenario's worden gedekt door unittesten met fakes.
Typische fouten bij het werken met Permission Handler zijn onder andere: het ontbreken van een checkSelfPermission-controle voor elke API-aanroep, het negeren van shouldShowRequestPermissionRationale, het opnieuw aanroepen van requestPermissions bij Never Ask Again en het bewaren van launchers zonder rekening te houden met de levenscyclus van de Activity. Laten we elk probleem en de oplossing bekijken.
De meest voorkomende fout — het aanroepen van een API zonder de machtigingsstatus te controleren. Ontwikkelaars nemen aan dat als een machtiging eenmaal is verleend, deze voor altijd blijft. De gebruiker kan deze echter op elk moment intrekken via de instellingen. Permission Handler moet altijd isPermissionGranted aanroepen voor het uitvoeren van een gevoelige bewerking. De tweede veelvoorkomende fout — het negeren van shouldShowRequestPermissionRationale en een herhaalde aanvraag die leidt tot onmiddellijke weigering zonder dialoog bij Never Ask Again.
Best practices omvatten: het maken van één Handler-instantie voor de volledige levenscyclus van de Activity, het gebruik van SharedFlow voor het doorgeven van resultaten aan ViewModel, het loggen van alle aanvragen en weigeringen voor analyse, en het tonen van een aangepast rationale-dialoog voor het systeemdialoog bij de eerste weigering. Het volgen van deze regels garandeert stabiele werking met machtigingen op alle Android-versies.
Veelgestelde vragen
Permission Handler — een component voor gecentraliseerd beheer van runtime-machtigingen, die checkSelfPermission, requestPermissions en shouldShowRequestPermissionRationale inkapselt. Het vereenvoudigt codeonderhoud en verbetert testen.
ActivityResultContracts.RequestPermission uit de androidx.activity-bibliotheek wordt aanbevolen. Het vervangt de verouderde onRequestPermissionsResult en biedt een schone callback-API met een Boolean-resultaat.
Voor één machtiging is Handler niet verplicht — u kunt direct de RequestPermission-launcher in Activity aanroepen. Handler wordt noodzakelijk bij 3 of meer machtigingen om codeduplicatie te voorkomen.
Maak een PermissionHandler-interface en een fake-implementatie voor unittesten. Fake retourneert vooraf gedefinieerde resultaten zonder systeemaanroepen. Dit maakt het mogelijk ViewModel en UI-logica te testen zonder emulator.
Controleer na weigering shouldShowRequestPermissionRationale. Als de methode false retourneert — is de Never Ask Again-modus geactiveerd. Handler moet PermissionResult.DENIED(false) retourneren en de UI moet een knop tonen om naar Instellingen te gaan.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook