Permission Handler, çalışma zamanı izinlerini kontrol etmekten, istemekten ve sonuçlarını işlemekten sorumlu bir Android uygulama bileşenidir. Android Developer Guide, 2024'e göre, bir izin işleyicisi checkSelfPermission, requestPermissions ve shouldShowRequestPermissionRationale mantığını tek bir sınıf veya ViewModel'de merkezileştirir. Bu, kod bakımını basitleştirir ve test etmeyi iyileştirir.
Ana Noktalar
Permission Handler, Android çalışma zamanı izinlerinin merkezi yönetimi için bir mimari desendir. Uygulama kodu boyunca dağınık ContextCompat.checkSelfPermission ve ActivityCompat.requestPermissions çağrıları yerine, tüm istek ve sonuç işleme mantığı tek bir sınıfta toplanır. Bu, tekrarı azaltır, bakımı basitleştirir ve kodu daha öngörülebilir hale getirir.
Permission Handler ihtiyacı, Android 6.0'da çalışma zamanı izinlerinin getirilmesiyle ortaya çıktı. Bundan önce, tüm izinler yükleme sırasında istenirdi ve uygulama kodu herhangi bir API'yi kontrol olmadan kullanabilirdi. Çalışma zamanı modeline geçtikten sonra, tehlikeli bir iznin her kullanımı üç adımlı bir kontrol gerektirir: checkSelfPermission, requestPermissions, onRequestPermissionsResult. Bu mantığı Activity ve Fragment arasında yaymak, satır içi tekrara ve hatalara yol açar. Google I/O 2019'a göre, izin işlemeyi merkezileştirmek, Permission Denial ile ilgili hataların sayısını ortalama yüzde 60 oranında azaltır.
İyi bir Permission Handler, çağıran kod için temiz bir arayüz sağlar. Activity veya Fragment, isteğin ayrıntılarını bilmek zorunda değildir — requestCamera(callback) gibi bir yöntemi çağırırlar ve işleyicinin kendisi durum kontrolünü, gerekçe görüntülemeyi, sistem diyalog çağrısını ve sonucun callback'e iletilmesini yönetir. Bu, tek sorumluluk ilkesini uygular ve iş mantığını platform izin kodundan ayırır.
Bir uygulama 3 veya daha fazla tehlikeli izin kullandığında bir Permission Handler gerekli hale gelir. Tek bir izin (örneğin, QR kod tarayıcı için kamera) içeren basit uygulamalar için doğrudan çağrı yeterli olabilir. Ancak kamera, coğrafi konum, bildirimler ve depolama içeren tipik bir mobil uygulama için — merkezi bir işleyici bakım için gereklidir.
Tipik bir Permission Handler üç katmandan oluşur: bir arayüz sözleşmesi, ActivityResultLauncher ile bir uygulama ve bir ViewModel katmanı. Arayüz, her izin için istek yöntemlerini tanımlar — requestCamera, requestLocation, requestStorage. Uygulama, bu yöntemleri ilgili ActivityResultContracts.RequestPermission sözleşmelerine bağlar.
Temel mimari bileşenler:
Bu mimari, testlerde uygulamaların kolayca değiştirilmesine olanak tanır: gerçek bir ActivityResultLauncher yerine, sistem etkileşimi olmadan önceden tanımlanmış bir sonuç döndüren bir mock kullanılır. Bu, izin diyaloğu için bir Activity başlatmanın imkansız olduğu UI mantığının birim testleri için çok önemlidir.
Permission Handler, Activity ve Fragment yaşam döngüsünü dikkate almalıdır. Başlatıcılar, ekran döndürme ve Activity yeniden oluşturma sırasında durumu otomatik olarak kaydeden ve geri yükleyen ActivityResultRegistry'ye kaydedilir. İşleyici, Activity veya Fragment'e doğrudan referanslar saklamamalıdır — bunun yerine WeakReference kullanın veya registry'yi yapıcı aracılığıyla iletin. Bu, yapılandırma değişiklikleri sırasında bellek sızıntılarını ve çökmeleri önler.
Bir Permission Handler'ın temel uygulaması, ActivityResultContracts.RequestPermission üzerine inşa edilir. İşleyici, ComponentActivity veya Fragment'ten ActivityResultRegistry'yi alır ve her izin için başlatıcılar kaydeder. Her başlatıcı, kullanıcının yanıtından sonra çağrılan bir callback lambda'sını kabul eder.
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
}
}
İşleyici, ActivityResultRegistry'ye erişim sağlayan registerForActivityResult aracılığıyla Activity'nin onCreate yönteminde başlatılır. Başlatmadan sonra, işleyici Activity yaşam döngüsü boyunca istekleri işlemeye hazırdır. İlk istekten önce initialize'i çağırmak önemlidir, aksi takdirde başlatıcı kaydedilmez.
Permission Handler'ı ViewModel ile Entegre Etmek en gelişmiş yaklaşımdır. ViewModel istek durumunu yönetirken, Handler yalnızca platform çağrılarını gerçekleştirir. ViewModel, UiState'in hangi iznin istendiğini ve hangi sonucun alındığını açıkladığı StateFlow<PermissionUiState> içerir. Activity bu StateFlow'a abone olur ve isteği Handler'a devreder.
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()
}
Bu modelde, Activity başlangıçta Handler aracılığıyla isPermissionGranted'ı kontrol ederken, ViewModel yalnızca durumu yönetir. İzin verilmemişse — Activity uiState'e abone olur, Handler'dan requestCamera'yı çağırır ve sonucu onPermissionResult aracılığıyla ViewModel'e geri iletir. Platform kodunu iş mantığından ayırmak, ViewModel'in Android bağımlılıkları olmadan test edilmesine olanak tanır.
PermissionHandler arayüzü sayesinde Permission Handler'ın birim testi mümkündür. Testlerde, farklı senaryoları simüle eden bir FakePermissionHandler oluşturulur: izin verildi, reddedildi, Never Ask Again. Her senaryo bağımsız olarak test edilir. Bu, üç sonucun tümüne doğru tepki vermesi gereken UI mantığını test etmek için özellikle önemlidir.
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
}
}
Sahte uygulama, emülatör olmadan ViewModel'i test etmeye olanak tanır. cameraResult'ı istenen değere ayarlayın ve ViewModel'in UiState'ini doğru şekilde güncellediğini doğrulayın. Entegrasyon testleri, ActivityScenario ile gerçek PermissionHandler'ı kontrol eder, ancak genellikle uygulama başına yalnızca 2-3 test bulunur — kalan senaryolar sahte ile birim testleri tarafından kapsanır.
Permission Handler ile çalışırken tipik hatalar şunları içerir: her API çağrısından önce checkSelfPermission'ı kontrol etmemek, shouldShowRequestPermissionRationale'ı yok saymak, Never Ask Again'den sonra requestPermissions'ı yeniden çağırmak ve Activity yaşam döngüsünü dikkate almadan başlatıcıları saklamak. Her sorunu ve çözümünü inceleyelim.
En yaygın hata, izin durumunu kontrol etmeden bir API'yi çağırmaktır. Geliştiriciler, bir izin bir kez verildiyse sonsuza kadar kalacağını varsayar. Ancak, kullanıcı izni istediği zaman ayarlar aracılığıyla iptal edebilir. Bir Permission Handler, hassas bir işlem gerçekleştirmeden önce her zaman isPermissionGranted'ı çağırmalıdır. İkinci yaygın hata, shouldShowRequestPermissionRationale'ı yok saymak ve isteği tekrarlamaktır; bu, Never Ask Again modunda diyalog olmadan anında reddedilmeye yol açar.
En iyi uygulamalar şunları içerir: tüm Activity yaşam döngüsü için tek bir Handler örneği oluşturmak, sonuçları ViewModel'e iletmek için SharedFlow kullanmak, analiz için tüm istekleri ve reddetmeleri günlüğe kaydetmek ve ilk reddetmede sistem diyaloğundan önce özel bir gerekçe diyaloğu göstermek. Bu kurallara uymak, tüm Android sürümlerinde kararlı izin yönetimini garanti eder.
Sıkça Sorulan Sorular
Permission Handler, checkSelfPermission, requestPermissions ve shouldShowRequestPermissionRationale'ı kapsülleyen, çalışma zamanı izinlerinin merkezi yönetimi için bir bileşendir. Kod bakımını basitleştirir ve test etmeyi iyileştirir.
androidx.activity kütüphanesinden ActivityResultContracts.RequestPermission kullanılması önerilir. Eski onRequestPermissionsResult'ın yerini alır ve Boolean sonucu ile temiz bir callback API'si sağlar.
Tek bir izin için Handler zorunlu değildir — Activity'de doğrudan RequestPermission başlatıcı çağrısı kullanabilirsiniz. Kod tekrarını önlemek için 3 veya daha fazla izin olduğunda Handler gerekli hale gelir.
Birim testleri için bir PermissionHandler arayüzü ve onun sahte uygulamasını oluşturun. Sahte, sistem çağrıları olmadan önceden tanımlanmış sonuçlar döndürür. Bu, emülatör olmadan ViewModel ve UI mantığını test etmeye olanak tanır.
Reddetmeden sonra, shouldShowRequestPermissionRationale'ı kontrol edin. Yöntem false döndürürse — Never Ask Again modu aktiftir. Handler, PermissionResult.DENIED(false) döndürmeli ve UI, Ayarlar'a gitmek için bir buton göstermelidir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun