淺談ViewModel
1 主要功能
-
Activity、Fragment 存活期間 的資料儲存;
-
bind同一Activity 的多個Fragment間資料共享;
-
獨立或與LiveData配合實現程式碼解耦;
2 使用方法
1) 引入ViewModel
-
在build.gradle中新增編譯lifecycle.extensions module,該module同時包含ViewModel和LiveData:
compile('android.arch.lifecycle:extensions:1.0.0')
由於lifecycle.extensions內部依賴26.1.0版本的support_v7包,建議將工程中已引用的support_v7元件版本也升級到26.1.0或以上。
2) 構造資料物件
-
自定義ViewModel類,繼承ViewModel;
-
在自定義的ViewModel類中新增需要的資料物件;
public class DemoViewModel extends ViewModel { final public DemoData mData = new DemoData(); public DemoViewModel() {} }
3) 獲取資料
-
透過ViewModelProviders和Activity / Fragment獲取ViewModelProvider;
-
透過ViewModelProvider和自定義ViewModel類獲取自定義ViewModel物件;
-
從自定義ViewModel物件獲取資料物件,進行需要的讀寫操作。
DemoData data = ViewModelProviders.of(getActivity()) .get(DemoViewModel.class) .mData; data.doSth();
3 使用場景示範
1) 單Activity多Fragment間資料維護
-
需求點:
-
bind同一個Activity的Fragment A、B;
-
Fragment A、B間存在跳轉關係;
-
Fragment A、B共同維護一些資料,且A、B均有讀取、修改的業務需求;
-
傳統實現方法1:Intent +onFragmentResult()
大致流程: -
跳轉時需要將資料按k-v封裝入Bundle,或者將資料Parcelable傳遞給下個頁面;
-
下個頁面修改資料並返回後,需要從onFragmentResult()的Intent解析並同步資料;
-
Intent傳遞的資料大小總量不能超過1M;
麻煩且資料大小有限制。
-
傳統實現方法2:資料物件SingleInstance、static
缺點: -
資料的生命週期要自行控制;
-
容易記憶體洩漏;
同樣麻煩,且有一定風險。
ViewModel同時規避了傳統方法的缺點:
-
bind同一個Activity的Fragments均可以透過ViewModelProvider獲取共同的資料物件,無需主動進行資料傳遞;
-
脫離Intent、Bundle、Parcelable這些用起來很麻煩的控制元件;
-
資料生命週期由ViewModel內部掌控,無需手動管理銷燬;
2) 與LiveData配合實現UI、業務邏輯分層
同為官方架構元件的LiveData,功能是監聽資料變化,並回撥給註冊的observer。ViewModel+LiveData可以很方便的抽象出資料層和業務層,快速解耦。
下面的Demo來自官方案例。透過Demo,以及LiveData、ViewModel同處一個module,可以看出官方非常建議兩者搭配使用。再配合以往的Data-Binding,可以快速搭建起一套簡易的MVVM業務體系。
public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if(users == null) { users= new MutableLiveData<List<Users>>(); loadUsers(); } return users; } private void loadUsers() { //Do an asyncronous operation to fetch users. } } public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { //Create a ViewModel the first time the system calls an activity's onCreate()method. //Re-created activities receive the same MyViewModel instance created by thefirst activity. MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class); model.getUsers().observe(this,users -> { //update UI }); } }
4 生命週期特性
ViewModel的生命週期與Lifecycle同步,當Activity /Fragment超出Lifecycle範圍(並不是onDestroy()回撥),ViewModel連同其包含的資料一起被銷燬了。具體有多大呢,按照官方說法:
in the case of an activity, when it finishes, whilein the case of a fragment, when it’s detached.
Activity退棧後(Fragment則是與Activity解除關係後),ViewModel就解除引用關係,準備被系統回收了。下面這張圖片更直觀的展示了ViewModel的生命週期:
5 原始碼分析
帶著兩個小問題簡單的進行下原始碼分析:
1) ViewModel與Activity / Fragment的對映關係是如何建立起來的?
(以Activity為例)
第一部分:獲取ViewModelProvider
-
呼叫ViewModelProviders.of(Activity);
-
一路呼叫,最終走到HolderFragmentManager.holderFragmentFor(Activity);
-
HolderFragmentManager依次從FragmentManager和HolderFragmentManager持有的HashMap查詢:該Activity是否有HolderFragment與之對映 (HolderFragment的作用下面一個問題將談到) ;
HolderFragment.class HolderFragment holderFragmentFor(FragmentActivity activity) { FragmentManagerfm = activity.getSupportFragmentManager(); HolderFragment holder = findHolderFragment(fm); if(holder != null) { return holder; }else { holder = (HolderFragment)this.mNotCommittedActivityHolders.get(activity); if(holder != null) { return holder; }else { //建立HolderFragment
-
有則返回該HolderFragment;沒有則建立一個HolderFragment:
-
新建一個HolderFragment,commit給FragmentManager,並存入HashMap以標記為not committed狀態,防止重複commit;
-
向Application註冊對該Activity的生命週期監聽。如果HolderFragment尚未create,Activity就已經銷燬,則從HashMap中移除該Activity,防止洩露;
-
HolderFragment成功建立後,從HashMap中移除該Activity;
使用HolderFragment持有的ViewModelStore物件完成ViewModelProvider的建立;
第一部分的職責是構建 / 查詢HolderFragment,對構建過程中的異常做保護,最後返回ViewModelProvider。HolderFragment和ViewModelProvider共同持有的ViewModelStore將成為第二部分的核心;
(第二部分:獲取ViewModel)
-
呼叫ViewModelProvider.get(ViewModel.class);
-
透過ViewModel的規範名(canonical name),從HashMap中查詢是否已經存在該ViewModel的例項。有則返回;沒有則透過反射構建一個,並存入HashMap;
ViewModelStore.class public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap();
第二部分職責是對映,透過類名從HashMap查詢ViewModel例項。整個對映邏輯也可以簡化為:透過Activity類名找ViewModel例項;
2) ViewModel的生命週期如何管理?
ViewModel objects are scoped to the Lifecycle passed to the ViewModelProvider when getting the ViewModel.
看到官方文件中的這句話,心想ViewModel莫非是透過同為官方架構元件的Lifecycle來管理的生命週期的?
其實並沒有那麼複雜,ViewModel在第一次呼叫ViewModelProvider.get(ViewModel.class)建立;而銷燬就需要靠HolderFragment自己的onDestroy()回撥:
HolderFragment.class public void onDestroy() { super.onDestroy(); this.mViewModelStore.clear(); }
ViewModelStore.class public final void clear() { Iteratorvar1 = this.mMap.values().iterator(); while(var1.hasNext()){ ViewModel vm = (ViewModel)var1.next(); vm.onCleared(); } this.mMap.clear(); }
HolderFragment銷燬後,呼叫ViewModelStore.clear(),清理HashMap對ViewModel物件的引用,待系統GC回收ViewModel。這也解釋了建立ViewModelProvider時為什麼需要HolderFragment配合,HolderFragment掌控了ViewModel的生命週期。
6 Last but not least
簡述下官方架構中各元件的主要職責:
-
Lifecycle:將Activity /Fragment生命週期回撥事件向外丟擲;
-
LiveData:在Lifecycle範圍內監聽資料的變化;
-
ViewModel:在Lifecycle範圍記憶體儲和共享資料;
-
Room:簡化Db操作;
除了Room,可以感受到官方在盡力把大家從最初的MVC往MVVM引導,更加強大的官方元件將使 UI-業務-資料 的抽象過程變得更加簡單順滑。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31557897/viewspace-2219672/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 淺談 ViewModel 的生命週期控制View
- 淺淺談ReduxRedux
- 淺淺淺談JavaScript作用域JavaScript
- 淺談 PromisePromise
- 淺談mockMock
- 淺談PWA
- 淺談Disruptor
- 淺談反射反射
- 淺談vuexVue
- ElasticSearch淺談Elasticsearch
- 淺談NginxNginx
- 淺談promisePromise
- 淺談visibility
- 淺談flutterFlutter
- 淺談JMM
- Celery淺談
- 淺談JavaScriptJavaScript
- 淺談IHttpHandlerHTTP
- 淺談HTTPSHTTP
- 淺談SYNPROXY
- 淺談WebSocketWeb
- 淺談HTMLHTML
- ZooKeeper淺談
- ElasticJob淺談AST
- 淺談RMQMQ
- 淺談ThinkPHP 5.0PHP
- 淺談event loopOOP
- iOS 淺談 RunloopiOSOOP
- 淺談 Trait 類AI
- React淺談setStateReact
- 淺談React HooksReactHook
- 淺談Spring框架Spring框架
- 淺談TCP/IPTCP
- 淺談promise用法Promise
- 淺談品牌管理
- 淺談Mysql索引MySql索引
- 淺談async/awaitAI
- 淺談redux(一)Redux