Permission Handler est un composant d'application Android responsable de la vérification, de la demande et du traitement des résultats des autorisations d'exécution. Selon le Android Developer Guide, 2024, un gestionnaire d'autorisations centralise la logique de checkSelfPermission, requestPermissions et shouldShowRequestPermissionRationale dans une seule classe ou ViewModel. Cela simplifie la maintenance du code et améliore les tests.
Points clés
Permission Handler est un modèle architectural pour la gestion centralisée des autorisations d'exécution Android. Au lieu d'appels dispersés de ContextCompat.checkSelfPermission et ActivityCompat.requestPermissions dans tout le code de l'application, toute la logique de demandes et de traitement des résultats est concentrée dans une seule classe. Cela réduit les duplications, simplifie la maintenance et rend le code plus prévisible.
Le besoin de Permission Handler est apparu avec l'introduction des autorisations d'exécution dans Android 6.0. Avant cela, toutes les autorisations étaient demandées lors de l'installation, et le code de l'application pouvait utiliser n'importe quelle API sans vérification. Après le passage au modèle d'exécution, chaque utilisation d'une autorisation dangereuse nécessite une vérification en trois étapes : checkSelfPermission, requestPermissions, onRequestPermissionsResult. Répartir cette logique entre Activity et Fragment entraîne des duplications en ligne et des erreurs. Selon Google I/O 2019, la centralisation du traitement des autorisations réduit le nombre de bogues liés à Permission Denial d'en moyenne 60 pour cent.
Un bon Permission Handler fournit une interface propre pour le code appelant. L'Activity ou le Fragment ne doit pas connaître les détails de la demande — ils appellent une méthode comme requestCamera(callback), et le handler lui-même gère la vérification du statut, l'affichage de la justification, l'invocation de la boîte de dialogue système et la transmission du résultat au callback. Cela implémente le principe de responsabilité unique et sépare la logique métier du code d'autorisation de la plateforme.
Un Permission Handler devient nécessaire lorsqu'une application utilise 3 autorisations dangereuses ou plus. Pour les applications simples avec une seule autorisation (par exemple, caméra pour un scanner de codes QR), un appel direct peut suffire. Mais pour une application mobile typique avec caméra, géolocalisation, notifications et stockage — un handler centralisé est essentiel pour la maintenabilité.
Un Permission Handler typique se compose de trois couches : un contrat d'interface, une implémentation avec ActivityResultLauncher et une couche ViewModel. L'interface définit les méthodes de demande pour chaque autorisation — requestCamera, requestLocation, requestStorage. L'implémentation lie ces méthodes aux contrats ActivityResultContracts.RequestPermission correspondants.
Composants clés de l'architecture :
Cette architecture permet d'échanger facilement les implémentations dans les tests : au lieu d'un ActivityResultLauncher réel, on utilise un mock qui renvoie un résultat prédéfini sans interaction système. Ceci est crucial pour les tests unitaires de la logique d'interface utilisateur, où il est impossible de lancer une Activity pour une boîte de dialogue d'autorisation.
Le Permission Handler doit prendre en compte le cycle de vie de l'Activity et du Fragment. Les lanceurs sont enregistrés dans le ActivityResultRegistry, qui sauvegarde et restaure automatiquement l'état lors de la rotation de l'écran et de la recréation de l'Activity. Le handler ne doit pas stocker de références directes à l'Activity ou au Fragment — utilisez plutôt WeakReference ou passez le registry via le constructeur. Cela évite les fuites mémoire et les crashs lors des changements de configuration.
Une implémentation de base de Permission Handler est construite sur ActivityResultContracts.RequestPermission. Le handler reçoit le ActivityResultRegistry de ComponentActivity ou Fragment et enregistre des lanceurs pour chaque autorisation. Chaque lanceur accepte un lambda de callback qui est invoqué après la réponse de l'utilisateur.
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
}
}
Le handler est initialisé dans le onCreate de l'Activity via registerForActivityResult, qui donne accès au ActivityResultRegistry. Après l'initialisation, le handler est prêt à traiter les demandes tout au long du cycle de vie de l'Activity. Il est important d'appeler initialize avant la première demande, sinon le lanceur ne sera pas enregistré.
Intégrer un Permission Handler avec ViewModel est l'approche la plus avancée. Le ViewModel gère l'état des demandes, tandis que le Handler effectue uniquement les appels plateforme. Le ViewModel contient StateFlow<PermissionUiState>, où UiState décrit quelle autorisation est demandée et quel résultat a été reçu. L'Activity s'abonne à ce StateFlow et délègue la demande au 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()
}
Dans ce modèle, l'Activity vérifie isPermissionGranted via le Handler au démarrage, tandis que le ViewModel gère uniquement l'état. Si l'autorisation n'est pas accordée — l'Activity s'abonne à uiState, appelle requestCamera du Handler et transmet le résultat au ViewModel via onPermissionResult. La séparation du code plateforme de la logique métier permet de tester le ViewModel sans dépendances Android.
Les tests unitaires de Permission Handler sont possibles grâce à l'interface PermissionHandler. Dans les tests, un FakePermissionHandler est créé pour simuler différents scénarios : autorisation accordée, refusée, Never Ask Again. Chaque scénario est testé indépendamment. Ceci est particulièrement important pour tester la logique d'interface utilisateur qui doit réagir correctement aux trois résultats.
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
}
}
L'implémentation factice permet de tester le ViewModel sans émulateur. Il suffit de définir cameraResult sur la valeur souhaitée et de vérifier que le ViewModel met à jour correctement son UiState. Les tests d'intégration vérifient le PermissionHandler réel avec ActivityScenario, mais il n'y a généralement que 2-3 de ces tests par application — les scénarios restants sont couverts par des tests unitaires avec des fakes.
Les erreurs typiques lors de l'utilisation de Permission Handler incluent : ne pas vérifier checkSelfPermission avant chaque appel API, ignorer shouldShowRequestPermissionRationale, rappeler requestPermissions après Never Ask Again et stocker des lanceurs sans tenir compte du cycle de vie de l'Activity. Examinons chaque problème et sa solution.
L'erreur la plus courante est d'appeler une API sans vérifier l'état de l'autorisation. Les développeurs supposent que si une autorisation a été accordée une fois, elle le restera pour toujours. Cependant, l'utilisateur peut la révoquer à tout moment via les paramètres. Un Permission Handler doit toujours appeler isPermissionGranted avant d'effectuer une opération sensible. La deuxième erreur fréquente est d'ignorer shouldShowRequestPermissionRationale et de répéter la demande, ce qui entraîne un refus instantané sans dialogue en mode Never Ask Again.
Les meilleures pratiques incluent : créer une seule instance de Handler pour tout le cycle de vie de l'Activity, utiliser SharedFlow pour transmettre les résultats au ViewModel, journaliser toutes les demandes et refus pour analyse, et afficher une boîte de dialogue de justification personnalisée avant celle du système lors du premier refus. Suivre ces règles garantit une gestion stable des autorisations sur toutes les versions d'Android.
Foire aux questions
Permission Handler est un composant pour la gestion centralisée des autorisations d'exécution, encapsulant checkSelfPermission, requestPermissions et shouldShowRequestPermissionRationale. Il simplifie la maintenance du code et améliore les tests.
Il est recommandé d'utiliser ActivityResultContracts.RequestPermission de la bibliothèque androidx.activity. Il remplace l'ancien onRequestPermissionsResult et fournit une API de callback propre avec un résultat Boolean.
Pour une seule autorisation, un Handler n'est pas obligatoire — vous pouvez utiliser un appel direct au lanceur RequestPermission dans l'Activity. Un Handler devient nécessaire avec 3 autorisations ou plus pour éviter la duplication de code.
Créez une interface PermissionHandler et son implémentation factice pour les tests unitaires. La factice renvoie des résultats prédéfinis sans appels système. Cela permet de tester le ViewModel et la logique d'interface utilisateur sans émulateur.
Après le refus, vérifiez shouldShowRequestPermissionRationale. Si la méthode renvoie false — le mode Never Ask Again est actif. Le Handler doit renvoyer PermissionResult.DENIED(false), et l'interface utilisateur doit afficher un bouton pour accéder aux Paramètres.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi