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.

kotlin LifecycleObserver dengan @OnLifecycleEvent
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.

kotlin LifecycleService (ringkas)
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:

kotlin
// 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:

text
=== 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 -> DESTROYED

Perhatikan 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 #

KomponenLifecycle eventAksi
Location pollingON_RESUME / ON_PAUSEStart/stop update
Camera XON_START / ON_STOPBind/unbind use case
Database FlowON_START / ON_STOPCollect/lepas subscription
Sensor accelerometerON_RESUME / ON_PAUSERegister/unregister listener
BroadcastReceiver (dynamic)ON_START / ON_STOPregisterReceiver/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) { ... }:

kotlin repeatOnLifecycle untuk Flow
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 ViewModel sebagai 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.