Menangani Siklus Proses dengan Komponen Berbasis Siklus Proses Bagian dari Android Jetpack.
Komponen berbasis siklus proses melakukan tindakan sebagai respons terhadap perubahan status siklus proses komponen lain, seperti aktivitas dan fragmen. Komponen-komponen ini membantu Anda menghasilkan kode yang lebih teratur dan sering kali lebih ringan, yang lebih mudah dipelihara.
Pola umumnya yaitu mengimplementasikan tindakan komponen dependen dalam metode siklus proses milik aktivitas dan fragmen. Namun, pola ini menyebabkan ketidakteraturan kode dan penyebaran error. Dengan menggunakan komponen berbasis siklus proses, Anda dapat memindahkan kode komponen dependen keluar dari metode siklus proses dan ke dalam komponen itu sendiri.
Paket androidx.lifecycle
menyediakan class dan antarmuka yang memungkinkan Anda membuat komponen
berbasis siklus proses—yaitu komponen yang dapat otomatis menyesuaikan
perilakunya berdasarkan status siklus proses saat ini dari suatu aktivitas atau fragmen.
Sebagian besar komponen aplikasi yang didefinisikan dalam Framework Android telah menyertakan siklus proses. Siklus proses dikelola oleh sistem operasi atau kode framework yang bekerja di proses Anda. Siklus proses adalah inti cara kerja Android, dan aplikasi Anda harus mengikutinya. Jika tidak dilakukan, mungkin terjadi kebocoran memori atau error pada aplikasi.
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 } }
Meskipun contoh ini terlihat benar, pada aplikasi yang sebenarnya, Anda akan mendapati terlalu banyak
panggilan yang mengelola UI dan komponen lain sebagai respons atas status saat ini
dari siklus proses tersebut. Mengelola banyak komponen menempatkan banyak
kode pada metode siklus proses, seperti onStart()
dan
onStop()
, yang membuatnya sulit dikelola.
Selain itu, tidak ada jaminan bahwa komponen akan mulai sebelum aktivitas atau
fragmen dihentikan. Ini terjadi terutama jika kita perlu menjalankan
operasi yang berjalan lama, seperti pemeriksaan konfigurasi di onStart()
. Hal ini dapat menyebabkan kondisi race saat 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.
Siklus Proses
Lifecycle
adalah class
yang menyimpan informasi tentang status siklus proses suatu komponen (seperti
aktivitas atau fragmen) dan memungkinkan objek lain untuk mengikuti status ini.
Lifecycle
menggunakan dua
enumerasi utama untuk melacak status siklus proses komponen teratribusinya:
- Peristiwa
- Peristiwa siklus proses 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
.
Bayangkan status sebagai node grafik dan peristiwa sebagai sudut node-node ini.
Class dapat memantau status siklus proses komponen dengan mengimplementasikan
DefaultLifecycleObserver
dan mengganti metode yang terkait seperti onCreate
, onStart
, dll.
Kemudian, Anda dapat menambahkan pengamat dengan memanggil metode
addObserver()
dari class Lifecycle
dan meneruskan instance pengamat, seperti yang ditunjukkan dalam contoh
berikut:
Kotlin
class MyObserver : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { connect() } override fun onPause(owner: LifecycleOwner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(MyObserver())
Java
public class MyObserver implements DefaultLifecycleObserver { @Override public void onResume(LifecycleOwner owner) { connect() } @Override public void onPause(LifecycleOwner owner) { disconnect() } } 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 proses dari 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 mengimplementasikan
antarmuka LifecycleOwner
.
Komponen yang mengimplementasikan
DefaultLifecycleObserver
bekerja lancar dengan komponen yang mengimplementasikan
LifecycleOwner
karena pemilik dapat menyediakan siklus proses, yang dapat didaftarkan oleh pengamat untuk
diamati.
Untuk contoh pelacakan lokasi, kita dapat membuat class MyLocationListener
mengimplementasikan DefaultLifecycleObserver
,
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 proses dideklarasikan di MyLocationListener
, bukan
di aktivitas. Mengizinkan setiap komponen untuk menyimpan logika mereka sendiri membuat
logika aktivitas dan fragmen lebih mudah dipelihara.
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 callback
tertentu jika Lifecycle
tidak sedang
berstatus baik. Misalnya, jika callback menjalankan transaksi fragmen setelah
status aktivitas disimpan, 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 ): DefaultLifecycleObserver { private var enabled = false override fun onStart(owner: LifecycleOwner) { if (enabled) { // connect } } fun enable() { enabled = true if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { // connect if not connected } } override fun onStop(owner: LifecycleOwner) { // disconnect if connected } }
Java
class MyLocationListener implements DefaultLifecycleObserver { private boolean enabled = false; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } @Override public void onStart(LifecycleOwner owner) { if (enabled) { // connect } } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @Override public void onStop(LifecycleOwner owner) { // disconnect if connected } }
Dengan implementasi ini, class LocationListener
menjadi sepenuhnya berbasis
siklus proses. Jika kita perlu menggunakan LocationListener
dari aktivitas atau fragmen
lain, kita hanya perlu menginisialisasinya. Semua operasi penyiapan dan pemutusan
dikelola oleh class-nya sendiri.
Jika library menyediakan kelas yang perlu bekerja dengan siklus proses Android, kami menyarankan agar Anda menggunakan komponen berbasis siklus proses. Klien library dapat mengintegrasikan komponen-komponen itu dengan mudah tanpa pengelolaan siklus proses secara manual di sisi klien.
Mengimplementasikan LifecycleOwner kustom
Fragmen dan Aktivitas pada Support Library 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; } }
Praktik terbaik untuk komponen berbasis siklus proses
- 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 ikuti objekLiveData
untuk mencerminkan perubahan ke tampilan. - Cobalah untuk menulis UI berbasis data,
yang tanggung jawab pengontrol UI-nya
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. Namun, perlu diperhatikan,ViewModel
tidak bertanggung jawab untuk mengambil data (misalnya, dari jaringan). Sebagai gantinya,ViewModel
harus memanggil komponen yang sesuai untuk mengambil data, kemudian menyediakan kembali hasilnya ke pengontrol UI. - Gunakan Data Binding untuk memelihara antarmuka yang rapi 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, gunakan library seperti Butter Knife untuk menghindari kode boilerplate dan menghasilkan abstraksi yang lebih baik.
- Jika UI Anda kompleks, pertimbangkan membuat kelas presenter untuk menangani modifikasi UI. Tugas ini bisa jadi melelahkan, tetapi hasilnya membuat komponen UI Anda lebih mudah diuji.
- Hindari mereferensikan konteks
View
atauActivity
dalamViewModel
Anda. JikaViewModel
aktif lebih lama dibandingkan aktivitas (dalam kasus perubahan konfigurasi), artinya aktivitas Anda bocor dan tidak dibuang dengan benar oleh pembersih sampah memori. - Gunakan coroutine Kotlin untuk mengelola tugas yang berjalan lama dan operasi lain yang berjalan secara asinkron.
Kasus penggunaan komponen berbasis siklus proses
Komponen siklus proses dapat mempermudah Anda dalam mengelola siklus proses dalam berbagai kasus. Beberapa contohnya adalah:
- Beralih antara update lokasi yang umum dan akurat. Gunakan
komponen berbasis siklus proses untuk mengaktifkan update lokasi akurat saat
aplikasi lokasi Anda terlihat dan beralih ke update umum saat aplikasi berada
di latar belakang.
LiveData
, komponen berbasis siklus proses, memungkinkan aplikasi Anda untuk otomatis mengupdate UI saat lokasi pengguna berubah. - Menghentikan atau memulai buffering video. Gunakan komponen berbasis siklus proses untuk segera memulai buffering video, namun menunda pemutaran hingga aplikasi sepenuhnya dimulai. Anda dapat juga menggunakan komponen berbasis siklus proses untuk menghentikan buffering saat aplikasi Anda dimusnahkan.
- Memulai dan menghentikan konektivitas jaringan. Gunakan komponen berbasis siklus proses untuk mengaktifkan update live (streaming) data jaringan saat aplikasi sedang digunakan dan juga secara otomatis berhenti sementara saat aplikasi berada di latar belakang.
- Menjeda dan melanjutkan animasi drawable. Gunakan komponen berbasis siklus proses untuk menangani penjedaan animasi drawable saat aplikasi berada di latar belakang dan melanjutkan drawable saat aplikasi kembali digunakan.
Menangani peristiwa penghentian
Saat Lifecycle
berada di AppCompatActivity
atau Fragment
, status Lifecycle
berubah menjadi
CREATED
dan
peristiwa ON_STOP
dikirim saat AppCompatActivity
atau
onSaveInstanceState()
dari Fragment
dipanggil.
Saat status Fragment
atau AppCompatActivity
disimpan melalui
onSaveInstanceState()
, UI-nya dianggap
tidak dapat diubah
hingga
ON_START
dipanggil. Mencoba memodifikasi UI setelah status disimpan dapat menyebabkan
inkonsistensi dalam status navigasi aplikasi Anda, dan karena alasan itulah FragmentManager
menampilkan pengecualian jika aplikasi menjalankan
FragmentTransaction
setelah status disimpan. Untuk
detailnya, lihat commit()
.
LiveData
mencegah kasus ekstrem ini secara mandiri dengan tidak memanggil
pengamatnya jika Lifecycle
teratribusi pengamat tersebut tidak berstatus setidaknya
STARTED
.
Di balik layar, objek ini
memanggil isAtLeast()
sebelum memutuskan untuk memanggil pengamatnya.
Sayangnya, metode onStop()
pada AppCompatActivity
dipanggil setelah
onSaveInstanceState()
,
dan ini menyisakan celah yang tidak memperbolehkan perubahan status UI,
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, meskipun peristiwanya tidak dikirimkan hingga onStop()
dipanggil oleh sistem.
Sayangnya, solusi ini menimbulkan dua masalah utama:
- Pada level API 23 atau yang lebih lama, sistem Android sebenarnya menyimpan status
aktivitas walaupun sebagian status aktivitas tersebut dilakukan oleh aktivitas lain. Dengan kata lain,
sistem Android memanggil
onSaveInstanceState()
, tetapi tidak selalu memanggilonStop()
. Hal ini berpotensi menimbulkan interval panjang, yang membuat pengamat berpikir bahwa siklus proses masih aktif walaupun status UI-nya tidak dapat diubah. - Class apa pun yang ingin memperlihatkan perilaku serupa ke class
LiveData
harus mengimplementasikan solusi yang disediakan olehLifecycle
versibeta 2
dan yang lebih lama.
Referensi lainnya
Untuk mempelajari lebih lanjut cara menangani siklus proses dengan komponen berbasis siklus proses, lihat referensi tambahan berikut.
Contoh
- Sunflower, sebuah aplikasi demo yang menunjukkan praktik terbaik dengan Komponen Arsitektur
Codelab
Blog
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Ringkasan LiveData
- Menggunakan coroutine Kotlin dengan komponen yang mendukung siklus proses
- Modul Status Tersimpan untuk ViewModel