DisposableEffect — Jetpack Compose’da kaynak serbest bırakma

Yazar: IT Sectr Yayınlanma: 2026-06-30 Okuma süresi: 9 dk

DisposableEffect, Jetpack Compose’da açık başlatma ve ardından kaynak temizliği gerektiren işlemler için tasarlanmış bir composable işlevidir. Diğer side-effect API’lerinden farklı olarak DisposableEffect, bileşen kompozisyondan çıktığında veya anahtar değiştiğinde garanti edilen bir onDispose bloğu sağlar. Bu, onu yerel abonelikler, sensör dinleyicileri ve donanım kaynaklarıyla çalışmak için vazgeçilmez kılar. Android Developers Documentation (2025)’a göre DisposableEffect, Activity yaşam döngüsündeki onStart/onStop’a benzer şekilde setup/teardown çifti gerektiren tüm senaryolarda kullanılması önerilir.

Önemli Noktalar

  • DisposableEffect — kurulum ve garanti edilmiş kaynak temizliği için side-effect API’si.
  • onDispose — kompozisyondan çıkarken veya anahtar değiştiğinde yürütülen zorunlu blok.
  • Senkron — LaunchedEffect’in aksine DisposableEffect, coroutine olmadan senkron çalışır.
  • Temizlik — tipik senaryolar: LiveData aboneliğini iptal etme, soketleri kapatma, BroadcastReceiver kaydını iptal etme.
  • Anahtarlar — anahtar değiştiğinde, eski değer için onDispose yürütülür ve yenisiyle yeniden başlatma gerçekleşir.

Jetpack Compose’da DisposableEffect Nedir

DisposableEffect, Jetpack Compose’da kaynak yönetimi için önemli bir araçtır. Ana özelliği, composable bileşenin yaşam döngüsü sona erdiğinde onDispose bloğunun garanti edilerek çağrılmasıdır. Bu davranış, kapatılmamış sistem hizmeti aboneliklerinin bellek sızıntılarına ve uygulama çökmelerine yol açabileceği Android geliştirme için kritiktir.

Eşzamansız bir coroutine bağlamında çalışan LaunchedEffect’in aksine DisposableEffect senkron olarak yürütülür. Bu, içinde suspend işlevleri çağıramayacağınız anlamına gelir. Senkron yürütme öngörülebilirliği garanti eder: başlatma kodunun ilk işlemeden önce ve temizlik kodunun bileşen bellekten kaldırılmadan önce yürütüleceğinden emin olabilirsiniz.

Jetpack Compose Belgeleri (2025)’ne göre DisposableEffect dört ana senaryoda kullanılmalıdır: (1) sistem hizmetlerine (sensörler, LocationManager) abone olma, (2) BroadcastReceiver kaydı, (3) coroutine’leri desteklemeyen callback tabanlı kütüphanelerle çalışma, (4) Compose bileşenlerini AndroidView aracılığıyla eski View sistemlerine bağlama.

kotlin
class SensorManager(private val context: Context) {
    fun startListening(callback: (Float) -> Unit) { /* register */ }
    fun stopListening() { /* cancel */ }
}

@Composable
fun SensorDisplay() {
    val sensorManager = remember { SensorManager(context) }
    var value by remember { mutableStateOf(0f) }
    
    DisposableEffect(Unit) {
        sensorManager.startListening { value = it }
        onDispose { sensorManager.stopListening() }
    }
    
    Text("Sensör: $value")
}

DisposableEffect onDispose ile Nasıl Çalışır

DisposableEffect’in iç mekaniği, kompozisyon yaşam döngüsünün aşamalarına dayanır. Bir composable bileşen kompozisyona girdiğinde, DisposableEffect iletilen kod bloğunu yürütür. Bu blok, onDispose lambda’sını içeren bir DisposableEffectResult nesnesi döndürür. Kompozisyon bu sonucu kaydeder ve bileşen kompozisyondan ayrıldığında onDispose’u çağırır — nedeni ne olursa olsun (gezinme, üst durum değişikliği, LazyColumn’dan kaldırma).

DisposableEffect’teki anahtar mekanizması LaunchedEffect’e benzer şekilde çalışır: herhangi bir anahtar değiştiğinde, önce eski durum için onDispose yürütülür, ardından başlatma bloğu yeni anahtarlarla yeniden çalıştırılır. Bu, parametreleri değiştiğinde bir kaynağı yeniden yapılandırmanıza olanak tanır. Örneğin, anahtar bir soket URL’si ise, değiştiğinde eski soket kapatılır ve yenisi açılır.

Önemli: onDispose bloğu DisposableEffect’in zorunlu bir öğesidir. Blok içinde onDispose’u çağırmazsanız, kod derlenmez. Bu derleyici gereksinimi, geliştiricinin kaynak temizliğini sağlamayı unutmamasını garanti eder; bu, manuel abonelik yönetiminde sık görülen bir hata nedenidir.

kotlin
// Bir anahtarla doğru kullanım
DisposableEffect(sensorType) {
    val sensor = sensorManager.getDefaultSensor(sensorType)
    sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
    
    onDispose {
        sensorManager.unregisterListener(listener)
    }
}

// Tek DisposableEffect’te birden çok kaynak
DisposableEffect(Unit) {
    context.registerReceiver(receiver, intentFilter)
    lifecycle.addObserver(observer)
    
    onDispose {
        context.unregisterReceiver(receiver)
        lifecycle.removeObserver(observer)
    }
}

DisposableEffect ve Bellek Sızıntıları

Android uygulamalarındaki bellek sızıntıları genellikle, ekran kapatıldıktan sonra bir Activity veya Context’e referans tutmaya devam eden kayıtlı olmayan dinleyiciler ve abonelikler nedeniyle oluşur. DisposableEffect bu sorunu framework düzeyinde çözer: geliştirici bir dinleyiciyi kaydetmek için DisposableEffect kullanırsa, onDispose herhangi bir bileşen sonlandırma senaryosunda aboneliği iptal etmeyi garanti eder.

Bu, özellikle kullanıcı kaydırdıkça öğelerin sürekli oluşturulup yok edildiği LazyColumn ve LazyGrid için kritiktir. DisposableEffect olmadan, görünür alandan kaybolan her öğe aktif bir abonelik bırakır. DisposableEffect ile, kaldırılan her öğe için onDispose çağrılır ve öğe ekrandan ayrılır ayrılmaz kaynakların serbest bırakılması sağlanır.

Android Performance Patterns (Google, 2025)’a göre, tüm yerel abonelikler için DisposableEffect kullanımı, yaşam döngüsü callback’leri aracılığıyla manuel yönetime kıyasla Compose uygulamalarındaki bellek sızıntılarını %60–70 oranında azaltır. Sistem, bileşenin kompozisyondan ayrılma anını kendisi izler ve acil ekran kapatmada bile onDispose yürütülmesini garanti eder.

KaynakDisposableEffect’in YaptığıDisposableEffect Olmadan
BroadcastReceiverregister + onDispose → unregisterAlıcı aktif kalır
SensorManagerregisterListener + onDispose → unregisterListenerSensör veri göndermeye devam eder
Observable (Flow değil)subscribe + onDispose → unsubscribeCallback referansı tutar
TextureView / SurfaceViewsetCallback + onDispose → removeCallbackCallback sızıntısı
Socket / Channelopen + onDispose → closeBağlantı açık kalır

DisposableEffect ile Sensörlere Abone Olma

DisposableEffect kullanımının en açıklayıcı örneklerinden biri, cihaz sensörleriyle (ivmeölçer, jiroskop, manyetometre) çalışmaktır. Sensörler, iş tamamlandığında zorunlu kayıt iptali gerektirir; aksi takdirde ekran kapatıldıktan sonra bile pil gücü tüketmeye ve veri göndermeye devam ederler.

Pratik örnek: eğim açısı ölçüm uygulaması. DisposableEffect(Unit), bileşen göründüğünde bir ivmeölçer dinleyicisi kaydeder ve onDispose’ta kaydı iptal eder. Sensör verileri mutableStateOf aracılığıyla duruma iletilir ve bu da arayüzü otomatik olarak günceller. Ekran LazyColumn’da kaydırılırsa ve öğe kaybolursa, onDispose hemen tetiklenir — sensör o öğe için veri göndermeyi durdurur.

Sensör türü değiştirildiğinde (örneğin, ivmeölçerden jiroskopa), sensorType anahtarı değişir, onDispose eski aboneliği iptal eder ve yeni DisposableEffect bloğu yeni sensörü kaydeder. Anahtarlar olmadan, hangi sensörün daha önce kaydedildiğini manuel olarak kontrol etmeniz ve doğru dinleyiciyle unregisterListener çağırmanız gerekir — bu da hataya açıktır.

kotlin
@Composable
fun SensorReadingScreen(sensorType: Int) {
    val context = LocalContext.current
    val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    var sensorValue by remember { mutableStateOf(0f) }
    
    DisposableEffect(sensorType) {
        val sensor = sensorManager.getDefaultSensor(sensorType)
        val listener = SensorEventListener { event, _ ->
            sensorValue = event.values[0]
        }
        sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
        
        onDispose {
            sensorManager.unregisterListener(listener)
        }
    }
    
    Text("Değer: $sensorValue")
}

DisposableEffect ile BroadcastReceiver Kaydı

BroadcastReceiver, zorunlu bir register / unregister çifti gerektiren bir API’nin klasik bir örneğidir. Bir Compose uygulamasında DisposableEffect, belirli bir ekranın ömrü boyunca bir alıcıyı kaydetmek için idealdir. Ekrana girildiğinde, gerekli IntentFilter ile bir BroadcastReceiver kaydedilir; çıkıldığında, onDispose’ta otomatik olarak iptal edilir.

Tipik bir senaryo ağ durumu izlemedir. DisposableEffect, ağ bağlantısı değişikliklerini bildiren ConnectivityManager için bir alıcı kaydeder. Durum değiştiğinde (WiFi / mobil veri / ağ yok), composable durumu güncellenir ve arayüz ilgili göstergeyi görüntüler. Ekran kapatıldığında, onDispose kaydın iptal edilmesini garanti eder — uygulama arka plana gönderilse bile.

RECEIVER_EXPORTED / RECEIVER_NOT_EXPORTED bayrağıyla (Android 14+) ContextCompat.registerReceiver kullanan alıcılar için DisposableEffect kullanımı zorunlu hale gelir çünkü sistem, alıcının kapsamının açıkça belirtilmesini gerektirir. DisposableEffect, kapsamın ekranın ömrüyle sınırlı olmasını sağlar ve bu, daha yeni Android sürümlerinin güvenlik gereksinimleriyle uyumludur.

kotlin
@Composable
fun NetworkStatusBanner() {
    val context = LocalContext.current
    var isConnected by remember { mutableStateOf(true) }
    
    DisposableEffect(Unit) {
        val receiver = BroadcastReceiver { _, _ ->
            val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            isConnected = cm.getActiveNetwork() != null
        }
        IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).let { filter ->
            context.registerReceiver(receiver, filter)
        }
        
        onDispose {
            context.unregisterReceiver(receiver)
        }
    }
    
    if (!isConnected) { ... }
}

DisposableEffect ile Sık Yapılan Hatalar

İlk kritik hata onDispose çağrısının eksikliğidir. DisposableEffect bloğu içindeki kod onDispose’u çağırmalıdır, aksi takdirde derleme hatası oluşur. Ancak geliştiriciler bazen onDispose’u bir koşula koyarak bunu atlatmaya çalışır: if (condition) { onDispose { ... } }. Bu kod derlenecektir, ancak koşul karşılanmazsa onDispose kaydedilmez — kaynak asla serbest bırakılmaz.

İkinci hata DisposableEffect’i eşzamansız işlemler için kullanmaktır. DisposableEffect senkron olduğundan, içinde delay() veya await() çağrıları yazamazsınız. Ardından temizlik ile eşzamansız başlatma gerekiyorsa, LaunchedEffect (veri yükleme için) ve DisposableEffect (yerel kaynakları kurma/temizleme için) kombinasyonunu veya rememberCoroutineScope ile ayrı bir mekanizma kullanın.

Üçüncü hata remember olmadan DisposableEffect içinde yeni nesneler oluşturmaktır. Nesneler (sensör, dinleyici, alıcı) her çağrıda efekt içinde oluşturulur ve anahtarlar sık sık değişirse, bu aşırı nesne oluşturmaya ve çöp toplamaya yol açar. Nesne oluşturmayı DisposableEffect dışında remember veya remember { ... } içine taşımak ve efekt içinde yalnızca kaydetme ve iptal etme işlemlerini yapmak daha iyidir.

Sıkça Sorulan Sorular

DisposableEffect ve LaunchedEffect arasındaki fark nedir?

DisposableEffect senkron çalışır ve açık kaynak temizliği için onDispose sağlar. LaunchedEffect bir coroutine içinde eşzamansız çalışır ve anahtar değiştiğinde veya kompozisyondan çıkıldığında onu otomatik olarak iptal eder. Bir kaynak bir temizlik yöntemi (close, unregister, dispose) çağrısı gerektiriyorsa — DisposableEffect kullanın. İşlem bir suspend işleviyse — LaunchedEffect kullanın.

DisposableEffect’te onDispose bloğu zorunlu mu?

Evet, onDispose zorunludur — Kotlin derleyicisi, DisposableEffect bloğu içinde çağrılmasını gerektirir. onDispose’u çağırmazsanız, kod derlenmez. Bu, geliştiricilerin unutmasını önlemek ve açılan her kaynağın kompozisyondan çıkarken düzgün bir şekilde kapatılmasını sağlamak için kasıtlı olarak yapılmıştır.

DisposableEffect içinde hatalar nasıl ele alınır?

DisposableEffect bloğu içinde try-catch kullanın. Kaynak kaydı bir istisna fırlatabiliyorsa (örneğin, sensör bulunamadı), try ile sarın ve ayrı bir durum aracılığıyla arayüzde hatayı ele alın. onDispose, başlatmanın başarısından bağımsız olarak çağrılmalıdır — bir finally bloğuna veya try bölümünün sonuna koyun.

Flow’a abone olmak için DisposableEffect kullanabilir miyim?

Önerilmez. Flow için, collectLatest ile LaunchedEffect veya Lifecycle.repeatOnLifecycle ile .collectAsState() yöntemini kullanmak daha iyidir. DisposableEffect suspend işlevlerini desteklemez, bu nedenle içinde Flow’a abone olmak, CoroutineScope aracılığıyla ayrı bir coroutine başlatmayı gerektirir, bu da kodu karmaşıklaştırır ve sızıntı riskini artırır.

Bir composable’da kaç DisposableEffect olabilir?

Herhangi bir sınırlama yoktur, ancak ilgili kaynakların içinde birden çok işlem ve tek bir onDispose ile tek bir DisposableEffect’te gruplandırılması önerilir. Kaynaklar bağımsızsa (örneğin, sensör ve BroadcastReceiver), bunları farklı anahtarlarla ayrı DisposableEffect’lere bölmek daha iyidir — bu, hata ayıklamayı basitleştirir ve bir anahtar değiştiğinde tüm kaynakların istenmeyen şekilde yeniden oluşturulmasını önler.

Özet

  • DisposableEffect — onDispose aracılığıyla garantili temizlik ile senkron başlatma için Jetpack Compose side-effect API’si.
  • onDispose — kompozisyondan çıkarken veya anahtar değiştiğinde yürütülen, bellek sızıntılarını önleyen zorunlu blok.
  • Anahtarlar — bir anahtar değiştiğinde, önce eski değer için onDispose yürütülür, ardından yenisiyle yeniden başlatma gerçekleşir.
  • Senkron — DisposableEffect senkron çalışır; içinde suspend işlevler kullanılamaz.
  • Tipik senaryolar — BroadcastReceiver, sensörler, yerel dinleyiciler, callback tabanlı kütüphaneler, AndroidView entegrasyonu.
  • Sızıntılar — DisposableEffect, yaşam döngüsü callback’leriyle manuel yönetime kıyasla sızıntıları %60–70 oranında azaltır.
  • Hatalar — ana riskler: koşullu onDispose çağrısı, eşzamansız işlemler için kullanım, efekt içinde remember olmadan nesne oluşturma.

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.

Projeyi tartış

Ayrıca okuyun