Ders 08 / 15
Intent'ler ve Intent Süzgeçleri
Açık ve örtük intent'in farkı, intent süzgecinin eylem, kategori ve veriyle eşleşmesi, birden çok uygulamanın aynı intent'i karşıladığı durumda bekleyen karşılama ve varsayılan uygulama kaldırılınca değişen sonuç.
Yayın Güncelleme
Bu derste
- Açık ve örtük intent'i, intent süzgecinin üç ölçütünü ayırır
- Süzgecin varsayılan kategoriyi taşımamasının intent'i karşılıksız bıraktığını ölçer
- Birden çok uygulamanın eşleştiği intent'in bekleyen kaldığını ve koşulunu adıyla taşıdığını gösterir
- Varsayılan uygulama kaldırılınca aynı intent'in ikame ya da bekleyen karşılandığını doğrular
İçindekiler
İçerik sağlayıcıyı bir URI ile çağırdınız. Başka bir uygulama bileşenini çağırmanın genel yolu ise intent’tir. Önceki derslerde activity’yi, servisi ve yayın alıcısını farklı yollarla başlattınız: startActivity, startService, sendBroadcast. Üçünün ortak girdisi Intent nesnesidir. Intent, “şunu yap” diyen bir mesajdır ve hedefi iki biçimde söyler.
- Açık intent: hedef uygulama bileşeninin sınıfını ya da paket adını doğrudan verir. Platform eşleştirme yapmaz, o uygulama bileşenini başlatır.
- Örtük intent: hedefi vermez, yapılacak işi söyler: bir eylem, bir kategori ve bir veri. Platform kurulu uygulamaların intent süzgeçlerine bakar ve işi karşılayabilecek uygulama bileşenini bulur.
Dersin sorusu: bir intent gönderildiğinde platform isteği nasıl karşılıyor? Örtük intent’te bu soru tek bir cevapla bitmez: sıfır, bir ya da birden çok uygulama eşleşebilir. Ders üç sonucu da ölçer.
// 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 }}")
}
}
Intent Süzgeci
Bir uygulama bileşeninin hangi intent’leri alacağını bildirim dosyasındaki <intent-filter> öğesi söyler. Süzgeç üç ölçüt taşır.
- Eylem (
action): yapılacak işin adı.VIEWgöstermek,SENDgöndermek içindir. Intent’in eylemi, süzgeçte bildirilen eylemlerden biri olmalıdır. - Kategori (
category): uygulama bileşeninin nasıl çağrılabileceği. Intent’in taşıdığı her kategori süzgeçte bulunmalıdır. - Veri (
data): işlenecek içeriğin şeması ve türü. Süzgeç veri bildirmişse intent’in verisi onunla uyuşmalıdır; süzgeç veri bildirmemişse intent de veri taşımamalıdır.
<activity android:name=".ImageViewerActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="image" /> </intent-filter> </activity>
Burada ince bir kural vardır. startActivity ile örtük intent gönderildiğinde platform intent’e kendiliğinden DEFAULT kategorisini ekler. Bu yüzden startActivity ile ulaşılmak istenen bir activity’nin süzgecinde DEFAULT kategorisi bulunmak zorundadır. Süzgeçte eylem ve veri doğru olsa bile bu kategori yoksa eşleşme olmaz. Kural sık atlanır: activity çalışır görünür, ama örtük intent’e ulaşmaz.
// Platform kodu: ortuk ve acik intent gonderme
import android.content.Intent
import android.net.Uri
val implicit = Intent(Intent.ACTION_VIEW, Uri.parse("geo:41.0,29.0"))
startActivity(implicit)
val explicit = Intent().setClassName("com.example.maps", "com.example.maps.MapActivity")
startActivity(explicit)
Örtük intent yalnız eşleşen uygulama yoksa değil, birden çok uygulama eşleşiyorsa da özel bir davranış gösterir: platform kullanıcıya seçim ekranı gösterir. Kullanıcı bir uygulamayı varsayılan yaparsa sonraki intent’ler seçim sormadan o uygulamaya gider. Bu ders bu iki davranışı ölçer.
Eşleşme Modeli
Model intent’i eylem, veri şeması ve isteğe bağlı bir varsayılan seçimle kurar. İlk yardımcı dosya eşleşme kuralını ve sonuç kuralını, ikincisi örnek uygulamaların süzgeçlerini taşır.
// Resolver.kt — intent suzgecleriyle eslesmeyi ve sonucu kurar
class Filter(val app: String, val action: String, val categories: Set<String>, val scheme: String?)
fun matches(filter: Filter, action: String, scheme: String?): Boolean =
filter.action == action && "DEFAULT" in filter.categories && filter.scheme == scheme
fun resolve(action: String, scheme: String?, filters: List<Filter>, chosen: String?): Outcome {
val hits = filters.filter { matches(it, action, scheme) }.map { it.app }.sorted()
return when {
hits.isEmpty() -> Outcome.Refused("eslesme-yok")
hits.size == 1 -> Outcome.Given(hits[0])
chosen != null && chosen in hits -> Outcome.Given(chosen)
else -> Outcome.Waiting("kullanici-secimi")
}
}
fun resolveExplicit(app: String, installed: Set<String>): Outcome =
if (app in installed) Outcome.Given(app) else Outcome.Refused("uygulama-yok")
// Installed.kt — ornek uygulamalarin suzgecleri
val galeri = Filter("galeri", "VIEW", setOf("DEFAULT"), "image")
val duzenleyici = Filter("duzenleyici", "VIEW", setOf("DEFAULT"), "image")
val kamera = Filter("kamera", "VIEW", setOf("DEFAULT"), "image")
val harita = Filter("harita", "VIEW", setOf("DEFAULT"), "geo")
val tarayici = Filter("tarayici", "VIEW", setOf("DEFAULT", "BROWSABLE"), "https")
val sadeceTarama = Filter("sade-tarama", "VIEW", setOf("BROWSABLE"), "image")
resolve platformun bütün ayrıntısını modellemez; üç ölçütü (eylem, DEFAULT kategorisi, şema) ve sonuç kuralını taşır. Sonuç kuralı şudur: sıfır eşleşme karşılıksız, tek eşleşme o uygulama, birden çok eşleşmede varsayılan seçili ve eşleşenler arasındaysa o uygulama, değilse kullanıcının seçimini bekleyen karşılama.
UB1. Model veri ölçütünü yalnız şemayla kurar. Veri türü (mimeType), yol örüntüleri ve birden çok <data> öğesinin birleşimi ölçülmedi. Kategorilerden yalnız DEFAULT denetlenir.
Açık ve Örtük Intent
İlk ölçüm dört istek yapar. Açık intent kurulu uygulamayı doğrudan adıyla çağırır. Örtük intent bir şema ve eylemle eşleşme arar. Son iki istek karşılıksız kalır: biri kurulu olmayan bir uygulamayı açıkça ister, diğeri hiçbir uygulamanın süzgecinde bulunmayan bir şemayı gönderir.
// Main.kt — acik ve ortuk intent
fun main() {
val installed = setOf("galeri", "harita", "tarayici")
val filters = listOf(galeri, harita, tarayici)
val asks = listOf(
Ask("acik", "harita", resolveExplicit("harita", installed)),
Ask("ortuk-geo", "harita", resolve("VIEW", "geo", filters, null)),
Ask("ortuk-ftp", "tarayici", resolve("VIEW", "ftp", filters, null)),
Ask("acik-yok", "muzik", resolveExplicit("muzik", installed)),
)
for (ask in asks) {
ask.report()
}
tally(asks)
}
acik: istenen=harita | verilen=harita -> tam ortuk-geo: istenen=harita | verilen=harita -> tam ortuk-ftp: istenen=tarayici | neden=eslesme-yok -> karsiliksiz acik-yok: istenen=muzik | neden=uygulama-yok -> karsiliksiz tam: 2 ikame: 0 bekleyen: 0 karsiliksiz: 2
Açık intent ve eşleşen örtük intent tam karşılandı: ikisi de aynı uygulamaya ulaştı. ftp şeması hiçbir süzgeçte olmadığı için örtük intent karşılıksız kaldı; kurulu olmayan uygulamayı adıyla çağıran açık intent de öyle. İki karşılıksız arasındaki fark nedende görünür: eslesme-yok platformun aradığı eşleşmenin olmayışıdır, uygulama-yok ise hedefin kendisinin olmayışıdır.
Pratik sonuç: örtük intent’i gönderen kod, eşleşme olmayabileceğini hesaba katmak zorundadır. Platform bu durumda ActivityNotFoundException fırlatır. Gönderenin önceden resolveActivity ya da paket görünürlük bildirimiyle kontrol etmesi gerekir.
UB2. Modelde paket görünürlüğü yoktur. Gerçekte bir uygulama, başka uygulamaların kurulu olup olmadığını görmek için bildirim dosyasında <queries> öğesi bildirmek zorundadır; bu ölçülmedi.
Süzgeçte Eksik Kategori
Süzgeç doğru eylemi ve doğru şemayı taşıyabilir ve yine de intent’e ulaşmaz. Eksik DEFAULT kategorisi bunun en yaygın nedenidir. sade-tarama uygulaması VIEW eylemini ve image şemasını bildirmiştir, ama süzgecinde yalnız BROWSABLE kategorisi vardır.
// Main.kt — varsayilan kategori
fun main() {
val asks = listOf(
Ask("kategori-var", "galeri", resolve("VIEW", "image", listOf(galeri), null)),
Ask("kategori-yok", "sade-tarama", resolve("VIEW", "image", listOf(sadeceTarama), null)),
Ask("eylem-yok", "galeri", resolve("SEND", "image", listOf(galeri), null)),
)
for (ask in asks) {
ask.report()
}
tally(asks)
}
kategori-var: istenen=galeri | verilen=galeri -> tam kategori-yok: istenen=sade-tarama | neden=eslesme-yok -> karsiliksiz eylem-yok: istenen=galeri | neden=eslesme-yok -> karsiliksiz tam: 1 ikame: 0 bekleyen: 0 karsiliksiz: 2
DEFAULT kategorisini taşıyan süzgeç tam karşılandı. Kategoriyi taşımayan süzgeç, eylemi ve şeması doğru olduğu hâlde karşılıksız kaldı. Eylemi tutmayan intent de karşılıksız kaldı. İkisinin nedeni aynı etiketle (eslesme-yok) görünür; sebebi bulmak için süzgecin üç ölçütüne tek tek bakılır.
UB3. BROWSABLE kategorisi yalnız tarayıcıdan gelen bağlantılar için gerekir ve startActivity ile gelen örtük intent’e kendiliğinden eklenmez. Model bu ayrımı tek bir kuralla, DEFAULT denetimiyle kurar.
Birden Çok Eşleşme
İki uygulama aynı intent’i karşılayabiliyorsa platform seçimi kullanıcıya bırakır. Seçim ekranı gösterilmiştir ama kullanıcı henüz seçmemiştir: istek ne karşılanmış ne de reddedilmiştir. Bu bekleyen karşılamadır ve beklediği koşul kullanici-secimidir.
// Main.kt — tek eslesme, cok eslesme, varsayilan
fun main() {
val filters = listOf(galeri, duzenleyici, harita)
val asks = listOf(
Ask("tek-eslesme", "harita", resolve("VIEW", "geo", filters, null)),
Ask("cok-eslesme", "galeri", resolve("VIEW", "image", filters, null)),
Ask("varsayilan", "galeri", resolve("VIEW", "image", filters, "galeri")),
)
for (ask in asks) {
ask.report()
}
tally(asks)
}
tek-eslesme: istenen=harita | verilen=harita -> tam cok-eslesme: istenen=galeri | bekledigi=kullanici-secimi -> bekleyen varsayilan: istenen=galeri | verilen=galeri -> tam tam: 2 ikame: 0 bekleyen: 1 karsiliksiz: 0
Tek eşleşme tam. image şemasına iki uygulama uyduğu için ikinci istek bekleyen kaldı; çağıran tarafın kodu seçim ekranını göremez, yalnız startActivity döndüğünü görür. Üçüncü istekte kullanıcı galeriyi varsayılan yapmıştır ve platform seçim sormadan o uygulamayı başlattı: tam.
M12/K04’te “ertelendi” ile “bekleyen” ayrı tutuldu. Burada da aynı ayrım geçerlidir: seçim ekranı bir erteleme değil, adı belli bir koşulu bekleyen karşılamadır. Koşul kullanici-secimi olarak taşınır ve koşul karşılandığında sonuç tam ya da ikame olur.
UB4. Model varsayılan seçimi tek bir ad olarak taşır. Gerçekte varsayılan, intent türüne ve veri şemasına göre ayrı ayrı kaydedilir; bu ölçülmedi.
Varsayılan Uygulama Kaldırılınca
Varsayılan seçili uygulama kaldırılabilir. Bu durumda aynı intent artık başka sonuç verir. Kullanıcı galeriyi varsayılan yapmıştır; sonra galeri silinir. Geriye duzenleyici kalırsa tek eşleşme olur ve intent ona gider: kullanıcı galeriyi istiyordu, düzenleyici verildi. Geriye iki uygulama kalırsa seçim yeniden sorulur.
// Main.kt — varsayilan uygulama kaldirilinca
fun main() {
val asks = listOf(
Ask("duruyor", "galeri", resolve("VIEW", "image", listOf(galeri, duzenleyici), "galeri")),
Ask("tek-kaldi", "galeri", resolve("VIEW", "image", listOf(duzenleyici), "galeri")),
Ask("iki-kaldi", "galeri", resolve("VIEW", "image", listOf(duzenleyici, kamera), "galeri")),
)
for (ask in asks) {
ask.report()
}
tally(asks)
}
duruyor: istenen=galeri | verilen=galeri -> tam tek-kaldi: istenen=galeri | verilen=duzenleyici -> ikame iki-kaldi: istenen=galeri | bekledigi=kullanici-secimi -> bekleyen tam: 1 ikame: 1 bekleyen: 1 karsiliksiz: 0
Varsayılan duruyorsa tam. Varsayılan kalkıp geriye tek uygulama kalınca istek ikame edildi: intent hata vermedi, ama istenen uygulamaya gitmedi. Geriye iki uygulama kalınca kullanıcı yeniden sormaya geri döner: bekleyen. Aynı intent, aynı kod, kurulu uygulama kümesinin değişmesiyle üç farklı karşılama alabildi.
Sınırlayıcı Ölçüm
Aynı intent, aynı süzgeçler: yalnız kurulu uygulama sayısı değişir. Sıfır uygulama karşılıksız, bir uygulama tam, iki uygulama bekleyen.
// Main.kt — ayni intent, uc kurulu uygulama kumesi
fun main() {
val asks = listOf(
Ask("kurulu-yok", "galeri", resolve("VIEW", "image", emptyList(), null)),
Ask("tek-kurulu", "galeri", resolve("VIEW", "image", listOf(galeri), null)),
Ask("iki-kurulu", "galeri", resolve("VIEW", "image", listOf(galeri, duzenleyici), null)),
)
for (ask in asks) {
ask.report()
}
tally(asks)
println(asks.map { it.fulfillment })
}
kurulu-yok: istenen=galeri | neden=eslesme-yok -> karsiliksiz tek-kurulu: istenen=galeri | verilen=galeri -> tam iki-kurulu: istenen=galeri | bekledigi=kullanici-secimi -> bekleyen tam: 1 ikame: 0 bekleyen: 1 karsiliksiz: 1 [karsiliksiz, tam, bekleyen]
Üç istek, üç farklı karşılama. Bu dersin karşılaması bekleyendir, çünkü örtük intent’in sonucu intent’ten değil, aygıttaki kurulu uygulamalardan çıkar. Gönderen kod hangi sonucun geleceğini önceden bilemez; tek güvenilir yol resolveActivity ile bakmak ve her üç sonuca da hazır olmaktır.
UB5. Model uygulamaları aygıtta kurulu bir küme olarak kabul eder. Çok kullanıcılı profiller, iş profili ve uygulamanın etkin ya da devre dışı olması ölçülmedi.
Özet
- Bu dersin ölçütü: karşılama. Açık intent hedefini adıyla verir, örtük intent eylem, kategori ve veriyle eşleşme arar. Bu dersin karşılaması: bekleyen; örtük intent’in sonucu kurulu uygulamalara bağlıdır.
startActivityörtük intent’eDEFAULTkategorisini ekler; süzgeçte bu kategori yoksa eşleşme olmaz: karşılıksız.- Birden çok uygulama eşleşirse karşılama bekleyen kalır ve koşulu
kullanici-secimidir. Varsayılan seçiliyse tam. - Varsayılan uygulama kaldırılınca tek kalan uygulama ikame, iki kalan bekleyen sonucu verir.
- Aynı intent kurulu uygulama kümesine göre üç farklı karşılama alır: karşılıksız, tam, bekleyen.
Sonraki Adım
Intent ile bir activity başlatılınca platform onu bir göreve yerleştirir. Sıradaki ders, Görevler ve Geri Yığını, başlatma kiplerinin yeni bir örnek mi oluşturacağını yoksa var olanı mı kullanacağını ve geri yığınının her adımda nasıl göründüğünü ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.