Menangani Siklus Hidup dengan Komponen Berbasis Siklus Hidup   Bagian dari Android Jetpack.

Komponen berbasis siklus hidup melakukan tindakan sebagai respons terhadap perubahan status siklus hidup komponen lain, seperti aktivitas dan fragmen. Komponen-komponen ini membantu Anda menghasilkan kode yang lebih rapi dan sering kali lebih ringan, yang lebih mudah dipelihara.

Pola umumnya yaitu mengimplementasikan tindakan komponen dependen dalam metode siklus hidup milik aktivitas dan fragmen. Akan tetapi, pola ini menyebabkan ketidakteraturan kode dan penyebaran error. Dengan menggunakan komponen berbasis siklus hidup, Anda dapat memindahkan kode komponen dependen keluar dari metode siklus hidup dan ke dalam komponen itu sendiri.

Paket androidx.lifecycle menyediakan class dan antarmuka yang memungkinkan Anda membuat komponen berbasis siklus hidup —yaitu komponen yang dapat otomatis menyesuaikan perilaku berdasarkan status siklus hidup saat ini untuk sebuah aktivitas atau fragmen.

Sebagian besar komponen aplikasi yang didefinisikan dalam Framework Android telah menyertakan siklus hidup. Siklus hidup dikelola oleh sistem operasi atau kode framework yang berjalan di proses Anda. Siklus hidup adalah inti cara kerja Android, dan aplikasi Anda harus mengikutinya. Tidak mengikutinya dapat memicu kebocoran memori atau bahkan menyebabkan aplikasi tidak bekerja.

Bayangkan saat kita memiliki aktivitas yang menampilkan lokasi perangkat di layar. Implementasi yang umum dilakukan adalah sebagai berikut:

Kotlin

    internal class MyLocationListener(
            private val context: Context,
            private val callback: (Location) -> Unit
    ) {

        fun start() {
            // connect to system location service
        }

        fun stop() {
            // disconnect from system location service
        }
    }

    class MyActivity : AppCompatActivity() {
        private lateinit var myLocationListener: MyLocationListener

        override fun onCreate(...) {
            myLocationListener = MyLocationListener(this) { location ->
                // update UI
            }
        }

        public override fun onStart() {
            super.onStart()
            myLocationListener.start()
            // manage other components that need to respond
            // to the activity lifecycle
        }

        public override fun onStop() {
            super.onStop()
            myLocationListener.stop()
            // manage other components that need to respond
            // to the activity lifecycle
        }
    }
    

Java

    class MyLocationListener {
        public MyLocationListener(Context context, Callback callback) {
            // ...
        }

        void start() {
            // connect to system location service
        }

        void stop() {
            // disconnect from system location service
        }
    }

    class MyActivity extends AppCompatActivity {
        private MyLocationListener myLocationListener;

        @Override
        public void onCreate(...) {
            myLocationListener = new MyLocationListener(this, (location) -> {
                // update UI
            });
        }

        @Override
        public void onStart() {
            super.onStart();
            myLocationListener.start();
            // manage other components that need to respond
            // to the activity lifecycle
        }

        @Override
        public void onStop() {
            super.onStop();
            myLocationListener.stop();
            // manage other components that need to respond
            // to the activity lifecycle
        }
    }
    

Meski contoh ini terlihat baik-baik saja, pada aplikasi yang sebenarnya, Anda akan mendapati terlalu banyak panggilan yang mengelola UI dan komponen lain sebagai respons atas status saat ini dari siklus hidup tersebut. Pengelolaan banyak komponen akan menempatkan banyak kode pada metode siklus hidup, seperti onStart() dan onStop(), sehingga sulit dikelola.

Selain itu, tidak ada jaminan bawa komponen akan dimulai sebelum aktivitas atau fragmen dihentikan. Ini terjadi terutama jika kita perlu menjalankan operasi yang berdurasi lama, seperti pemeriksaan konfigurasi di onStart(). Hal ini dapat menyebabkan kondisi race, di mana metode onStop() selesai sebelum onStart(), sehingga komponen aktif lebih lama dari yang dibutuhkan.

Kotlin

    class MyActivity : AppCompatActivity() {
        private lateinit var myLocationListener: MyLocationListener

        override fun onCreate(...) {
            myLocationListener = MyLocationListener(this) { location ->
                // update UI
            }
        }

        public override fun onStart() {
            super.onStart()
            Util.checkUserStatus { result ->
                // what if this callback is invoked AFTER activity is stopped?
                if (result) {
                    myLocationListener.start()
                }
            }
        }

        public override fun onStop() {
            super.onStop()
            myLocationListener.stop()
        }

    }
    

Java

    class MyActivity extends AppCompatActivity {
        private MyLocationListener myLocationListener;

        public void onCreate(...) {
            myLocationListener = new MyLocationListener(this, location -> {
                // update UI
            });
        }

        @Override
        public void onStart() {
            super.onStart();
            Util.checkUserStatus(result -> {
                // what if this callback is invoked AFTER activity is stopped?
                if (result) {
                    myLocationListener.start();
                }
            });
        }

        @Override
        public void onStop() {
            super.onStop();
            myLocationListener.stop();
        }
    }
    

Paket androidx.lifecycle menyediakan class dan antarmuka yang memudahkan Anda mengatasi masalah ini dengan cara yang andal dan terisolasi.

Lifecycle

Lifecycle adalah class yang menyimpan informasi tentang status siklus hidup suatu komponen (seperti aktivitas atau fragmen) dan memungkinkan objek lain untuk mengamati status ini.

Lifecycle menggunakan dua enumerasi utama untuk melacak status siklus hidup komponen terkaitnya:

Peristiwa
Peristiwa siklus hidup yang dikirim dari framework dan class Lifecycle. Peristiwa ini memetakan ke peristiwa callback dalam aktivitas dan fragmen.
Status
Status saat ini dari komponen yang dilacak oleh objek Lifecycle.
Diagram status siklus hidup
Gambar 1. Status dan peristiwa yang membentuk siklus hidup aktivitas

Bayangkan status sebagai node di grafik dan peristiwa sebagai sudut node-node ini.

Suatu class dapat mengamati status siklus hidup komponen dengan cara menambahkan anotasi ke metodenya. Lalu, Anda dapat menambahkan pengamat dengan memanggil metode addObserver() class Lifecycle dan meneruskan instance pengamat Anda, seperti yang ditunjukkan pada contoh berikut:

Kotlin

    class MyObserver : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        fun connectListener() {
            ...
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        fun disconnectListener() {
            ...
        }
    }

    myLifecycleOwner.getLifecycle().addObserver(MyObserver())
    

Java

    public class MyObserver implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        public void connectListener() {
            ...
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        public void disconnectListener() {
            ...
        }
    }

    myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
    

Pada contoh di atas, objek myLifecycleOwner mengimplementasikan antarmuka LifecycleOwner , seperti yang dijelaskan di bagian berikut.

LifecycleOwner

LifecycleOwner adalah antarmuka metode tunggal yang menunjukkan bahwa class memiliki Lifecycle. Ini memiliki satu metode, getLifecycle(), yang harus diimplementasikan oleh class tersebut. Jika Anda mencoba mengelola siklus hidup keseluruhan proses aplikasi, lihat ProcessLifecycleOwner.

Antarmuka ini memisahkan kepemilikan Lifecycle dari class individual seperti Fragment dan AppCompatActivity, dan memungkinkan penulisan komponen yang dapat digunakan dengan class individual tersebut. Semua class aplikasi kustom dapat mengimpelementasikan antarmuka LifecycleOwner .

Komponen yang mengimplementasikan LifecycleObserver berfungsi lancar dengan komponen yang mengimplementasikan LifecycleOwner karena pemilik dapat menyediakan siklus hidup, yang dapat didaftarkan pengamat untuk mengamati.

Untuk contoh pelacakan lokasi, kita dapat mengatur agar class MyLocationListener mengimplementasikan LifecycleObserver lalu menginisialisasinya dengan Lifecycle aktivitas pada metode onCreate(). Tindakan ini memungkinkan class MyLocationListener menjadi mandiri, yang berarti bahwa logika untuk bereaksi terhadap perubahan status siklus hidup dideklarasikan di MyLocationListener, bukan di aktivitas. Mengizinkan komponen individu untuk menyimpan logika mereka sendiri membuat logika aktivitas dan fragmen lebih mudah dikelola.

Kotlin

    class MyActivity : AppCompatActivity() {
        private lateinit var myLocationListener: MyLocationListener

        override fun onCreate(...) {
            myLocationListener = MyLocationListener(this, lifecycle) { location ->
                // update UI
            }
            Util.checkUserStatus { result ->
                if (result) {
                    myLocationListener.enable()
                }
            }
        }
    }
    

Java

    class MyActivity extends AppCompatActivity {
        private MyLocationListener myLocationListener;

        public void onCreate(...) {
            myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
                // update UI
            });
            Util.checkUserStatus(result -> {
                if (result) {
                    myLocationListener.enable();
                }
            });
      }
    }
    

Kasus penggunaan yang umum adalah menghindari pemanggilan callback tertentu jika Lifecycle tidak sedang berstatus baik. Misalnya, jika callback menjalankan transaksi fragmen setelah status aktivitas disimpan, maka callback ini akan memicu error, dan kita tidak ingin memicu callback itu lagi.

Untuk mempermudah kasus penggunaan ini, class Lifecycle mengizinkan objek lain mengkueri status saat ini.

Kotlin

    internal class MyLocationListener(
            private val context: Context,
            private val lifecycle: Lifecycle,
            private val callback: (Location) -> Unit
    ) {

        private var enabled = false

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun start() {
            if (enabled) {
                // connect
            }
        }

        fun enable() {
            enabled = true
            if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
                // connect if not connected
            }
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun stop() {
            // disconnect if connected
        }
    }
    

Java

    class MyLocationListener implements LifecycleObserver {
        private boolean enabled = false;
        public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
           ...
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        void start() {
            if (enabled) {
               // connect
            }
        }

        public void enable() {
            enabled = true;
            if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
                // connect if not connected
            }
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        void stop() {
            // disconnect if connected
        }
    }
    

Dengan implementasi ini, class LocationListener menjadi sepenuhnya berbasis siklus hidup. Jika kita perlu menggunakan LocationListener dari aktivitas atau fragmen lain, kita hanya perlu menginisialiasinya. Semua operasi penyiapan dan pemutusan dikelola oleh class-nya sendiri.

Jika library menyediakan class yang perlu berfungsi dengan siklus hidup Android, kami merekomendasikan agar Anda menggunakan komponen berbasis siklus hidup. Klien library dapat mengintegrasikan komponen-komponen itu dengan mudah tanpa pengelolaan siklus hidup secara manual di sisi klien.

Mengimplementasikan LifecycleOwner kustom

Fragmen dan Aktivitas pada Library Dukungan 26.1.0 dan yang lebih baru telah mengimplementasikan antarmuka LifecycleOwner .

Jika memiliki class kustom yang ingin dijadikan sebagai LifecycleOwner, Anda dapat menggunakan class LifecycleRegistry , tetapi Anda perlu meneruskan peristiwa ke class tersebut, seperti yang ditunjukkan dalam contoh kode berikut:

Kotlin

    class MyActivity : Activity(), LifecycleOwner {

        private lateinit var lifecycleRegistry: LifecycleRegistry

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            lifecycleRegistry = LifecycleRegistry(this)
            lifecycleRegistry.markState(Lifecycle.State.CREATED)
        }

        public override fun onStart() {
            super.onStart()
            lifecycleRegistry.markState(Lifecycle.State.STARTED)
        }

        override fun getLifecycle(): Lifecycle {
            return lifecycleRegistry
        }
    }
    

Java

    public class MyActivity extends Activity implements LifecycleOwner {
        private LifecycleRegistry lifecycleRegistry;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            lifecycleRegistry = new LifecycleRegistry(this);
            lifecycleRegistry.markState(Lifecycle.State.CREATED);
        }

        @Override
        public void onStart() {
            super.onStart();
            lifecycleRegistry.markState(Lifecycle.State.STARTED);
        }

        @NonNull
        @Override
        public Lifecycle getLifecycle() {
            return lifecycleRegistry;
        }
    }
    

Tips untuk komponen berbasis siklus hidup

  • Buatlah pengontrol UI (aktivitas dan fragmen) yang seramping mungkin. Pengontrol UI tidak boleh mencoba memperoleh datanya sendiri; sebagai gantinya, gunakan ViewModel untuk tujuan tersebut, dan amati objek LiveData untuk mencerminkan perubahan ke tampilan.
  • Cobalah untuk menulis UI berbasis data, di mana tanggung jawab pengontrol UI Anda adalah memperbarui tampilan saat terjadi perubahan data, atau memberitahukan kembali tindakan pengguna ke ViewModel.
  • Tempatkan logika data Anda di class ViewModel. ViewModel harus berfungsi sebagai penghubung antara pengontrol UI dan seluruh aplikasi Anda. Akan tetapi perlu diperhatikan, ViewModel tidak bertanggung jawab untuk mengambil data (misalnya dari jaringan). Sebaliknya, ViewModel harus memanggil komponen yang sesuai untuk mengambil data, kemudian menyediakan kembali hasilnya ke pengontrol UI.
  • Gunakan Data Binding untuk memelihara antarmuka yang bersih antara tampilan dan pengontrol UI. Tindakan ini memungkinkan Anda untuk membuat tampilan lebih deklaratif dan meminimalkan kode update yang perlu ditulis dalam aktivitas dan fragmen Anda. Jika Anda lebih nyaman melakukan hal ini menggunakan bahasa pemrograman Java, gunakanlah library seperti Butter Knife untuk menghindari kode boilerplate dan menghasilkan pemisahan yang lebih baik.
  • Jika UI Anda kompleks, pertimbangkan membuat class presenter untuk menangani modifikasi UI. Tugas ini bisa jadi melelahkan, tetapi hasilnya komponen UI Anda lebih mudah diuji.
  • indari mereferensikan konteks View atau Activity dalam ViewModel Anda. Jika ViewModel aktif lebih lama dibandingkan aktivitas (dalam kasus perubahan konfigurasi), artinya aktivitas Anda bocor dan tidak dibuang dengan benar oleh pengumpul sampah.
  • Gunakan coroutine Kotlin untuk mengelola tugas yang berjalan lama dan operasi lain yang berjalan secara asinkron.

Kasus penggunaan komponen berbasis siklus hidup

Komponen berbasis siklus hidup dapat mempermudah Anda dalam mengelola siklus hidup dalam berbagai kasus. Beberapa contohnya adalah:

  • Beralih antara pembaruan lokasi sementara dan akurat. Gunakan komponen berbasis siklus hidup untuk mengaktifkan pembaruan lokasi akurat saat aplikasi lokasi Anda terlihat dan beralih ke pembaruan lokasi sementara saat aplikasi berada di latar belakang. LiveData, komponen berbasis siklus hidup, memungkinkan aplikasi Anda untuk otomatis mengupdate UI saat lokasi pengguna berubah.
  • Menghentikan atau memulai buffering video. Gunakan komponen berbasis siklus hidup untuk segera memulai buffering video, tetapi menunda pemutaran hingga aplikasi sepenuhnya dimulai. Anda juga dapat menggunakan komponen berbasis siklus hidup untuk menghentikan buffering saat aplikasi dimusnahkan.
  • Memulai dan menghentikan konektivitas jaringan. Gunakan komponen berbasis siklus hidup untuk mengaktifkan update live (streaming) data jaringan saat aplikasi sedang digunakan di latar depan dan juga secara otomatis menjeda saat aplikasi berada di latar belakang.
  • Menjeda dan melanjutkan animasi yang dapat digambar. Gunakan komponen berbasis siklus hidup untuk menangani penjedaan animasi yang dapat digambar saat aplikasi berada di latar belakang dan melanjutkan gambar saat aplikasi kembali digunakan di latar depan.

Menangani peristiwa penghentian

Saat Lifecycle berada di AppCompatActivity atau Fragment, status Lifecycle berubah menjadi CREATED dan peristiwa ON_STOP dikirim saat AppCompatActivity atau onSaveInstanceState() Fragment dipanggil.

Saat status Fragment atau AppCompatActivity disimpan melalui onSaveInstanceState(), UI-nya dianggap statis hingga ON_START dipanggil. Jika Anda mencoba memodifikasi UI setelah status disimpan, inkonsistensi dapat terjadi pada status navigasi aplikasi Anda, dan karena itulah FragmentManager menampilkan pengecualian jika aplikasi menjalankan FragmentTransaction setelah status disimpan. Lihat commit() untuk detailnya.

LiveData mencegah kasus ekstrem ini secara mandiri dengan tidak memanggil pengamatnya jika Lifecycle yang terkait pengamat tersebut tidak berstatus setidaknya STARTED. Di balik layar, ini akan memanggil isAtLeast() sebelum memutuskan untuk memanggil pengamatnya.

Sayangnya, metode onStop() pada AppCompatActivity dipanggil setelah onSaveInstanceState(), dan ini menyisakan celah di mana perubahan status UI tidak diizinkan, tetapi Lifecycle belum dipindahkan ke status CREATED .

Untuk mencegah masalah ini, class Lifecycle pada versi beta2 dan yang lebih rendah menandai status ini sebagai CREATED tanpa mengirim peristiwanya, sehingga kode apa pun yang memeriksa status saat ini akan mendapatkan nilai sebenarnya meski peristiwanya sendiri tidak dikirimkan hingga onStop() dipanggil oleh sistem.

Sayangnya, solusi ini menimbulkan dua masalah utama:

  • Pada API level 23 atau lebih rendah, sistem Android sebenarnya menyimpan status aktivitas meski sebagian status aktivitas tersebut dilakukan oleh aktivitas lain. Dengan kata lain, sistem Android memanggil onSaveInstanceState() tetapi tidak selalu memanggil onStop(). Hal ini berpotensi menimbulkan interval panjang, yang membuat pengamat berpikir bahwa siklus hidup masih aktif meski status UI-nya tidak dapat diubah.
  • Class apa pun yang ingin memperlihatkan perilaku serupa ke class LiveData harus mengimplementasikan solusi yang disediakan oleh Lifecycle versi beta 2 dan yang lebih rendah.

Referensi lainnya

Untuk mempelajari lebih lanjut cara menangani siklus hidup dengan komponen berbasis siklus hidup, lihat referensi tambahan berikut.

Contoh

Codelab

Blog