Android Jetpack ViewModel解析

前言

最近幾天把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)的:

官方ViewModel生命周期

這里先說(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)求)嗎诗芜?

引入了ViewModelLiveData之后,可以實(shí)現(xiàn)vmview的解耦埃疫,只是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核心類

通常定義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,核心就在ViewModelProviderget()方法

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è)新的方法onRetainNonConfigurationInstancegetLastNonConfigurationInstance下梢,更多細(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ù)塞蹭。

借用剛才文章的一張圖來(lái)說(shuō)明保存恢復(fù)數(shù)據(jù)流程
保存恢復(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ì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘟裸,一起剝皮案震驚了整個(gè)濱河市客叉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖兼搏,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卵慰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡佛呻,警方通過(guò)查閱死者的電腦和手機(jī)裳朋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吓著,“玉大人鲤嫡,你說(shuō)我怎么就攤上這事∫勾#” “怎么了泛范?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)紊撕。 經(jīng)常有香客問(wèn)我罢荡,道長(zhǎng),這世上最難降的妖魔是什么对扶? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任区赵,我火速辦了婚禮,結(jié)果婚禮上浪南,老公的妹妹穿的比我還像新娘笼才。我一直安慰自己,他們只是感情好络凿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布骡送。 她就那樣靜靜地躺著,像睡著了一般絮记。 火紅的嫁衣襯著肌膚如雪摔踱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天怨愤,我揣著相機(jī)與錄音派敷,去河邊找鬼。 笑死撰洗,一個(gè)胖子當(dāng)著我的面吹牛篮愉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播差导,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼试躏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了柿汛?” 一聲冷哼從身側(cè)響起冗酿,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤埠对,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后裁替,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體项玛,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年弱判,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了襟沮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昌腰,死狀恐怖开伏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遭商,我是刑警寧澤固灵,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站劫流,受9級(jí)特大地震影響巫玻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祠汇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一仍秤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧可很,春花似錦诗力、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至菜拓,卻和暖如春圈澈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尘惧。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留递递,地道東北人喷橙。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像登舞,于是被迫代替她去往敵國(guó)和親贰逾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容