A Permission Handler az Android-alkalmazás egyik összetevője, amely a runtime engedélyek ellenőrzéséért, kéréséért és eredményeinek feldolgozásáért felelős. A Android Developer Guide, 2024 szerint az engedélykezelő egyetlen osztályban vagy ViewModel-ben centralizálja a checkSelfPermission, requestPermissions és shouldShowRequestPermissionRationale logikáját. Ez leegyszerűsíti a kód karbantartását és javítja a tesztelést.
Főbb pontok
Permission Handler — egy architekturális minta az Android runtime engedélyek központi kezelésére. Az alkalmazás kódjában szétszórt ContextCompat.checkSelfPermission és ActivityCompat.requestPermissions hívások helyett a kérések és eredmények feldolgozásának teljes logikája egyetlen osztályban összpontosul. Ez csökkenti a duplikációt, leegyszerűsíti a karbantartást és kiszámíthatóbbá teszi a kódot.
A Permission Handler szükségessége az Android 6.0-ban bevezetett runtime engedélyekkel merült fel. Ezelőtt minden engedélyt telepítéskor kértek, és az alkalmazás kódja minden API-t ellenőrzés nélkül használhatott. A runtime modellre való áttérés után minden veszélyes engedély használata háromlépéses ellenőrzést igényel: checkSelfPermission, requestPermissions, onRequestPermissionsResult. Ennek a logikának az Activity és Fragment közötti szétszórása inline duplikációhoz és hibákhoz vezet. A Google I/O 2019 adatai szerint az engedélyek feldolgozásának centralizálása átlagosan 60 százalékkal csökkenti a Permission Denial hibák számát.
Egy jó Permission Handler tiszta interfészt biztosít a hívó kód számára. Az Activity vagy Fragment nem ismerheti a kérés részleteit — meghív egy metódust, mint a requestCamera(callback), és a handler maga kezeli az állapotellenőrzést, az indoklás megjelenítését, a rendszerpárbeszédablak meghívását és az eredmény továbbítását a callback-hez. Ez megvalósítja az egységes felelősség elvét és elválasztja az üzleti logikát az engedélyek platformkódjától.
Permission Handler akkor válik szükségessé, amikor az alkalmazás 3 vagy több veszélyes engedélyt használ. Egyszerű alkalmazásoknál egyetlen engedéllyel (például kamera QR-kód olvasóhoz) meg lehet úszni közvetlen hívással. De egy tipikus mobilalkalmazásnál kamerával, helymeghatározással, értesítésekkel és tárhellyel — a központosított handler kötelező a karbantarthatósághoz.
Egy tipikus Permission Handler három rétegből áll: interfész-szerződés, implementáció ActivityResultLauncher-rel és réteg ViewModel számára. Az interfész meghatározza a kérési metódusokat minden engedélyhez — requestCamera, requestLocation, requestStorage. Az implementáció összekapcsolja ezeket a metódusokat a megfelelő ActivityResultContracts.RequestPermission szerződésekkel.
Az architektúra kulcsfontosságú összetevői:
Ez az architektúra lehetővé teszi az implementáció egyszerű cseréjét tesztekben: a valódi ActivityResultLauncher helyett egy mock-ot használunk, amely előre meghatározott eredményt ad vissza anélkül, hogy kapcsolatba lépne a rendszerrel. Ez kritikus fontosságú a UI logika egységteszteléséhez, ahol az Activity elindítása egy engedélypárbeszédablakhoz lehetetlen.
A Permission Handler-nek figyelembe kell vennie az Activity és Fragment életciklusát. A launcherek az ActivityResultRegistry-ben regisztrálódnak, amely automatikusan menti és visszaállítja az állapotot képernyőelforgatáskor és az Activity újbóli létrehozásakor. A Handler nem tárolhat közvetlen hivatkozásokat Activity-re vagy Fragment-re — ehelyett használjon WeakReference-t vagy adja át a registry-t a konstruktoron keresztül. Ez megakadályozza a memóriaszivárgást és az összeomlásokat konfigurációs változásoknál.
Az alap implementáció a Permission Handler az ActivityResultContracts.RequestPermission-re épül. A Handler megkapja az ActivityResultRegistry-t a ComponentActivity-től vagy Fragment-től, és regisztrálja a launchereket minden engedélyhez. Minden launcher elfogad egy lambda callback-et, amely a felhasználó válasza után hívódik meg.
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
}
}
A Handler inicializálása az Activity onCreate metódusában történik a registerForActivityResult segítségével, amely hozzáférést biztosít az ActivityResultRegistry-hez. Az inicializálás után a handler készen áll a kérések feldolgozására az Activity teljes életciklusa alatt. Fontos, hogy az initialize-t az első kérés előtt hívjuk meg, különben a launcher nem lesz regisztrálva.
Permission Handler integrációja ViewModel-lel a legfejlettebb megközelítés. A ViewModel kezeli a kérések állapotát, a Handler pedig csak platformhívásokat hajt végre. A ViewModel tartalmaz egy StateFlow<PermissionUiState> objektumot, ahol az UiState leírja, hogy melyik engedélyt kérik és milyen eredményt kaptak. Az Activity feliratkozik erre a StateFlow-ra és delegálja a kérést a Handler-nek.
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()
}
Ebben a modellben az Activity indításkor ellenőrzi az isPermissionGranted értéket a Handler-en keresztül, a ViewModel pedig csak az állapotot kezeli. Ha az engedély nincs megadva — az Activity feliratkozik az uiState-ra, meghívja a requestCamera-t a Handler-ből, és visszaküldi az eredményt a ViewModel-nek az onPermissionResult-on keresztül. A platformkód és az üzleti logika szétválasztása lehetővé teszi a ViewModel tesztelését Android függőségek nélkül.
Egységtesztelés a PermissionHandler interfésznek köszönhetően lehetséges. A tesztekben létrehozunk egy FakePermissionHandler-t, amely különböző forgatókönyveket szimulál: engedély megadva, elutasítva, Never Ask Again. Minden forgatókönyvet függetlenül tesztelünk. Ez különösen fontos a UI logika teszteléséhez, amelynek helyesen kell reagálnia mindhárom kimenetelre.
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
}
}
A Fake implementáció lehetővé teszi a ViewModel tesztelését emulátor nélkül. Elég beállítani a cameraResult értékét a kívánt értékre, és ellenőrizni, hogy a ViewModel helyesen frissíti-e az UiState-t. Az integrációs tesztek a valódi PermissionHandler-t ellenőrzik ActivityScenario-val, de általában 2-3 ilyen teszt van a teljes alkalmazásra — a többi forgatókönyvet egységtesztek fedik le fake-ekkel.
Gyakori hibák a Permission Handler használata során: a checkSelfPermission ellenőrzés hiánya minden API hívás előtt, a shouldShowRequestPermissionRationale figyelmen kívül hagyása, a requestPermissions újbóli meghívása Never Ask Again esetén, valamint a launcherek tárolása az Activity életciklusának figyelembevétele nélkül. Vizsgáljuk meg az egyes problémákat és megoldásukat.
A leggyakoribb hiba — API hívása az engedély állapotának ellenőrzése nélkül. A fejlesztők feltételezik, hogy ha az engedélyt egyszer megadták, az örökre megmarad. Azonban a felhasználó bármikor visszavonhatja a beállításokban. A Permission Handler-nek mindig meg kell hívnia az isPermissionGranted metódust egy érzékeny művelet végrehajtása előtt. A második gyakori hiba — a shouldShowRequestPermissionRationale figyelmen kívül hagyása és az ismételt kérés, amely Never Ask Again esetén azonnali elutasításhoz vezet párbeszédablak nélkül.
A legjobb gyakorlatok közé tartozik: egyetlen Handler példány létrehozása az Activity teljes életciklusára, a SharedFlow használata az eredmények ViewModel-be történő továbbításához, az összes kérés és elutasítás naplózása analitikai célokra, valamint egy egyéni indoklási párbeszédablak megjelenítése a rendszer párbeszédablaka előtt az első elutasításnál. Ezen szabályok betartása garantálja a stabil működést az engedélyekkel az Android minden verzióján.
Gyakran Ismételt Kérdések
Permission Handler — egy komponens a runtime engedélyek központi kezelésére, amely beágyazza a checkSelfPermission, requestPermissions és shouldShowRequestPermissionRationale metódusokat. Leegyszerűsíti a kód karbantartását és javítja a tesztelést.
Az ActivityResultContracts.RequestPermission ajánlott az androidx.activity könyvtárból. Ez helyettesíti az elavult onRequestPermissionsResult-ot, és tiszta callback API-t biztosít Boolean eredménnyel.
Egyetlen engedélyhez Handler nem kötelező — használhatja a közvetlen RequestPermission launcher hívást Activity-ben. A Handler 3 vagy több engedély esetén válik szükségessé a kódismétlődés elkerülése érdekében.
Hozzon létre egy PermissionHandler interfészt és annak fake implementációját az egységtesztekhez. A Fake előre meghatározott eredményeket ad vissza rendszerhívások nélkül. Ez lehetővé teszi a ViewModel és UI logika tesztelését emulátor nélkül.
Az elutasítás után ellenőrizze a shouldShowRequestPermissionRationale értékét. Ha a metódus false-t adott vissza — a Never Ask Again mód aktív. A Handler-nek PermissionResult.DENIED(false) értéket kell visszaadnia, a UI-nak pedig egy gombot kell mutatnia a Beállításokhoz való navigáláshoz.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is