原文:REACTIVE APPS WITH MODEL-VIEW-INTENT - PART6 - RESTORING STATE
做者:Hannes Dorfmann
譯者:卻把清梅嗅android
在前幾篇文章中,咱們討論了Model-View-Intent(MVI)
和單向數據流的重要性,這極大簡化了狀態的恢復,那麼其過程和原理是什麼呢,本文咱們針對這個問題進行探討。git
咱們將針對2個場景進行探討:github
Bundle
中獲取以前在Activity.onSaveInstanceState()
保存的狀態)這種狀況處理起來很是簡單。咱們只須要保持咱們的RxJava
流隨着時間的推移從Android
生命週期組件(即Activity
,Fragment
甚至ViewGroups
)種發射新的狀態。緩存
好比Mosby
的 MviBasePresenter
類在內部就使用了相似這樣的RxJava
的流:使用 PublishSubject 發射intent
,以及使用 BehaviorSubject 對View
進行渲染。對此,在 第二部分 中我已經闡述了是如何實現的。其主要思想是MviBasePresenter
是一個和View
生命週期隔離的組件,所以它可以被View
脫離和附着。在Mosby
中,當View
被永久銷燬時,Presenter
被destroyed
(垃圾收集)。一樣,這只是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)
去保存狀態。學習
與MVP
、MVVM
不一樣的是,在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 services
、RxJava
以及MVI
,敬請期待。
《使用MVI打造響應式APP》原文
《使用MVI打造響應式APP》譯文
《使用MVI打造響應式APP》實戰
Hello,我是卻把清梅嗅,若是您以爲文章對您有價值,歡迎 ❤️,也歡迎關注個人博客或者Github。
若是您以爲文章還差了那麼點東西,也請經過關注督促我寫出更好的文章——萬一哪天我進步了呢?