İçeriğe geç
academia.sh

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ı. VIEW göstermek, SEND gö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’e DEFAULT kategorisini 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.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat