前言
最近幾天把Jetpack中的3劍客Lifecycle+ViewModel+LiveData的使用和原理學(xué)習(xí)了一遍缀台,這3者也是構(gòu)建MVVM模式的核心。這個(gè)系列的工具凭疮,建議大家在官網(wǎng)學(xué)習(xí):https://developer.android.google.cn/jetpack饭耳。
目前網(wǎng)上對(duì)這3者使用和源碼解析的文章也沉淀了不少,大家可以自行查閱执解。本文換個(gè)方向來(lái)跟大家聊聊我所理解的關(guān)于ViewModel的一些內(nèi)容寞肖。
系列文章
Android Jetpack ViewModel解析
Android Jetpack LiveData解析
Android Jetpack DataBinding原理淺析(簡(jiǎn)版)
看看官方對(duì)Viewmodel作用的解釋
官方的Viewmodel用法例子
官方的解釋說(shuō)明與案例還是很清晰的,所以就不做過(guò)多解釋了材鹦。說(shuō)一下我在學(xué)習(xí)過(guò)程中的一些疑問(wèn)吧逝淹。
1、在官網(wǎng)和Google的github demo中都建議在mvvm模式中的vm層去繼承ViewModel桶唐,那為什么一定要繼承ViewModel呢栅葡?我直接在VM中定義LiveData數(shù)據(jù)不行嗎?是否繼承ViewModel的區(qū)別在哪尤泽?
下面通過(guò)一個(gè)例子來(lái)講解欣簇,這里為了突出核心要點(diǎn)就用近乎偽代碼的形式來(lái)表述。首先在mvvm的VM
層去獲取網(wǎng)絡(luò)數(shù)據(jù)坯约,實(shí)際上是model
層執(zhí)行的熊咽,然后當(dāng)獲取數(shù)據(jù)成功后調(diào)用LiveData
對(duì)象的setValue()
方法通知Activity的觀察者去刷新UI。
public class NewsViewModel extends ViewModel {
private NewsModel newsModel;
//adapter數(shù)據(jù)源
private List<ItemNews> mList = new ArrayList<>();
public MutableLiveData<List<ItemNews>> itemNewsLiveData = new MutableLiveData<>();
public NewsViewModel() {
newsModel = new NewsModel();
}
public void getNews() {
mList = newsModel.getNews();
//通知活躍的觀察者更新數(shù)據(jù)
itemNewsLiveData.setValue(mList);
}
}
Activity里面的觀察者
newsViewModel.itemNewsLiveData.observe(this, new Observer<List<ItemNews>>() {
@Override
public void onChanged(@Nullable List<ItemNews> itemNews) {
newsAdapter.updateAll(itemNews);
}
});
這里按照官方的規(guī)范闹丐,只是在View層持有VM的引用横殴,而VM不持有View的引用。到這一步我就有一個(gè)疑問(wèn),貌似不繼承ViewModel也可以完成VM與View的交互衫仑,那引入它到底有什么好處呢梨与?
再回顧下官方的說(shuō)法:
ViewModel 類旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)。
ViewModel
類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)存在文狱。
上面說(shuō)到ViewModel
管理數(shù)據(jù)是和生命周期相關(guān)的粥鞋,且能持久存儲(chǔ)數(shù)據(jù)。那下面就簡(jiǎn)單解析一下是如何與生命周期相關(guān)的:
這里先說(shuō)結(jié)論瞄崇,源碼驗(yàn)證待會(huì)兒再說(shuō)呻粹。
現(xiàn)在可以看出,好處1:
因?yàn)?code>ViewModel的生命周期是比Activity還要長(zhǎng)苏研,所以ViewModel可以持久保存UI數(shù)據(jù)等浊,具體來(lái)說(shuō)是Activity因?yàn)榕渲酶幕蛘弑幌到y(tǒng)意外回收的時(shí)候,會(huì)自動(dòng)保存數(shù)據(jù)楣富,然后在Activity重建的時(shí)候就可以繼續(xù)使用銷毀之前保存的數(shù)據(jù)凿掂。
好處2:
當(dāng)使用ViewModel
的 Activity 正常退出時(shí),內(nèi)部會(huì)調(diào)用ViewModel
對(duì)象的onCleared()
方法纹蝴,以便它可以清理資源。首先內(nèi)部會(huì)釋放ViewModel
數(shù)據(jù)踪少,同時(shí)你也可以在vm層重寫這個(gè)方法去釋放資源塘安,取消網(wǎng)絡(luò)等。
好處3:
在 Fragment 之間共享數(shù)據(jù)援奢。這個(gè)比較簡(jiǎn)單兼犯,只要2個(gè)Fragment獲取
ViewModel
對(duì)象時(shí)傳入的LifecycleOwner一致就可以共享ViewModel
中定義的數(shù)據(jù)。
現(xiàn)在回頭看剛才的例子集漾,當(dāng)配置改變導(dǎo)致Activity重建的時(shí)候切黔,如果沒有使用ViewModel存儲(chǔ)數(shù)據(jù),那么就會(huì)重新請(qǐng)求數(shù)據(jù)重繪頁(yè)面具篇;如果用ViewModel的話纬霞,就能用頁(yè)面銷毀之前保存的數(shù)據(jù)去直接顯示視圖。
2驱显、在MVVM中用了ViewModel+LiveData之后還有必要在Activity/Fragment的onDestory()方法中手動(dòng)取消耗時(shí)任務(wù)(網(wǎng)絡(luò)請(qǐng)求)嗎诗芜?
引入了ViewModel
和LiveData
之后,可以實(shí)現(xiàn)vm
和view
的解耦埃疫,只是view引用vm伏恐,而vm是不持有view的引用的。在activity退出之后即是還有網(wǎng)絡(luò)在繼續(xù)也不會(huì)引發(fā)內(nèi)存泄漏和空指針異常栓霜,所以不在ondestory
取消網(wǎng)絡(luò)也是可以的翠桦。如果你還是想取消,可以重寫onCleared()
方法去做胳蛮。
原理解析
ViewModel
源碼真的很簡(jiǎn)單销凑,就幾個(gè)類愁铺,實(shí)際上實(shí)際上真正的業(yè)務(wù)處理核心就只有ViewModelProvider
,其他都是輔助闻鉴。
-
ViewModel
類是一個(gè)抽象方法茵乱,只有一個(gè)空方法onCleared()
,它會(huì)在onDestory的時(shí)候被調(diào)用孟岛,所以你可以重寫這個(gè)方法瓶竭,在里面做些釋放資源、取消網(wǎng)絡(luò)的操作渠羞。 -
AndroidViewModel
是繼承ViewModel
的斤贰,就多了一步,提供Application
參數(shù) -
ViewModelStore
:和名字一樣次询,就是存儲(chǔ)ViewModel的荧恍,它里面定義了一個(gè)HashMap來(lái)存儲(chǔ)ViewModel,key值是ViewModel全路徑+一個(gè)默認(rèn)的前綴屯吊。大概這樣:key=android.arch.lifecycle.ViewModelProvider.DefaultKey:com.zx.mvvmdemo.vm.NewsViewModel
-
ViewModelStoreOwner
:是一個(gè)接口送巡,只有一個(gè)方法,是獲取ViewModelStore
對(duì)象的
通常定義ViewModel之后盒卸,通過(guò)如下方式獲取ViewModel對(duì)象
newsViewModel = ViewModelProviders.of(this).get(NewsViewModel.class);
of
傳FragmentActivity或者Fragment對(duì)象骗爆。
這里主要分析2個(gè)問(wèn)題:
- 1、ViewModel是如何創(chuàng)建的蔽介?
- 2摘投、ViewModel是如何保存數(shù)據(jù)的?
這里不按照?qǐng)?zhí)行流程去閱讀源碼那樣分析虹蓄,因?yàn)檫@么詳細(xì)的內(nèi)容已經(jīng)有人寫了犀呼,我就講一下關(guān)鍵的思路,細(xì)節(jié)的話薇组,推薦一遍不錯(cuò)的:
Android ViewModel外臂,再學(xué)不會(huì)你砍我
從入口來(lái)看,是外觀模式体箕,ViewModelProviders
就是外觀類专钉,真正的業(yè)務(wù)處理是在ViewModelProvider
注意前者多了一個(gè)s
,核心就在ViewModelProvider
的get()
方法
邏輯大致如下:
首先從ViewModelStore的HashMap這個(gè)緩存中取取數(shù)據(jù)累铅,如果有直接返回ViewModel跃须,如果沒有通過(guò)ViewModelProvider內(nèi)部的工廠類去創(chuàng)建,ViewModel的創(chuàng)建具體來(lái)說(shuō)是通過(guò)反射區(qū)做的娃兽,并且保存在緩存中菇民,這樣就獲取到了ViewModel對(duì)象。
到這里,問(wèn)題1就解決了第练。下面看看問(wèn)題2阔馋,數(shù)據(jù)是如何保存的。
數(shù)據(jù)保存是在FragmentActivity中實(shí)現(xiàn)的娇掏。
因?yàn)榕渲米兓瘜?dǎo)致Activity銷毀重建以前的方案是保存數(shù)據(jù)在onSaveInstanceState
呕寝,而還原在onRestoreInstanceState
,但是這2個(gè)方法是有很大瑕疵的婴梧,于是在新的SDK中FragmentActivity內(nèi)置了2個(gè)新的方法onRetainNonConfigurationInstance
和getLastNonConfigurationInstance
下梢,更多細(xì)節(jié)可以參考:
https://www.cnblogs.com/dengxianzhi/articles/2248655.html
其中onRetainNonConfigurationInstance
是在onStop() 和 onDestroy()之間被調(diào)用,它內(nèi)部會(huì)保存ViewModel數(shù)據(jù);而在onCreate的時(shí)候會(huì)調(diào)用getLastNonConfigurationInstance
來(lái)恢復(fù)數(shù)據(jù)塞蹭。
總結(jié)
1孽江、讓mvvm的vm去繼承ViewModel
,并在其內(nèi)部定義番电、管理UI所需的數(shù)據(jù)岗屏,不僅可以讓View和ViewModel
解耦,同時(shí)其內(nèi)部自動(dòng)關(guān)聯(lián)生命周期漱办,可以減少在Activity的生命周期方法中寫大量的樣板代碼这刷。
2、ViewModel保存數(shù)據(jù)這個(gè)功能還是有點(diǎn)用的洼冻。一開始我覺得當(dāng)屏幕旋轉(zhuǎn)的時(shí)候你可以通過(guò)configChanges
的設(shè)置來(lái)阻止它的重建崭歧,這樣就不需要viewmodel保存數(shù)據(jù)了。但是其它的一些意外情況也是有可能導(dǎo)致Activity重建的撞牢,比如當(dāng)前activity在填寫一堆表單數(shù)據(jù),中間打開了其他app叔营,在后臺(tái)被回收了屋彪,后面再進(jìn)入的時(shí)候如果沒有保存就得重填,如果你的UI數(shù)據(jù)放在ViewModel的話就會(huì)自動(dòng)回復(fù)绒尊,這樣看來(lái)畜挥,ViewModel的保存數(shù)據(jù)功能是不是還挺有用呢
3、利用ViewModel共享數(shù)據(jù)可以更簡(jiǎn)潔婴谱,減少很多不必要的接口蟹但。
4、友情提示:在創(chuàng)建ViewModel對(duì)象的時(shí)候不能手動(dòng)去new一個(gè)谭羔,而是通過(guò)Provider的方式去獲取华糖,這樣的話才能利用ViewModel的那些優(yōu)勢(shì)。