Menyimpan status di aplikasi android

Hari ini saya ingin berbagi dengan Anda pendekatan lain untuk mempertahankan status ketika mengembangkan aplikasi android. Bukan rahasia bahwa aplikasi kita di latar belakang dapat dibunuh kapan saja dan masalah ini menjadi lebih mendesak dengan diperkenalkannya penghematan energi yang agresif - halo Oreo . Juga, tidak ada yang membatalkan perubahan konfigurasi pada ponsel: orientasi, perubahan bahasa, dll. Dan untuk membuka aplikasi dari latar belakang dan menampilkan antarmuka dalam keadaan terakhir, kita harus berhati-hati menyimpannya. Oh, onSaveInstanceState ini.

onSaveInstanceState

Betapa sakitnya dia membawa kita.

Saya akan memberikan contoh nanti menggunakan Clean Achitecture dan Dagger2 , jadi bersiaplah untuk ini :)

Masalah mempertahankan status tergantung pada tugas dapat diselesaikan dengan beberapa cara:

  1. Simpan data primer di onSaveInstanceState host (Aktivitas, Fragmen) - seperti pengidentifikasi halaman, pengguna, dan apa pun. Apa yang kita butuhkan untuk akuisisi data awal dan tampilan halaman.
  2. Simpan data yang diterima dalam program interaktif di repositori (SharedPreference, Database.
  3. Gunakan fragmen rethein untuk menyimpan dan mengembalikan data saat membuat ulang aktivitas.

Tetapi bagaimana jika kita perlu mengembalikan keadaan ui, serta reaksi antarmuka saat ini terhadap tindakan pengguna? Untuk kesederhanaan, mari kita pertimbangkan solusi untuk masalah ini menggunakan contoh nyata. Kami memiliki halaman login - pengguna memasukkan datanya, mengklik tombol dan kemudian dia menerima panggilan masuk. Aplikasi kita masuk ke latar belakang. Sistem membunuhnya. Kedengarannya menakutkan, bukan?)

Pengguna kembali ke aplikasi dan apa yang harus dilihatnya? Minimal, melanjutkan operasi login dan menunjukkan kemajuan. Jika aplikasi berhasil masuk sebelum memanggil metode onDestroy host, maka pengguna akan melihat navigasi ke layar mulai aplikasi. Perilaku ini dapat dengan mudah diselesaikan menggunakan mesin negara. Laporan yang sangat bagus dari Yandex . Dalam artikel yang sama saya akan mencoba membagikan pemikiran saya tentang laporan ini.

Sekarang sedikit kode:

Basestate

public interface BaseState<VIEW extends BaseView, OWNER extends BaseOwner> extends Parcelable{ /** * Get name * * @return name */ @NonNull String getName(); /** * Enter to state * * @param aView view */ void onEnter(@NonNull VIEW aView); /** * Exit from state */ void onExit(); /** * Return to next state */ void forward(); /** * Return to previous state */ void back(); /** * Invalidate view * * @param aView view */ void invalidateView(@NonNull VIEW aView); /** * Get owner * * @return owner */ @NonNull OWNER getOwner(); /** * Set owner * * @param aOwner owner */ void setOwner(@NonNull OWNER aOwner); } 

Baseoverner

 public interface BaseOwner<VIEW extends BaseView, STATE extends BaseState> extends BasePresenter<VIEW>{ /** * Set state * * @param aState state */ void setState(@NonNull STATE aState); } 

BasestateImpl

 public abstract class BaseStateImpl<VIEW extends BaseView, OWNER extends BaseOwner> implements BaseState<VIEW, OWNER>{ private OWNER mOwner; @NonNull @Override public String getName(){ return getClass().getName(); } @Override public void onEnter(@NonNull final VIEW aView){ Timber.d( getName()+" onEnter"); //depend from realization } @Override public void onExit(){ Timber.d(getName()+" onExit"); //depend from realization } @Override public void forward(){ Timber.d(getName()+" forward"); onExit(); //depend from realization } @Override public void back(){ Timber.d(getName()+" back"); onExit(); //depend from realization } @Override public void invalidateView(@NonNull final VIEW aView){ Timber.d(getName()+" invalidateView"); //depend from realization } @NonNull @Override public OWNER getOwner(){ return mOwner; } @Override public void setOwner(@NonNull final OWNER aOwner){ mOwner = aOwner; } 

Dalam kasus kami, pemilik negara akan menjadi presenter.

Melihat halaman login, tiga status unik dapat dibedakan:

LoginInitState , LoginProgressingState , LoginCompleteState .

Jadi, sekarang pertimbangkan apa yang terjadi di negara-negara ini.

LoginInitState kami memiliki validasi bidang dan jika validasi berhasil, tombol login menjadi aktif.

Permintaan Login dibuat di LoginProgressingState , token disimpan, permintaan tambahan dibuat untuk memulai aktivitas utama aplikasi.

LoginCompleteState menavigasi ke layar utama aplikasi.

Secara kondisional, transisi antar negara dapat ditampilkan dalam diagram berikut:

Diagram Status Login

Status LoginProgressingState keluar jika operasi login berhasil dalam keadaan LoginCompleteState , dan jika LoginInitState gagal . Dengan demikian, ketika pandangan kita dilepaskan, kita memiliki keadaan presenter yang sepenuhnya deterministik. Kita harus menyimpan keadaan ini menggunakan mekanisme android standar onSaveInstanceState . Agar kami dapat melakukan ini, semua status login harus mengimplementasikan antarmuka Parcelable . Oleh karena itu, kami memperluas BaseState antarmuka antarmuka kami .

Selanjutnya, kami memiliki pertanyaan, bagaimana cara meneruskan keadaan ini dari presenter ke host kami? Cara termudah adalah dengan meminta data kepada presenter, tetapi dari sudut pandang arsitektur, ini tidak terlihat bagus. Jadi simpanlah fragmen untuk membantu kami. Kami dapat membuat antarmuka untuk cache dan mengimplementasikannya dalam fragmen ini:

 public interface Cache{ /** * Save cache data * * @param aData data */ void saveCacheData(@Nullable Parcelable aData); @Nullable Parcelable getCacheData(); /** * Check that cache exist * * @return true if cache exist */ boolean isCacheExist(); } 

Selanjutnya, kami menyuntikkan fragmen cache ke konstruktor interaktor, seperti Cache. Kami menambahkan metode dalam integrator untuk mendapatkan dan menyimpan status dalam cache. Sekarang, dengan setiap perubahan dalam kondisi presenter, kita dapat menyimpan status di dalam interaktor, dan pada gilirannya interaktor menyimpan dalam cache. Semuanya menjadi sangat logis. Ketika host pertama kali dimuat, presenter menerima keadaan dari interaktor, yang pada gilirannya menerima data dari cache. Beginilah tampilan metode perubahan keadaan dalam presenter:

 @Override public void setState(@NonNull final LoginBaseState aState){ mState.onExit(); mState = aState; clearDisposables(); mState.setOwner(this); mState.onEnter(getView()); mInteractor.setState(mState); } 

Saya ingin mencatat hal ini - menyimpan data melalui cache dapat dilakukan untuk data apa pun, tidak hanya untuk negara bagian. Anda mungkin perlu membuat cache snippet unik Anda sendiri untuk menyimpan data saat ini. Artikel ini menjelaskan pendekatan umum. Saya juga ingin mencatat bahwa situasi yang dipermasalahkan sangat berlebihan. Dalam hidup Anda harus menyelesaikan masalah yang jauh lebih rumit. Misalnya, dalam aplikasi kami tiga halaman digabungkan: login, registrasi, pemulihan kata sandi. Dalam hal ini, diagram keadaan adalah sebagai berikut:

Sebutkan diagram dalam proyek nyata

Hasilnya, dengan menggunakan pola dan pendekatan keadaan yang dijelaskan dalam artikel, kami dapat membuat kode lebih mudah dibaca dan dipelihara. Dan yang penting adalah mengembalikan keadaan aplikasi saat ini.

Kode lengkap dapat dilihat di repositori .

Source: https://habr.com/ru/post/id420023/


All Articles