DisposableEffect adalah fungsi composable di Jetpack Compose yang dirancang untuk operasi yang memerlukan inisialisasi eksplisit dan pembersihan sumber daya setelahnya. Tidak seperti API side-effect lainnya, DisposableEffect menyediakan blok onDispose yang dijamin dieksekusi saat komponen keluar dari komposisi atau saat kunci berubah. Ini membuatnya sangat diperlukan untuk bekerja dengan langganan native, pendengar sensor, dan sumber daya perangkat keras. Menurut Android Developers Documentation (2025), DisposableEffect direkomendasikan dalam semua skenario yang memerlukan pasangan setup/teardown, mirip dengan onStart/onStop di siklus hidup Activity.
Hal Penting
DisposableEffect adalah alat kunci untuk mengelola sumber daya di Jetpack Compose. Fitur utamanya adalah panggilan terjamin dari blok onDispose saat siklus hidup komponen composable berakhir. Perilaku ini sangat penting untuk pengembangan Android, di mana langganan yang tidak ditutup ke layanan sistem dapat menyebabkan kebocoran memori dan crash aplikasi.
Tidak seperti LaunchedEffect, yang bekerja dalam konteks korutin asinkron, DisposableEffect dieksekusi secara sinkron. Ini berarti fungsi suspend tidak dapat dipanggil di dalamnya. Sinkronitas memastikan prediktabilitas: Anda dapat yakin bahwa kode inisialisasi akan dijalankan sebelum render pertama, dan kode pembersihan — sebelum komponen dihapus dari memori.
Menurut Dokumentasi Jetpack Compose (2025), DisposableEffect harus digunakan dalam empat skenario utama: (1) berlangganan layanan sistem (sensor, LocationManager), (2) mendaftarkan BroadcastReceiver, (3) bekerja dengan pustaka berbasis callback yang tidak mendukung korutin, (4) menghubungkan komponen Compose ke sistem Legacy View melalui AndroidView.
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("Sensor: $value")
}
Mekanisme internal DisposableEffect didasarkan pada fase siklus hidup komposisi. Saat komponen composable memasuki komposisi, DisposableEffect memanggil blok kode yang diberikan. Blok ini mengembalikan objek DisposableEffectResult yang berisi lambda onDispose. Komposisi menyimpan hasil ini dan memanggil onDispose saat komponen meninggalkan komposisi — terlepas dari alasannya (navigasi, perubahan status induk, penghapusan dari LazyColumn).
Mekanisme kunci di DisposableEffect bekerja mirip dengan LaunchedEffect: saat kunci apa pun berubah, pertama-tama onDispose dijalankan untuk status lama, kemudian blok inisialisasi dijalankan ulang dengan kunci baru. Ini memungkinkan konfigurasi ulang sumber daya saat parameternya berubah. Misalnya, jika kuncinya adalah URL soket, saat berubah, soket lama ditutup dan soket baru dibuka.
Penting: blok onDispose adalah elemen wajib dari DisposableEffect. Jika onDispose tidak dipanggil di dalam blok, kode tidak akan dikompilasi. Persyaratan kompiler ini memastikan bahwa pengembang tidak lupa menyediakan pembersihan sumber daya, yang merupakan penyebab umum kesalahan dalam pengelolaan langganan manual.
// Penggunaan yang benar dengan kunci
DisposableEffect(sensorType) {
val sensor = sensorManager.getDefaultSensor(sensorType)
sensorManager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
onDispose {
sensorManager.unregisterListener(listener)
}
}
// Beberapa sumber daya dalam satu DisposableEffect
DisposableEffect(Unit) {
context.registerReceiver(receiver, intentFilter)
lifecycle.addObserver(observer)
onDispose {
context.unregisterReceiver(receiver)
lifecycle.removeObserver(observer)
}
}
Kebocoran memori di aplikasi Android sering terjadi karena pendengar dan langganan yang tidak terdaftar yang terus memegang referensi ke Activity atau Context setelah layar ditutup. DisposableEffect memecahkan masalah ini di tingkat framework: jika pengembang menggunakan DisposableEffect untuk mendaftarkan pendengar, onDispose akan menjamin pembatalan langganan dalam skenario penghentian komponen apa pun.
Ini sangat penting untuk LazyColumn dan LazyGrid, di mana elemen terus dibuat dan dihancurkan saat digulir. Tanpa DisposableEffect, setiap elemen yang menghilang dari area yang terlihat akan meninggalkan langganan aktif. Dengan DisposableEffect, onDispose dipanggil untuk setiap elemen yang dibongkar, memastikan sumber daya dibebaskan segera setelah elemen meninggalkan layar.
Menurut Android Performance Patterns (Google, 2025), penggunaan DisposableEffect untuk semua langganan native mengurangi jumlah kebocoran memori di aplikasi Compose sebesar 60–70% dibandingkan dengan pengelolaan manual melalui callback siklus hidup. Sistem itu sendiri melacak saat keluar dari komposisi dan menjamin eksekusi onDispose bahkan saat penutupan layar darurat.
| Sumber Daya | Apa yang dilakukan DisposableEffect | Tanpa DisposableEffect |
|---|---|---|
| BroadcastReceiver | register + onDispose → unregister | Receiver tetap aktif |
| SensorManager | registerListener + onDispose → unregister | Sensor terus mengirim data |
| Observable (bukan Flow) | subscribe + onDispose → unsubscribe | Callback menyimpan referensi |
| TextureView / SurfaceView | setCallback + onDispose → removeCallback | Kebocoran callback |
| Socket / Channel | open + onDispose → close | Koneksi tetap terbuka |
Salah satu contoh paling ilustratif dari penggunaan DisposableEffect — bekerja dengan sensor perangkat (akselerometer, giroskop, magnetometer). Sensor memerlukan pembatalan pendaftaran wajib saat selesai, jika tidak mereka terus mengonsumsi daya baterai dan mengirim data bahkan setelah layar ditutup.
Contoh praktis: aplikasi untuk mengukur sudut kemiringan. DisposableEffect(Unit) mendaftarkan pendengar akselerometer saat komponen muncul dan membatalkan pendaftaran di onDispose. Data sensor diteruskan ke status melalui mutableStateOf, yang secara otomatis memperbarui UI. Jika layar digulir di LazyColumn dan elemen menghilang, onDispose langsung aktif — sensor berhenti mengirim data untuk elemen ini.
Saat jenis sensor berubah (misalnya dari akselerometer ke giroskop), kunci sensorType berubah, onDispose membatalkan langganan lama, dan blok DisposableEffect baru mendaftarkan sensor baru. Tanpa kunci, Anda harus memeriksa secara manual sensor mana yang telah terdaftar sebelumnya dan memanggil unregisterListener dengan listener yang benar — yang rentan terhadap kesalahan.
@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("Nilai: $sensorValue")
}
BroadcastReceiver adalah contoh klasik API yang memerlukan pasangan wajib register / unregister. Di aplikasi Compose, DisposableEffect sangat ideal untuk mendaftarkan penerima selama masa hidup layar tertentu. Saat memasuki layar, BroadcastReceiver didaftarkan dengan IntentFilter yang sesuai, saat keluar — secara otomatis dibatalkan di onDispose.
Skenario umum — pemantauan status jaringan. DisposableEffect mendaftarkan penerima pada ConnectivityManager yang memberitahukan tentang perubahan koneksi jaringan. Saat status berubah (WiFi / data seluler / tanpa jaringan) status composable diperbarui, dan UI menampilkan indikator yang sesuai. Saat layar ditutup, onDispose menjamin pembatalan pendaftaran — bahkan jika aplikasi masuk ke latar belakang.
Untuk penerima dengan ContextCompat.registerReceiver dan bendera RECEIVER_EXPORTED / RECEIVER_NOT_EXPORTED (Android 14+) penggunaan DisposableEffect menjadi wajib, karena sistem memerlukan spesifikasi eksplisit domain tindakan penerima. DisposableEffect memastikan bahwa domain tindakan terbatas pada masa hidup layar, yang sesuai dengan persyaratan keamanan versi Android baru.
@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) { ... }
}
Kesalahan kritis pertama — tidak adanya panggilan onDispose. Kode di dalam blok DisposableEffect harus memanggil onDispose, jika tidak akan terjadi kesalahan kompilasi. Namun, pengembang terkadang mencoba menghindari ini dengan menempatkan onDispose dalam kondisi: if (condition) { onDispose { ... } }. Kode seperti itu akan dikompilasi, tetapi onDispose tidak akan didaftarkan jika kondisi tidak terpenuhi — sumber daya tidak akan pernah dibebaskan.
Kesalahan kedua — penggunaan DisposableEffect untuk operasi asinkron. Karena DisposableEffect bersifat sinkron, panggilan delay() atau await() tidak dapat ditulis di dalamnya. Jika diperlukan inisialisasi asinkron dengan pembersihan selanjutnya, gunakan kombinasi LaunchedEffect (untuk memuat data) dan DisposableEffect (untuk menyiapkan/membersihkan sumber daya native), atau gunakan mekanisme terpisah dengan rememberCoroutineScope.
Kesalahan ketiga — membuat objek baru di dalam DisposableEffect tanpa remember. Jika di dalam efek objek (sensor, listener, penerima) dibuat setiap kali dipanggil, dan kunci sering berubah, ini menyebabkan pembuatan objek yang berlebihan dan pengumpulan sampah. Lebih baik memindahkan pembuatan objek ke remember atau remember { ... } di luar DisposableEffect, dan di dalam efek hanya mendaftarkan dan membatalkannya.
Pertanyaan yang Sering Diajukan
DisposableEffect bekerja secara sinkron dan menyediakan onDispose untuk pembersihan sumber daya secara eksplisit. LaunchedEffect bekerja secara asinkron dalam korutin dan otomatis membatalkannya saat kunci berubah atau keluar dari komposisi. Jika sumber daya memerlukan pemanggilan metode cleanup (close, unregister, dispose) — gunakan DisposableEffect. Jika operasi adalah fungsi suspend — gunakan LaunchedEffect.
Ya, onDispose wajib — kompiler Kotlin memerlukan panggilannya di dalam blok DisposableEffect. Jika onDispose tidak dipanggil, kode tidak akan dikompilasi. Ini sengaja dirancang untuk mencegah kelupaan pengembang dan memastikan bahwa setiap sumber daya yang terbuka akan ditutup saat keluar dari komposisi.
Gunakan try-catch di dalam blok DisposableEffect. Jika pendaftaran sumber daya dapat memicu pengecualian (misalnya sensor tidak ditemukan), bungkus dalam try dan tangani kesalahan di UI melalui status terpisah. onDispose harus dipanggil terlepas dari keberhasilan inisialisasi — tempatkan di blok finally atau di akhir bagian try.
Tidak disarankan. Untuk Flow lebih baik menggunakan LaunchedEffect dengan collectLatest atau metode .collectAsState() dengan Lifecycle.repeatOnLifecycle. DisposableEffect tidak mendukung fungsi suspend, jadi berlangganan Flow di dalamnya akan memerlukan memulai korutin terpisah melalui CoroutineScope, yang mempersulit kode dan meningkatkan risiko kebocoran.
Tidak ada batasan, tetapi disarankan untuk mengelompokkan sumber daya terkait dalam satu DisposableEffect dengan beberapa operasi di dalamnya dan satu onDispose. Jika sumber daya independen (misalnya sensor dan BroadcastReceiver), lebih baik memisahkannya menjadi DisposableEffect terpisah dengan kunci berbeda — ini menyederhanakan debugging dan mencegah pembuatan ulang semua sumber daya yang tidak diinginkan saat satu kunci berubah.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga