[譯]使用MVI打造響應式APP(六):恢復狀態

原文:REACTIVE APPS WITH MODEL-VIEW-INTENT - PART6 - RESTORING STATE
做者:Hannes Dorfmann
譯者:卻把清梅嗅android

在前幾篇文章中,咱們討論了Model-View-Intent(MVI)和單向數據流的重要性,這極大簡化了狀態的恢復,那麼其過程和原理是什麼呢,本文咱們針對這個問題進行探討。git

咱們將針對2個場景進行探討:github

  • 在內存中恢復狀態(好比當屏幕方向發生改變)
  • 持久化恢復狀態(好比從Bundle中獲取以前在Activity.onSaveInstanceState()保存的狀態)

內存中

這種狀況處理起來很是簡單。咱們只須要保持咱們的RxJava流隨着時間的推移從Android生命週期組件(即ActivityFragment甚至ViewGroups)種發射新的狀態。緩存

好比MosbyMviBasePresenter 類在內部就使用了相似這樣的RxJava的流:使用 PublishSubject 發射intent,以及使用 BehaviorSubjectView進行渲染。對此,在 第二部分 中我已經闡述了是如何實現的。其主要思想是MviBasePresenter是一個和View生命週期隔離的組件,所以它可以被View脫離和附着。在Mosby中,當View被永久銷燬時,Presenterdestroyed(垃圾收集)。一樣,這只是Mosby的一個實現細節,您的MVI實現可能徹底不一樣。網絡

重要的是,像Presenter這樣的組件存活在View的生命週期以外,由於這樣很容易處理View脫離和附着的事件。每當View(從新)依附到Presenter時,咱們只需調用view.render(previousState)(所以Mosby內部使用了BehaviorSubject)。app

這只是處理屏幕方向改變的一種處理方案,它一樣適用於返回棧導航中。例如,Fragment在返回棧中,咱們若是從返回棧中返回,咱們能夠簡單的再次調用view.render(previousState),而且,view也會顯示正確的狀態。ide

事實上,即便沒有View對其進行依附,狀態也依然會被更新,由於Presenter存活在View的生命週期以外,並被保存在RxJava流中。設想若是沒有View附着,則會收到一個更改數據(部分狀態)的推送通知,一樣,每當View從新附着時,最新狀態(包含來自推送通知的更新數據)將被移交給View進行渲染。函數

持久化狀態

這種場景在MVI這種單向數據流模式下也很簡單。如今咱們但願View層的狀態不只僅存在於內存中,即便進程終止也可以對其持有。Android中一般的一種解決方案是經過調用Activity.onSaveInstanceState(Bundle)去保存狀態。學習

MVPMVVM不一樣的是,在MVI中你持有了表明狀態的Model,View有一個render(state)方法來記錄最新的狀態,這讓持有最後一個狀態變得簡單。所以,顯然易見的是打包和存儲狀態到一個bundle下面,而且以後恢復它:spa

class MyActivity extends Activity implements MyView {

  private final static KEY_STATE = "MyStateKey";
  private MyViewState lastState;

  @Override
  public void render(MyState state) {
    lastState = state;
    ... // 更新UI控件
  }

  @Override
  public void onSaveInstanceState(Bundle out){
    out.putParcelable(KEY_STATE, lastState);
  }

  @Override
  public void onCreate(Bundle saved){
    super.onCreate(saved);
    MyViewState initialState = null;
    if (saved != null){
      initialState = saved.getParcelable(KEY_STATE);
    }

    presenter = new MyPresenter( new MyStateReducer(initialState) ); // With dagger: new MyDaggerModule(initialState)
  }
  ...
}
複製代碼

我想你已得要領,請注意,在onCreate()中咱們並不直接調用view.render(initialState), 咱們讓初始狀態的邏輯下沉到狀態管理的地方: 狀態摺疊器(請參考第三部分),咱們將它與.scan(initialState,reducerFunction)搭配使用。

結語

與其餘模式相比,使用單向數據流和表示狀態的Model,許多與狀態相關的東西更容易實現。可是,我一般不會在個人App中將狀態持久化,兩個緣由:首先,Bundle有大小限制,所以你不能將任意大的狀態放入bundle中(或者你能夠將狀態保存到文件或像Realm這樣的對象存儲中);其次,咱們只討論瞭如何序列化和反序列化狀態,但這不必定與恢復狀態相同。

例如:假設咱們有一個LCE(加載內容錯誤)視圖,它會在加載數據時顯示一個指示器,並在完成加載後顯示條目列表,所以狀態就相似MyViewState.LOADING。讓咱們假設加載須要一些時間,而就在此時進程恰好被終止了(好比忽然一個電話打了進來,致使電話應用佔據了前臺)。

若是咱們僅僅將MyViewState.LOADING進行序列化並在以後進行反序列化操做對狀態進行恢復,咱們的狀態摺疊器會調用view.render(MyViewState.LOADING),目前爲止這是正確的,但實際上咱們 永遠不會經過這個狀態對網絡進行請求加載數據

如您所見,序列化和反序列化狀態與狀態恢復不一樣,這可能須要一些額外的步驟來增長複雜性(固然對於MVI來講這實現起來一樣比其它模式更簡單),當從新建立View時,包含某些數據的反序列化狀態可能會過期,所以您可能必須刷新(加載數據)。

在我研究過的大多數應用程序中,我發現相比之下這種方案更簡單且友好:即,將狀態僅僅保存在內存中,而且在進程死亡後以空的初始狀態啓動,好像應用程序將首次啓動同樣。理想狀況下,App具備對緩存和離線的支持,所以在進程終止後加載數據的速度也會很快。

這最終致使了我和其它Android開發者針對一個問題進行了激烈的辯論:

若是我使用緩存或存儲,我已經擁有了一個存活於Android組件生命週期以外的組件,並且我再也不須要去處理相關的狀態存儲問題,而且MVI毫無心義,對嗎?

這其中大多數Android開發者推薦 Mike Nakhimovich 發表的 《Presenter 不是爲了持久化》這篇文章介紹的 NyTimes Store,這是一個數據加載和緩存庫。遺憾的是,那些開發人員不明白 加載數據和緩存不是狀態管理。例如,若是我必須從緩存或存儲中加載數據呢?

最後,相似NyTimes Store的庫幫助咱們處理進程終止了嗎?顯然沒有,由於進程隨時都有可能被終止。咱們能作的僅僅是祈禱Android操做系統不要殺死咱們的進程,由於咱們還有一些須要經過Service作的事(這也是一個可以不生存於其它android組件生命週期的組件),或者咱們能夠經過使用RxJava而再也不須要Android Service了,這可行嗎?

咱們將在下一章節探討關於android servicesRxJava以及MVI,敬請期待。

劇透:我認爲咱們確實須要服務。

系列目錄

《使用MVI打造響應式APP》原文

《使用MVI打造響應式APP》譯文

《使用MVI打造響應式APP》實戰


關於我

Hello,我是卻把清梅嗅,若是您以爲文章對您有價值,歡迎 ❤️,也歡迎關注個人博客或者Github

若是您以爲文章還差了那麼點東西,也請經過關注督促我寫出更好的文章——萬一哪天我進步了呢?

相關文章
相關標籤/搜索