Banyak operasi Android tidak boleh berjalan terus-menerus — polling lokasi hanya relevan saat UI terlihat, kamera harus dilepas saat Activity terhenti, dan observer database sebaiknya dilepas saat Activity di-destroy. Menulis callback ini secara manual pada onResume dan onPause berisiko lupa dan mengakibatkan leak. Jetpack menyediakan LifecycleObserver agar komponen dapat bereaksi otomatis terhadap perubahan state pemilik (Activity, Fragment, atau Service). Bab ini menjelaskan konsep ini dan menunjukkan implementasinya sebagai pola yang dapat diuji.
LifecycleOwner dan Lifecycle #
LifecycleOwner adalah interface yang dipakai oleh Activity, Fragment, dan Service modern untuk mengekspos objek Lifecycle. Objek Lifecycle memuat state saat ini (CREATED, STARTED, RESUMED, dst.) serta daftar observer. LifecycleObserver adalah interface marker — kita menandai method dengan @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) agar dipanggil saat state owner berubah.
class LocationObserver(private val context: Context) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun start() { /* mulai polling lokasi */ }
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun stop() { /* hentikan polling */ }
}
class MainActivity : AppCompatActivity() {
private val observer = LocationObserver(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(observer)
}
}Sejak Lifecycle 2.4, anotasi @OnLifecycleEvent sudah deprecated dan digantikan oleh DefaultLifecycleObserver yang menyediakan method onResume, onPause, dst. sebagai override default. Pendekatan ini lebih type-safe dan tidak memakai refleksi.
LifecycleService #
Selain Activity dan Fragment, Service juga dapat menjadi LifecycleOwner dengan memakai LifecycleService dari package androidx.lifecycle. Service yang mengikat diri ke lifecycle dapat dimonitor oleh observer, sehingga resource dilepas dengan benar saat Service dihentikan.
class PlayerService : LifecycleService() {
private val observer = PlayerObserver()
override fun onCreate() {
super.onCreate()
lifecycle.addObserver(observer)
}
}LifecycleService cocok untuk Service yang menjalankan operasi sekaligus ingin merespons onBind / onUnbind secara deklaratif.
Simulasi Lifecycle dan Observer #
Cuplikan berikut memodelkan Lifecycle, LifecycleOwner, dan LifecycleObserver sebagai state machine ringkas. LocationObserver mulai polling saat state RESUMED dan berhenti saat PAUSED:
// lifecycle_aware_components.kt
// Mensimulasikan LifecycleOwner, LifecycleObserver, dan @OnLifecycleEvent
// sebagai state machine ringkas. Komponen yang mengamati lifecycle akan
// mulai/berhenti sesuai state owner.
enum class LifecycleState {
INITIALIZED, CREATED, STARTED, RESUMED, PAUSED, STOPPED, DESTROYED
}
class Lifecycle {
var state: LifecycleState = LifecycleState.INITIALIZED
private set
private val observers = mutableListOf<LifecycleObserver>()
fun addObserver(observer: LifecycleObserver) {
observers.add(observer)
}
fun moveTo(newState: LifecycleState) {
val previous = state
state = newState
println("[Lifecycle] $previous -> $newState")
observers.forEach { it.onStateChanged(previous, newState) }
}
}
interface LifecycleObserver {
fun onStateChanged(previous: LifecycleState, current: LifecycleState)
}
// Komponen yang melakukan polling saat RESUMED, berhenti saat PAUSED
class LocationObserver : LifecycleObserver {
var active: Boolean = false
private set
override fun onStateChanged(previous: LifecycleState, current: LifecycleState) {
when (current) {
LifecycleState.RESUMED -> start()
LifecycleState.PAUSED -> stop()
else -> Unit
}
}
private fun start() { active = true; println(" LocationObserver: start polling") }
private fun stop() { active = false; println(" LocationObserver: stop polling") }
}
class LifecycleOwner(val nama: String) {
val lifecycle = Lifecycle()
fun onCreate() = lifecycle.moveTo(LifecycleState.CREATED)
fun onStart() = lifecycle.moveTo(LifecycleState.STARTED)
fun onResume() = lifecycle.moveTo(LifecycleState.RESUMED)
fun onPause() = lifecycle.moveTo(LifecycleState.PAUSED)
fun onStop() = lifecycle.moveTo(LifecycleState.STOPPED)
fun onDestroy() = lifecycle.moveTo(LifecycleState.DESTROYED)
}
fun main() {
val owner = LifecycleOwner("MainActivity")
val location = LocationObserver()
owner.lifecycle.addObserver(location)
println("=== Skenario normal: onCreate -> onStart -> onResume ===")
owner.onCreate()
owner.onStart()
owner.onResume()
println("\n=== Pengguna pindah ke layar lain: onPause ===")
owner.onPause()
println("Location aktif? ${location.active}")
println("\n=== Pengguna kembali: onResume ===")
owner.onResume()
println("Location aktif? ${location.active}")
println("\n=== Activity dihancurkan: onStop -> onDestroy ===")
owner.onStop()
owner.onDestroy()
}Output yang Dihasilkan:
=== Skenario normal: onCreate -> onStart -> onResume ===
[Lifecycle] INITIALIZED -> CREATED
[Lifecycle] CREATED -> STARTED
[Lifecycle] STARTED -> RESUMED
LocationObserver: start polling
=== Pengguna pindah ke layar lain: onPause ===
[Lifecycle] RESUMED -> PAUSED
LocationObserver: stop polling
Location aktif? false
=== Pengguna kembali: onResume ===
[Lifecycle] PAUSED -> RESUMED
LocationObserver: start polling
Location aktif? true
=== Activity dihancurkan: onStop -> onDestroy ===
[Lifecycle] RESUMED -> STOPPED
[Lifecycle] STOPPED -> DESTROYEDPerhatikan bahwa LocationObserver mulai polling saat owner masuk state RESUMED dan berhenti saat PAUSED. Pemilik tidak perlu tahu detail observer — observer bereaksi sendiri berdasarkan state. Pola ini mengurangi coupling antar Activity dan komponen.
Pola Pakai Umum #
| Komponen | Lifecycle event | Aksi |
|---|---|---|
| Location polling | ON_RESUME / ON_PAUSE | Start/stop update |
| Camera X | ON_START / ON_STOP | Bind/unbind use case |
| Database Flow | ON_START / ON_STOP | Collect/lepas subscription |
| Sensor accelerometer | ON_RESUME / ON_PAUSE | Register/unregister listener |
| BroadcastReceiver (dynamic) | ON_START / ON_STOP | registerReceiver/unregisterReceiver |
Coroutines dengan lifecycleScope #
Selain LifecycleObserver, library androidx.lifecycle:lifecycle-runtime-ktx menyediakan lifecycleScope — CoroutineScope yang otomatis dibatalkan saat Lifecycle di-destroy. Untuk membatasi coroutine pada saat lifecycle setidaknya STARTED, kita memakai repeatOnLifecycle(STARTED) { ... }:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect { state -> render(state) }
}
}Saat lifecycle turun ke STOPPED, repeatOnLifecycle membatalkan collector dan memulai ulang saat STARTED kembali. Pola ini mencegah pemrosesan UI saat aplikasi tidak terlihat.
Referensi Lanjutan #
- ViewModel
— rujukan komponen
ViewModelsebagai bagian inti dari pustaka Lifecycle-Aware - Guide to app architecture — panduan arsitektur Android yang memuat pola komponen sadar siklus hidup
Selanjutnya #
StateFlow yang dikeluarkan ViewModel adalah pilihan modern untuk state UI. Chapter StateFlow untuk UI akan membandingkannya dengan LiveData dan membahas pola single source of truth.