---
title: "Activity'ler"
source: 'https://academia.sh/tr/kurslar/android-platformu/activityler'
course: 'Android Platformu ve Araçlar'
language: tr
updated: '2026-10-21T05:02:35+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Activity'yi kullanıcıyla etkileşen tek ekranlık uygulama bileşeni olarak tanımlar"
    - 'Bildirim dosyasında bildirilmeyen activity isteğinin karşılıksız kaldığını ölçer'
    - "Başlatıcı süzgecinin hangi activity'nin açılacağını belirlediğini üç durumla gösterir"
    - "Dışa kapalı bir activity'nin çağıran uygulamaya göre iki farklı karşılama aldığını doğrular"
---

# Activity'ler

Activity'nin ne olduğu, bildirim dosyasında nasıl bildirildiği ve bildirilmemiş ya da dışa kapalı bir activity isteğinin platformca nasıl karşılandığının ölçümü.

Önceki derste sekiz isteğin biri karşılıksız kaldı: bildirim dosyasında adı geçmeyen bir activity hiç açılmadı. Bu ders o satırın arkasındaki düzeneği okur. Activity, kullanıcıyla etkileşen ve genellikle tek bir ekranı sahiplenen uygulama bileşenidir. Bir uygulama birden çok activity taşıyabilir: bir giriş ekranı, bir profil ekranı, bir ayarlar ekranı. Platform bunların hangisinin var olduğunu uygulamanın koduna bakarak değil, bildirim dosyasına bakarak öğrenir.

Dersin ölçütü yine **karşılama**dır: bir activity istendiğinde platform isteği nasıl karşılıyor? Aynı çekirdek kural çalışır. Verilen değer istenene eşitse tam, eşit değilse ikame, platform bir koşulu bekliyorsa bekleyen, reddediyorsa karşılıksızdır.

```kotlin
// 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 }}")
    }
}
```

## Bildirim Dosyasındaki Activity

Her activity bildirim dosyasında bir `<activity>` öğesiyle bildirilir. Platform bir activity'yi başlatmadan önce bu bildirime bakar. Bildirilmemişse çağrı bir istisnayla sona erer ve activity açılmaz. Başlatıcı ekranı da aynı dosyadan gelir: `MAIN` eylemini ve `LAUNCHER` kategorisini taşıyan bir intent süzgeci, activity'yi başlatıcıda görünür kılar.

```xml
<application>
    <activity android:name=".MainActivity" android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".ProfileActivity" android:exported="false" />
</application>
```

Kodda bir activity'yi başlatmak tek satırdır. Platform çağrıyı bildirim dosyasıyla karşılaştırır.

```kotlin
// Platform kodu: ayni uygulamadaki bir activity'yi acik intent ile baslatir
import android.content.Intent

fun openProfile() {
    startActivity(Intent(this, ProfileActivity::class.java))
}
```

Model, platformun bu kuralını kurar. Aşağıdaki ölçüm bildirilen üç activity ve istenen beş activity ile çalışır. `launchActivity` yalnız tek bir şeye bakar: istenen ad bildirilenler kümesinde var mı. Varsa istenen verilir, yoksa istek reddedilir ve neden `bildirimde-yok` olarak yazılır.

```kotlin
// Main.kt — bildirilen ve bildirilmeyen activity istekleri
fun launchActivity(target: String, declared: Set<String>): Outcome =
    if (target in declared) Outcome.Given(target) else Outcome.Refused("bildirimde-yok")

fun main() {
    val declared = setOf("MainActivity", "ProfileActivity", "DetailActivity")
    val asks = listOf(
        Ask("ana-ekran", "MainActivity", launchActivity("MainActivity", declared)),
        Ask("profil", "ProfileActivity", launchActivity("ProfileActivity", declared)),
        Ask("detay", "DetailActivity", launchActivity("DetailActivity", declared)),
        Ask("ayarlar", "SettingsActivity", launchActivity("SettingsActivity", declared)),
        Ask("yazim-hatasi", "ProfilActivity", launchActivity("ProfilActivity", declared)),
    )
    for (ask in asks) {
        ask.report()
    }
    tally(asks)
}
```

```text
ana-ekran: istenen=MainActivity | verilen=MainActivity -> tam
profil: istenen=ProfileActivity | verilen=ProfileActivity -> tam
detay: istenen=DetailActivity | verilen=DetailActivity -> tam
ayarlar: istenen=SettingsActivity | neden=bildirimde-yok -> karsiliksiz
yazim-hatasi: istenen=ProfilActivity | neden=bildirimde-yok -> karsiliksiz
tam: 3
ikame: 0
bekleyen: 0
karsiliksiz: 2
```

Beş istekten üçü tam, ikisi karşılıksız çıktı. Bildirilen üç activity istendiği gibi verildi. `SettingsActivity` sınıfı kodda var olabilir, ama bildirim dosyasında yazılı olmadığı için platform onu tanımaz. `ProfilActivity` ise bir yazım hatasıdır ve aynı yoldan reddedilir. İkisinin de karşılaması karşılıksızdır; ikame ya da bekleyen çıkmaz, çünkü platform "buna yakın bir activity açayım" ya da "bir koşul sağlansın" demez. Bildirilmemiş bir activity için bekleyecek bir koşul da yoktur.

**UB1.** Model gerçek çağrıdaki istisnayı fırlatmaz; reddi `Refused` olarak döndürür. Platformda başlatma çağrısı bir istisnayla sona erer, modelde aynı olay karşılıksız bir satır olur.

## Başlatıcı Süzgeci

Kullanıcı uygulama simgesine dokunduğunda hangi activity açılır? Cevabı kod vermez, bildirim dosyasındaki süzgeçler verir. `LAUNCHER` kategorisini taşıyan activity başlatıcının giriş noktasıdır. Bir uygulama geliştiricinin "ana ekran bu olsun" dediği activity ile başlatıcıda gerçekten açılan activity farklı olabilir: süzgeç yanlış activity'ye konmuşsa ya da hiçbirine konmamışsa karşılama değişir.

Aşağıdaki ölçümde istenen her seferinde `MainActivity`'dir; süzgeçlerin yeri değişir. `launcherEntry` kategorisinde `LAUNCHER` bulunan activity'leri ada göre sıralar ve ilkini verir. Hiçbiri yoksa isteği `baslatici-yok` nedeniyle reddeder.

```kotlin
// Main.kt — baslatici sugecinin yerine gore karsilama
fun launcherEntry(filters: Map<String, Set<String>>): Outcome {
    val entries = filters.filter { "LAUNCHER" in it.value }.keys.sorted()
    return if (entries.isEmpty()) Outcome.Refused("baslatici-yok") else Outcome.Given(entries.first())
}

fun main() {
    val onMain = mapOf("MainActivity" to setOf("MAIN", "LAUNCHER"), "ProfileActivity" to emptySet<String>())
    val none = mapOf("MainActivity" to setOf("MAIN"), "ProfileActivity" to emptySet<String>())
    val onProfile = mapOf("MainActivity" to setOf("MAIN"), "ProfileActivity" to setOf("MAIN", "LAUNCHER"))
    val asks = listOf(
        Ask("suzgec-main-uzerinde", "MainActivity", launcherEntry(onMain)),
        Ask("suzgec-yok", "MainActivity", launcherEntry(none)),
        Ask("suzgec-profil-uzerinde", "MainActivity", launcherEntry(onProfile)),
    )
    for (ask in asks) {
        ask.report()
    }
    tally(asks)
}
```

```text
suzgec-main-uzerinde: istenen=MainActivity | verilen=MainActivity -> tam
suzgec-yok: istenen=MainActivity | neden=baslatici-yok -> karsiliksiz
suzgec-profil-uzerinde: istenen=MainActivity | verilen=ProfileActivity -> ikame
tam: 1
ikame: 1
bekleyen: 0
karsiliksiz: 1
```

Aynı istek, süzgecin üç ayrı yerleşiminde üç ayrı karşılama aldı. Süzgeç `MainActivity` üzerindeyken istek tam karşılandı. Süzgeç hiçbir activity'de yokken uygulama başlatıcıda görünmez ve istek karşılıksız kaldı. Süzgeç `ProfileActivity` üzerindeyken başlatıcı bir activity açar, ama istenen değil, başkası; bu ikamedir. Üçüncü satır, geliştiricinin ana ekranı sandığı activity ile kullanıcının gördüğü activity'nin neden ayrışabildiğini gösterir.

**UB2.** Model süzgeçlerin yalnız kategorisine bakar. Gerçek süzgeç eylemi, kategoriyi ve veriyi birlikte eşler; bu eşleme Intent'ler ve Intent Süzgeçleri dersinde ölçülür.

## Çağırana Dönen Sonuç

Bir activity başka bir activity'nin isteğiyle açılabilir ve işi bitince çağırana bir sonuç döndürebilir. Seçici ekranlar, onay ekranları ve form ekranları bu biçimde çalışır. Çağıran taraf sonucu bir geri çağrıda alır; sonuç iki parçadan oluşur: bir sonuç kodu ve isteğe bağlı bir veri intent'i. Sonuç kodu iki hazır değerden biridir: `RESULT_OK` ya da `RESULT_CANCELED`. Kullanıcı geri tuşuna basarak activity'den çıkarsa platform, activity'nin hiçbir şey ayarlamamış olması hâlinde `RESULT_CANCELED` döndürür.

```kotlin
// Platform kodu: sonuc isteyen activity baslatma
import androidx.activity.result.contract.ActivityResultContracts

val pick = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == RESULT_OK) {
        showSelection(result.data)
    }
}
```

Çağıranın istediği şey bir seçimdir. Verilen ise kullanıcının ne yaptığına bağlıdır. Aşağıdaki ölçümde istenen her seferinde `tamam`dır. Kullanıcı kaydeder, geri tuşuna basar, activity çöker ya da kullanıcı henüz karar vermemiştir. `resultOf` bu dört durumu dört sonuca çevirir: kaydedilirse `tamam`, geri tuşuyla `iptal`, çökmede ret nedeni `activity-coktu`, karar yoksa `kullanici` beklenir.

```kotlin
// Main.kt — activity sonucunun dort hali
fun resultOf(event: String): Outcome = when (event) {
    "kaydet" -> Outcome.Given("tamam")
    "geri" -> Outcome.Given("iptal")
    "cokme" -> Outcome.Refused("activity-coktu")
    else -> Outcome.Waiting("kullanici")
}

fun main() {
    val events = listOf("kaydet", "geri", "cokme", "dusunuyor")
    val asks = events.map { Ask("sonuc-$it", "tamam", resultOf(it)) }
    for (ask in asks) {
        ask.report()
    }
    tally(asks)
}
```

```text
sonuc-kaydet: istenen=tamam | verilen=tamam -> tam
sonuc-geri: istenen=tamam | verilen=iptal -> ikame
sonuc-cokme: istenen=tamam | neden=activity-coktu -> karsiliksiz
sonuc-dusunuyor: istenen=tamam | bekledigi=kullanici -> bekleyen
tam: 1
ikame: 1
bekleyen: 1
karsiliksiz: 1
```

Dört durum dört karşılamayı birer kez üretti. Burada ders için asıl bilgi şudur: `iptal` bir hata değildir. Platform geri tuşuna basan kullanıcıya bir sonuç koduyla yanıt verir ve çağıran kod bunu normal bir yol olarak karşılamak zorundadır. Çağıranın yalnızca `RESULT_OK` dalını yazması, `ikame` satırını ele almayan bir kod demektir. Çöken activity ise çağırana bir sonuç bile döndürmez; çağıranın gördüğü şey "sonuç yok"tur ve bu `karsiliksiz` satırında modellenmiştir. `bekleyen` satırı gerçek bir sonuç değildir; kullanıcının henüz karar vermediğini söyler.

**UB3.** Model sonuç kodunu ve verilen değeri tek bir dizgede birleştirir. Gerçek platformdaki ayrık `resultCode` ve `data` ikilisi, ve sonucun süreç sonlanmasından sonra çağıran activity yeniden oluşturulduğunda teslim edilmesi ölçülmedi.

## exported Bildirimi Eksikse

Sınırlayıcı ölçümde `android:exported` değeri bir girdi olarak alınır. Gerçek kural bir adım daha içerir: bir activity bir intent süzgeci taşıyorsa `exported` değeri bildirimde açıkça yazılmak zorundadır. Süzgeç varsa ve değer yazılmamışsa, derleme aracı bildirim dosyasını reddeder ve uygulama yüklenemez. Süzgeç yoksa ve değer yazılmamışsa activity dışa kapalı sayılır.

Kuralın mantığı şudur: süzgeç taşıyan bir activity başkalarınca bulunmak için yazılmıştır, ama bu bulunabilirliğin gerçekten istenip istenmediği yazarın açık kararına bırakılır. Aşağıdaki ölçüm istenen şeyi `kurulum` olarak alır. Verilen, bildirimin kuralı geçip geçmediğidir.

```kotlin
// Main.kt — exported bildirimi ve suzgec
fun checkEntry(hasFilter: Boolean, exportedWritten: Boolean?): Outcome =
    if (hasFilter && exportedWritten == null) Outcome.Refused("exported-eksik")
    else Outcome.Given("kurulum")

fun main() {
    val asks = listOf(
        Ask("suzgec-var-exported-yok", "kurulum", checkEntry(true, null)),
        Ask("suzgec-var-exported-true", "kurulum", checkEntry(true, true)),
        Ask("suzgec-yok-exported-yok", "kurulum", checkEntry(false, null)),
        Ask("suzgec-yok-exported-false", "kurulum", checkEntry(false, false)),
    )
    for (ask in asks) {
        ask.report()
    }
    tally(asks)
}
```

```text
suzgec-var-exported-yok: istenen=kurulum | neden=exported-eksik -> karsiliksiz
suzgec-var-exported-true: istenen=kurulum | verilen=kurulum -> tam
suzgec-yok-exported-yok: istenen=kurulum | verilen=kurulum -> tam
suzgec-yok-exported-false: istenen=kurulum | verilen=kurulum -> tam
tam: 3
ikame: 0
bekleyen: 0
karsiliksiz: 1
```

Reddedilen tek durum, süzgeç taşıyıp `exported` değerini yazmayan activity'dir. Bu ret, uygulama çalışırken değil, kurulum ya da derleme anında gelir; bu yüzden hata, kullanıcının karşısına çıkmadan geliştiricinin önüne çıkar. Bu kuralın getirdiği alışkanlık: her süzgeçli activity için `exported` değerini bilerek yazmak. Başlatıcı activity'si `true` olmak zorundadır; çünkü başlatıcı uygulamanın dışındaki bir sistem bileşenidir.

**UB4.** Model, derleme aracının ve platformun bu kontrolü hangi sürümden itibaren yaptığını ayırt etmez. Kuralın uygulandığı en düşük hedef sürüm ve eski sürümlerdeki örtük varsayılan ölçülmedi.

## Sınırlayıcı Ölçüm

Karşılama kuralı nedeni görmez, ama nedenin değiştiği yer vardır: `android:exported`. Bu öznitelik activity'nin başka uygulamalardan çağrılıp çağrılamayacağını söyler. `false` ise yalnız aynı uygulamanın kodu activity'yi açabilir. Bildirilmiş ve dışa kapalı bir activity, çağıranın kim olduğuna göre iki farklı karşılama alır. Yani aynı activity, aynı bildirim, farklı çağıran: tam ile karşılıksız arasındaki fark çağıranın paketinden gelir.

Aşağıdaki ölçüm `openFrom` fonksiyonuyla bunu kurar. Activity bildirimde yoksa ret nedeni `bildirimde-yok`, dışa kapalıysa ve çağıran başka bir uygulamaysa `disa-kapali` olur. Bunların dışında istenen activity verilir.

```kotlin
// Main.kt — disa kapali activity ve cagiran uygulama
fun openFrom(target: String, exported: Map<String, Boolean>, caller: String, owner: String): Outcome {
    val isExported = exported[target] ?: return Outcome.Refused("bildirimde-yok")
    return if (!isExported && caller != owner) Outcome.Refused("disa-kapali") else Outcome.Given(target)
}

fun main() {
    val exported = mapOf("ProfileActivity" to false, "ShareActivity" to true)
    val owner = "app.ana"
    val asks = listOf(
        Ask("ayni-uygulama", "ProfileActivity", openFrom("ProfileActivity", exported, "app.ana", owner)),
        Ask("baska-uygulama", "ProfileActivity", openFrom("ProfileActivity", exported, "app.diger", owner)),
        Ask("disa-acik", "ShareActivity", openFrom("ShareActivity", exported, "app.diger", owner)),
        Ask("bildirilmemis", "SettingsActivity", openFrom("SettingsActivity", exported, "app.diger", owner)),
    )
    for (ask in asks) {
        ask.report()
    }
    println(asks.filter { it.fulfillment == "karsiliksiz" }.map { it.label })
}
```

```text
ayni-uygulama: istenen=ProfileActivity | verilen=ProfileActivity -> tam
baska-uygulama: istenen=ProfileActivity | neden=disa-kapali -> karsiliksiz
disa-acik: istenen=ShareActivity | verilen=ShareActivity -> tam
bildirilmemis: istenen=SettingsActivity | neden=bildirimde-yok -> karsiliksiz
[baska-uygulama, bildirilmemis]
```

`ayni-uygulama` ve `baska-uygulama` aynı activity'yi ister; ilki tam, ikincisi karşılıksızdır ve fark yalnız çağıranın paketidir. `disa-acik` dışa açık bir activity'yi başka uygulamadan ister ve tam karşılanır. Son satır, iki karşılıksız isteği tek listede toplar: ikisinin karşılaması aynı, nedenleri farklıdır. Birincisi bildirimde bulunan ama dışa kapalı bir activity'den, ikincisi bildirimde hiç bulunmayan bir activity'den gelir. Karşılama kategorisi bu ayrımı taşımaz; ayrım `neden=` alanındadır.

**UB5.** Model `exported` öznitelik değerini bir girdi olarak alır. Gerçek platformda varsayılan değer süzgecin bulunup bulunmamasına bağlıdır ve burada ölçülmedi.

## Özet

- Bu dersin ölçütü: **karşılama**. Activity, kullanıcıyla etkileşen uygulama bileşenidir ve platform onu bildirim dosyasındaki `<activity>` öğesinden tanır. Bu dersin karşılaması: **karşılıksız**; ölçülen istek bildirilmemiş ya da dışa kapalı activity'dir ve platform ona yakın bir activity açmaz.
- Bildirilmeyen bir activity isteği karşılıksız kalır; yazım hatası da aynı yoldan reddedilir. Beş istekte 3 tam ve 2 karşılıksız çıktı.
- Başlatıcı süzgecinin yeri karşılamayı belirler: `MainActivity` üzerinde tam, hiçbirinde karşılıksız, `ProfileActivity` üzerinde ikame.
- `android:exported="false"` olan activity aynı uygulamadan tam, başka uygulamadan karşılıksız karşılanır.
- İki karşılıksız istek farklı nedenlerden gelebilir; kural nedeni görmez, neden ayrı bir alanda tutulur.

## Sonraki Adım

Bildirilen bir activity açıldığında yaşamı orada bitmez: kullanıcı ekranı döndürür, başka uygulamaya geçer, geri döner. Sıradaki ders, `Activity Yaşam Döngüsü`, bu geçişlerde activity'nin durumunun nasıl korunduğunu ve nasıl kaybolduğunu aynı kuralla ölçer.
