Ders 15 / 15
İzin Modeli
Kurulum izni ile çalışma zamanı izninin, normal ve tehlikeli iznin, kesin ve yaklaşık konumun, reddin ve bir daha sorma durumunun karşılama modeli ile kursun kapanış tablosu.
Yayın Güncelleme
Bu derste
- Kurulum izni ile çalışma zamanı iznini ve normal izin ile tehlikeli izni ayırır
- Kullanıcının henüz yanıtlamadığı tehlikeli iznin bekleyen, reddedilenin karşılıksız olduğunu ölçer
- Kesin konum yerine verilen yaklaşık konumun ikame, bir daha sorma durumunun karşılıksız olduğunu ölçer
- Kurs boyunca ölçülen on beş dersin karşılamasını tek tabloda okur
İçindekiler
Önceki ders uygulamanın aygıta kurulmasını ölçtü. Kurulum bir kez yapılır ve uygulama aygıtta durur. Sonra uygulama kullanıcının kamerasını, konumunu ya da rehberini ister. İzin (permission) uygulamanın korunan bir veriye ya da donanıma erişim hakkıdır. Bu ders, bir izin isteğine platformun ne verdiğini ölçer ve kursu kapatır.
Kursun ölçü ekseni değişmez: bu isteği platform nasıl karşılıyor. Karşılama dörttür: tam, ikame, bekleyen ve karşılıksız. Karşılamayı etiket değil kural verir: verilen değer istenene eşitse tam, eşit değilse ikame; bekleyen ve karşılıksız doğrudan sonuçtan çıkar. Aşağıdaki dosya o kuralı taşır ve her derste aynen yinelenir.
// Fulfillment.kt — bir istegin platformca nasil karsilandigini siniflandirir
sealed interface Outcome {
data class Given(val value: String) : Outcome
data class Waiting(val on: String) : Outcome
data class Refused(val reason: String) : Outcome
}
class Ask(val label: String, val wanted: String, val outcome: Outcome) {
val fulfillment: String = when (outcome) {
is Outcome.Waiting -> "bekleyen"
is Outcome.Refused -> "karsiliksiz"
is Outcome.Given -> if (outcome.value == wanted) "tam" else "ikame"
}
fun report() {
val detail = when (outcome) {
is Outcome.Given -> "verilen=${outcome.value}"
is Outcome.Waiting -> "bekledigi=${outcome.on}"
is Outcome.Refused -> "neden=${outcome.reason}"
}
println("$label: istenen=$wanted | $detail -> $fulfillment")
}
}
fun tally(asks: List<Ask>) {
for (name in listOf("tam", "ikame", "bekleyen", "karsiliksiz")) {
println("$name: ${asks.count { it.fulfillment == name }}")
}
}
M30/K02’deki izin dersleri, istenen izin kümesiyle verilen kümenin buluşmasını ölçmüştü. Burada her izin ayrı bir istektir ve kullanıcının henüz yanıtlamadığı bir izin, kendi başına bir kategoridir: bekleyen. M12/K03’te karar kullanıcıya aitti, ama kategori kararın sahibini adlandırmıyordu; karşılama aynı kalıyordu ve yalnız neden= alanı sahibi adlandırabiliyordu. Burada da öyledir.
Kurulum İzni ve Çalışma Zamanı İzni
İzinler iki anda verilir. Kurulum izni (install-time permission) uygulama kurulurken, kullanıcıya soru sorulmadan verilir. Çalışma zamanı izni (runtime permission) uygulama çalışırken, kullanıcıya bir iletişim kutusu gösterilerek istenir. Hangisinin hangi yoldan geçeceğini izin türü belirler:
- Normal izin (normal permission) kullanıcının gizliliğine az dokunur, örneğin ağa erişim. Bildirim dosyasında bildirilmesi yeterlidir ve kurulumda verilir.
- Tehlikeli izin (dangerous permission) kullanıcının verisine ya da donanımına dokunur, örneğin kamera, ses kaydı ve konum. Bildirim dosyasında bildirilmesi gerekir, ama kullanıcı çalışma zamanında onaylamadıkça verilmez.
Bildirim dosyasındaki bildirim şuna benzer. Platform araçlarının okuduğu bir dosyadır ve çalıştırılmaz.
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" />
Model bir izni adı, tehlikeli olup olmadığı ve bildirim dosyasında bildirilip bildirilmediğiyle tanımlar. Kullanıcının yanıtı ayrı bir girdidir: yanıt yoksa null, onay ver, ret reddet. Başka bir metin, kullanıcının istenenden farklı bir şey verdiğini gösterir.
// Permission.kt — izin istegi modeli
class Perm(val name: String, val dangerous: Boolean, val declared: Boolean)
fun resolvePermission(label: String, perm: Perm, reply: String?): Ask {
val outcome = when {
!perm.declared -> Outcome.Refused("bildirilmedi")
!perm.dangerous -> Outcome.Given(perm.name)
reply == null -> Outcome.Waiting("kullanici-yaniti")
reply == "reddet" -> Outcome.Refused("kullanici-reddetti")
reply == "ver" -> Outcome.Given(perm.name)
else -> Outcome.Given(reply)
}
return Ask(label, perm.name, outcome)
}
Ölçümde üç izin vardır. Normal bir izin ile iki tehlikeli izin sorulur ve kullanıcı hiçbirini henüz yanıtlamamıştır.
// Main.kt — normal ve tehlikeli izin
fun main() {
val asks = listOf(
resolvePermission("ag", Perm("internet", false, true), null),
resolvePermission("kamera", Perm("kamera", true, true), null),
resolvePermission("mikrofon", Perm("ses-kaydi", true, true), null),
)
for (ask in asks) ask.report()
tally(asks)
}
ag: istenen=internet | verilen=internet -> tam kamera: istenen=kamera | bekledigi=kullanici-yaniti -> bekleyen mikrofon: istenen=ses-kaydi | bekledigi=kullanici-yaniti -> bekleyen tam: 1 ikame: 0 bekleyen: 2 karsiliksiz: 0
Normal izin hiçbir yanıt beklemeden tam oldu. Tehlikeli iki izin reddedilmedi, bekliyor ve beklediği şeyin adı kullanici-yaniti. Bu dersin karşılaması bekleyendir.
AP6. Model yanıtı tek bir metin olarak kurar. Gerçek platformda yanıt, iletişim kutusunun sonucudur ve iznin hangi seçenekle verildiğine göre ayrılır; ölçüm iletişim kutusunu taklit etmez.
AP7. Model normal ile tehlikeli izin ayrımını izin başına bir mantıksal değer olarak alır. Gerçek platformda bir iznin koruma düzeyini sürüm belirler; hangi iznin hangi düzeyde olduğu ölçülmedi.
İzin İsteme Akışı
Çalışma zamanı izni kod içinde bir sözleşmeyle istenir. ActivityResultContracts.RequestPermission iznin adını alır ve kullanıcının onay verip vermediğini bir geri çağrıyla bildirir. Aşağıdaki parça akışın şeklini gösterir. Platform API’si kullanır, bu yüzden ölçülmez ve çıktısı iddia edilmez.
val launcher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) openCamera() else showFallback()
}
launcher.launch(Manifest.permission.CAMERA)
Akışta iki geri dönüş yolu vardır ve model ikisini de ayırır. İletişim kutusu açıldığı anda uygulama yanıt beklemektedir: bekleyen. Kullanıcı yanıt verince aynı istek ya tam ya karşılıksız olur. Bunu ölçmek için aynı izin üç kullanıcı durumunda sorulur.
// Main.kt — yanit once, onay ve ret
fun main() {
val camera = Perm("kamera", true, true)
val asks = listOf(
resolvePermission("yanit-yok", camera, null),
resolvePermission("onay", camera, "ver"),
resolvePermission("ret", camera, "reddet"),
)
for (ask in asks) ask.report()
tally(asks)
}
yanit-yok: istenen=kamera | bekledigi=kullanici-yaniti -> bekleyen onay: istenen=kamera | verilen=kamera -> tam ret: istenen=kamera | neden=kullanici-reddetti -> karsiliksiz tam: 1 ikame: 0 bekleyen: 1 karsiliksiz: 1
Aynı istek üç karşılama aldı ve fark izinden gelmedi, kullanıcının yanıtından geldi. Karşılama kategorisi sahibi söylemez; yalnız neden=kullanici-reddetti kullanıcının kararını adlandırır.
Kesin ve Yaklaşık Konum
Konum izni iki ayrıntıda istenir. Kesin konum (precise location) aygıtın bulunduğu yeri ayrıntılı verir. Yaklaşık konum (approximate location) yalnız çevreyi verir. Uygulama ikisini birlikte isteyebilir ve kullanıcı yaklaşık olanı seçerse uygulama kesin konum olmadan çalışmak zorunda kalır. Model bunu ikame sayar: istenen kesin konumdur, verilen yaklaşık konumdur.
// Main.kt — kesin konum istendi, kullanici ne verdi
fun main() {
val location = Perm("kesin-konum", true, true)
val asks = listOf(
resolvePermission("kesin-ver", location, "ver"),
resolvePermission("yaklasik-ver", location, "yaklasik-konum"),
resolvePermission("yanit-yok", location, null),
resolvePermission("ret", location, "reddet"),
)
for (ask in asks) ask.report()
tally(asks)
}
kesin-ver: istenen=kesin-konum | verilen=kesin-konum -> tam yaklasik-ver: istenen=kesin-konum | verilen=yaklasik-konum -> ikame yanit-yok: istenen=kesin-konum | bekledigi=kullanici-yaniti -> bekleyen ret: istenen=kesin-konum | neden=kullanici-reddetti -> karsiliksiz tam: 1 ikame: 1 bekleyen: 1 karsiliksiz: 1
Dört durumun dördü ayrı bir kategoriye düştü. Kullanıcı yaklaşık konumu verince uygulama reddedilmedi, istediğinden azıyla çalışıyor.
AP8. Model yaklaşık konumu kesin konumun ikamesi olarak adlandırır. Gerçek platformda yaklaşık konumun doğruluğu aygıta ve ayara göre değişir; ölçüm bu doğruluğu bir sayıyla vermez.
Ret, Bir Daha Sorma ve Gerekçe
Kullanıcı bir isteği reddedince uygulama aynı iznin iletişim kutusunu hemen tekrar gösteremeyebilir. Platform iki durumu ayırır. İlk retten sonra sistem, iznin neden gerektiğini açıklamak için gerekçe göstermeyi önerir: shouldShowRequestPermissionRationale değeri true döner. Gerekçe (rationale) uygulamanın kendi ekranıdır, kullanıcıya iznin neye yaradığını söyler. Kullanıcı isteği “bir daha sorma” ile reddettiğinde ise sistem iletişim kutusunu hiç göstermez ve istek doğrudan reddedilmiş döner.
Gerekçe karşılamayı değiştirmez. Değişen, kullanıcının vereceği yanıtın olasılığıdır. Karşılamayı değiştiren, kullanıcıya ulaşılıp ulaşılamadığıdır. Model bunu tek bir girdiyle kurar: iletişim kutusu engellenmiş mi.
// Denial.kt — ret sonrasi istek modeli
fun askAfterDenial(label: String, perm: Perm, blocked: Boolean, reply: String?): Ask {
val outcome = when {
blocked -> Outcome.Refused("bir-daha-sorma")
reply == null -> Outcome.Waiting("kullanici-yaniti")
reply == "reddet" -> Outcome.Refused("kullanici-reddetti")
else -> Outcome.Given(perm.name)
}
return Ask(label, perm.name, outcome)
}
Ölçümde kullanıcı kamerayı bir kez reddetmiştir. İlk iki satırda iletişim kutusu açıktır ve gerekçeden önce ve sonra sorulur. Son iki satırda kullanıcı “bir daha sorma” demiştir: biri yanıtsız, biri onaylı sorulur.
// Main.kt — gerekce ve bir daha sorma
fun main() {
val camera = Perm("kamera", true, true)
val asks = listOf(
askAfterDenial("gerekce-oncesi", camera, false, null),
askAfterDenial("gerekce-sonrasi", camera, false, "ver"),
askAfterDenial("bir-daha-sorma", camera, true, null),
askAfterDenial("bir-daha-sorma-onay", camera, true, "ver"),
)
for (ask in asks) ask.report()
tally(asks)
}
gerekce-oncesi: istenen=kamera | bekledigi=kullanici-yaniti -> bekleyen gerekce-sonrasi: istenen=kamera | verilen=kamera -> tam bir-daha-sorma: istenen=kamera | neden=bir-daha-sorma -> karsiliksiz bir-daha-sorma-onay: istenen=kamera | neden=bir-daha-sorma -> karsiliksiz tam: 1 ikame: 0 bekleyen: 1 karsiliksiz: 2
Engellenmiş iletişim kutusunda onay yanıtı bile sonucu değiştirmedi, çünkü yanıt kullanıcıya hiç ulaşmadı. Bu durumdan çıkış uygulamanın içinde değildir: kullanıcı iznin kendisini sistem ayarlarından açar.
AP9. Model “bir daha sorma” durumunu tek bir mantıksal değer olarak alır. Gerçek platformda bu durumun hangi reddin kaç kez tekrarlanmasıyla oluştuğu sürüme göre değişir; ölçüm bunu belirlemez.
Sınırlayıcı Ölçüm
Karşılama kuralı sonucu ve eşitliği görür, nedeni görmez. İzinde bu sınırın bir yüzü vardır: bildirilmemiş bir izin hiçbir kullanıcı yanıtıyla verilemez. Model karşılaştırmayı sırayla yapar. Önce izin bildirim dosyasında var mı, sonra tehlikeli mi, sonra kullanıcı ne dedi. İlk uyuşmayan alan sonucu belirler ve adını verir.
Ölçümde aynı kamera izni bildirimde yazılı olmadan ve kullanıcı onay vermişken sorulur. Karşılaştırma için normal bir izin de bildirilmeden sorulur ve bildirilmiş hali eklenir.
// Main.kt — bildirilmemis izin
fun main() {
val asks = listOf(
resolvePermission("bildirimsiz-kamera", Perm("kamera", true, false), "ver"),
resolvePermission("bildirimsiz-ag", Perm("internet", false, false), null),
resolvePermission("bildirimli-kamera", Perm("kamera", true, true), "ver"),
)
for (ask in asks) ask.report()
tally(asks)
}
bildirimsiz-kamera: istenen=kamera | neden=bildirilmedi -> karsiliksiz bildirimsiz-ag: istenen=internet | neden=bildirilmedi -> karsiliksiz bildirimli-kamera: istenen=kamera | verilen=kamera -> tam tam: 1 ikame: 0 bekleyen: 0 karsiliksiz: 2
Kullanıcının onayı bildirilmemiş kamera iznini kurtarmadı. Normal izin de bildirilmeden verilmedi: kurulum izni de bildirim dosyasından başlar.
Kapanış Tablosu
Kurs on beş ders boyunca aynı soruyu sordu. Aşağıdaki tablo her dersin ölçtüğü isteği, o dersin karşılamasını ve dersin sınırlayıcı ölçümünün gösterdiğini bir arada verir. Karşılama hücresi her dersin Özet’indeki “Bu dersin karşılaması” maddesinden alınmıştır. Seçim kuralı tektir: dersin karşılaması, “Ölçülen istek” sütunundaki odak isteğin karşılamasıdır; dersteki ölçümlerin çoğunluğu değildir.
| Ders | Ölçülen istek | Karşılama | Sınırlayıcı ölçüm |
|---|---|---|---|
| Android Mimari Katmanları | bildirilen activity, normal izin | tam | aynı tam iki ayrı nedenden gelir, kural nedeni görmez; uyumluluk karşılığı ikame |
| Activity’ler | bildirilmemiş ya da dışa kapalı activity | karşılıksız | android:exported: aynı activity çağırana göre tam ya da karşılıksız |
| Activity Yaşam Döngüsü | yapılandırma değişikliğinde durum | ikame | durdurulmuş activity’ye arayüz güncellemesi bekler, yok edilmişe karşılıksız |
| Fragment’lar | ev sahibi durmuşken fragment işlemi | bekleyen | durum kaydedildikten sonra commit karşılıksız, commitAllowingStateLoss ikame |
| Servisler | arka plandan servis başlatma | karşılıksız | ön plan servisi ikame, koşullu zamanlanmış iş bekleyen |
| Yayın Alıcıları | örtük sistem yayını | karşılıksız | aynı alıcı sınıfı üç yolda iki sonuç |
| İçerik Sağlayıcılar | sağlayıcıdan satır sorgusu | tam | izinsiz sorgu karşılıksız, URI izni yalnız tek satıra |
| Intent’ler ve Intent Süzgeçleri | örtük intent | bekleyen | kurulu uygulama sayısı: sıfır karşılıksız, bir tam, iki bekleyen |
| Görevler ve Geri Yığını | yeni örnek isteği | ikame | aynı kip, yığıtın durumuna göre iki sonuç |
| Bildirim Dosyası | birleştirilen bildirim öğesi | tam | yer tutucu çözülmeden sonuç belli değil, bekleyen |
| Kaynak Sistemi | niteleyicili kaynak seçimi | ikame | bölge dizini yoksa dil dizini; niteleyici çakışması |
| Derleme Sistemi | geçişli bağımlılık sürümü | ikame | 1.10 sürümü 1.9 sürümünden yüksek; katı sürüm çakışması |
| Ürün Çeşitleri | kod kümesinden istenen dosya | ikame | aynı iki kümede bulunan .kt sınıfı çakışır, karşılıksız |
| Geliştirme Ortamı | uygulamanın aygıta kurulması | bekleyen | işlemci mimarisi uyuşmazlığı API düzeyi yeterli olsa da karşılıksız |
| İzin Modeli | tehlikeli çalışma zamanı izni | bekleyen | bildirilmemiş izin hiçbir yanıtla verilmez |
Dağılım: tam 3, ikame 5, bekleyen 4, karşılıksız 3; toplam 15. Açılış dersindeki çekirdek 3/2/2/1 dağılımını verdi. İki dağılım aynı şey değildir. Çekirdek sekiz isteğin tek bir ölçümüydü; tablo on beş dersin her birinin kendi karşılamasını sayar. Aralarında bir tahmin köprüsü kurulmaz: biri bir modelin çıktısı, öteki on beş ayrı modelin tek tek seçilmiş satırıdır.
Özet
- Bu dersin ölçütü: karşılama; iznin kullanıcıdan yanıt bekleyip beklemediği, kimin karar verdiği değil. Bu dersin karşılaması: bekleyen.
- Kurulum izni kurulumda, kullanıcıya sorulmadan verilir; çalışma zamanı izni çalışırken, kullanıcıya sorularak verilir.
- Normal izin bildirilince tam olur; tehlikeli izin kullanıcı yanıtlayana kadar bekleyendir, ret karşılıksızdır.
- Kesin konum istenip yaklaşık konum verilirse sonuç ikamedir.
- Gerekçe karşılamayı değiştirmez; “bir daha sorma” durumunda sistem kullanıcıya ulaşmaz ve istek karşılıksız döner.
- Bildirim dosyasında bildirilmemiş izin, kullanıcı onay verse de verilmez.
Sonraki Adım
Kurs burada kapanıyor. Sıradaki kurs arayüzün bildirimsel biçimde kurulmasını ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.