Android Lifecycle、ViewModel和LiveData

1礁遵、概述

在I / O '17的時候轻绞,其中一個重要的主題是Architecture Components。這是一個官方的新庫榛丢。旨在幫助開發(fā)者設(shè)計“健壯铲球,可測試和可維護(hù)”的應(yīng)用程序。簡而言之晰赞,這個庫可以幫助開發(fā)者更好地處理生命周期事件和配置更改時的數(shù)據(jù)持久性稼病,同時還能幫助開發(fā)者創(chuàng)建更好的體系結(jié)構(gòu)應(yīng)用程序选侨,并避免難以維護(hù)和測試的膨脹類。

其官方文檔在這里:Android Architecture Components

這里我打算詳細(xì)討論下Architecture Components里面的Lifecycle然走、ViewModel和LiveData這3個部分援制。先看一下這三者的類和相關(guān)類的關(guān)系圖:

關(guān)系圖

粗一看有些復(fù)雜,接下來會詳細(xì)分析下各個部分負(fù)責(zé)的內(nèi)容芍瑞。

2晨仑、Lifecycle

根據(jù)官方文檔,Lifecycle是一個抽象類拆檬,一個有Android 生命周期的對象附在它上面洪己, 并且它持該對象的當(dāng)前生命周期所處狀態(tài),所以其他對象可以觀察到這種狀態(tài)并做出相應(yīng)的反應(yīng)竟贯。為了跟蹤這種狀態(tài)答捕,Lifecycle類包含兩個枚舉類Event和State。

2.1 Event

一個Event代表當(dāng)Android 生命周期的對象的生命周期發(fā)生改變時候屑那,會觸發(fā)的一個生命周期事件(例如一個activity正在被恢復(fù))拱镐。在LifecycleObserver類中,可以為每個事件實現(xiàn)回調(diào)持际,這樣在生命周期的對象的生命周期改變的時候我們能進(jìn)行相關(guān)的處理沃琅。arch.lifecycle包提供了Annotation,這意味著可以在類中注釋應(yīng)該在某些生命周期事件中觸發(fā)的方法蜘欲。比如像下面這樣:


@OnLifecycleEvent(ON_STOP)

void doSometing(){

}

還可以在同一個帶注釋的方法中處理多個生命周期事件:


@OnLifecycleEvent({ON_STOP益眉,ON_START})

void doSometing(){

}

下面是各種事件的情況:

① ON_ANY:在任何生命周期事件時觸發(fā)。

② ON_CREATE:創(chuàng)建LifecycleOwner(下面會講這個類)時將觸發(fā)此事件芒填。

③ ON_DESTROY:LifecycleOwner被銷毀時將觸發(fā)此事件呜叫。

④ ON_PAUSE:LifecycleOwner暫停時將觸發(fā)此事件。

⑤ ON_RESUME:在LifecycleOwner恢復(fù)時觸發(fā)此事件殿衰。

⑥ ON_START:啟動LifecycleOwner時觸發(fā)此事件朱庆。

⑦ ON_STOP:LifecycleOwner停止時觸發(fā)此事件。

ON_CREATE闷祥,ON_START娱颊,ON_RESUME的方法會在LifeCycleOwner對應(yīng)方法(onCreate()、onStart()凯砍、onResume())返回后被調(diào)用箱硕。生命周期事件ON_DESTROY, ON_STOP, ON_PAUSE的方法會在LifeCycleOwner對應(yīng)方法(onDestory()、onStop()悟衩、onPause()被調(diào)用之前調(diào)用剧罩。

2.2 State

生命周期的State本質(zhì)上是介于兩個生命周期事件之間的一種情況。觸發(fā)事件后座泳,生命周期將進(jìn)入一個狀態(tài)惠昔,然后在觸發(fā)另一個事件時離開該狀態(tài)并進(jìn)入另一個狀態(tài)幕与。如下圖所示:

狀態(tài)轉(zhuǎn)換

從上圖可以看到,Lifecycle實例在任意時間段里肯定是下面五個狀態(tài)的其中一種:

① INITIALISED:初始起點的生命周期狀態(tài)镇防。

② CREATED:ON_CREATE事件之后啦鸣,ON_START事件之前的狀態(tài)±囱酰或者是ON_STOP事件之后诫给,ON_DESTROY事件之前的狀態(tài)。

③ STARTED:ON_START事件之后啦扬,ON_RESUME事件之前的狀態(tài)中狂。或者是事件之ON_PAUSE后考传,ON_STOP事件之前的狀態(tài)吃型。

④ RESUMED:ON_RESUME事件之后的狀態(tài)证鸥。

⑤ DESTROYED:ON_DESTROY事件之后的狀態(tài)僚楞。

如果想獲取Lifecycle實例的當(dāng)前狀態(tài),那么可以調(diào)用getCurrentState()方法枉层,該方法允許開發(fā)者在任何時間檢索其當(dāng)前狀態(tài)泉褐。這有助于在執(zhí)行某種形式的操作之前檢查Lifecycle組件的狀態(tài)。State對象還可以調(diào)用isAtLeast()方法來判斷當(dāng)前狀態(tài)是否大于等于給定狀態(tài)鸟蜡。

2.3 Lifecycle相關(guān)方法

了解了Lifecycle所包含的State和Event枚舉類膜赃,現(xiàn)在看看Lifecycle和其它類進(jìn)行交互的一些方法。

Lifecycle方法

上圖中看到可以Lifecycle的核心方法主要用來管理觀察者:

① addObserver():該方法用來添加一個Observer實例揉忘,只要LifecycleOwner改變狀態(tài)就會通知它跳座。當(dāng)添加一個Observer時,它將接收導(dǎo)致當(dāng)前狀態(tài)的所有事件泣矛。舉例來說疲眷,如果Lifecycle目前在RESUMED狀態(tài),則觀察員將收到ON_CREATE您朽,ON_START和ON_RESUME事件狂丝。

② removeObserver():可以調(diào)用此方法從Lifecycle的觀察者列表中刪除給定的觀察者。從生命周期中刪除觀察者將不再接收任何觸發(fā)事件哗总。

③ getCurrentState():返回生命周期所在的當(dāng)前狀態(tài)几颜。

前面提到了一個和Lifecycle息息相關(guān)的類LifecycleOwner,接下來看一下這個類是干什么的讯屈。

2.4 LifecycleOwner

這個其實是一個接口類蛋哭,里面只有一個方法getLifecycle()


public interface LifecycleOwner {

    @NonNull

    Lifecycle getLifecycle();

}

里面的唯一方法getLifecycle()就是用來返回Lifecycle的。而這個方法的所代表的意思很簡單涮母,告訴要使用Lifecycle的組件谆趾,我是一個生命周期感知組件准颓,我存在生命周期的概念。我們現(xiàn)在看v4組件下的Activity棺妓,會繼承自SupportActivity攘已,和Fragment,這兩個類都實現(xiàn)了LifecycleOwner這個接口怜跑。同時這兩個類有一個LifecycleRegistry屬性样勃,這個屬性就是繼承自Lifecycle而LifecycleOwner的getLifecycle()返回的就是這個LifecycleRegistry。這樣就可以通過這個方法獲取該Activity的Lifecycle性芬,再通過對Lifecycle的觀察對Activity的生命周期進(jìn)行觀察峡眶,如下:


class MainActivity extends AppCompatActivity() {

   protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState)

        getLifecycle().addObserver(new SomeObserver())

    }

}

SomeObserver類繼承自LifecycleObserver,如下所示


class SomeObserver implements LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)

        void MyResume() {

        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)

        void MyPause() {

        }

    }

這樣的話植锉,當(dāng)MainActivity 觸發(fā)相應(yīng)的事件辫樱,SomeObserver 就會回調(diào)相應(yīng)的方法。

Lifecycle俊庇、LifecycleOwner狮暑、LifecycleObserver 三者關(guān)系

到此Lifecycle相關(guān)部分介紹的差不多了,但是還不夠辉饱,官方還提供了其他已經(jīng)封裝好的能感知Lifecycle組件生命周期的配套組件LiveData和ViewModels搬男。

3 LiveData

LiveData的作用是持有一份給定的數(shù)據(jù),并且能夠在生命周期變化中觀察它彭沼。該類為開發(fā)者提供了一系列方法缔逛,方便對這個類持有數(shù)據(jù)的觀察者的管理。LiveData會根據(jù)觀察者綁定的LifecycleOwner的生命周期情況姓惑,來決定是否將數(shù)據(jù)改變的情況通知給觀察者褐奴。這么做的好處就是,比如在一個Activity里面請求了網(wǎng)絡(luò)更新這個Activity A界面下的數(shù)據(jù)于毙,但是如果數(shù)據(jù)還沒有請求回來這時候用戶跳轉(zhuǎn)到了另一個Activity B敦冬,這時候如果這個數(shù)據(jù)是被LiveData所持有的,那么這個被網(wǎng)絡(luò)請求更新的數(shù)據(jù)就不會通知給Activity A望众。

LiveData的處理邏輯如下圖所示:

LiveData處理邏輯

3.1 setValue()

當(dāng)調(diào)用LiveData的setValue(T value)方法時匪补,將設(shè)置LiveData持有的數(shù)據(jù)。判斷是否有活躍的觀察者烂翰,這里的活躍觀察者指觀察者綁定的LifecycleOwner中的Lifecycle處于STARTED和RESUMED狀態(tài)的觀察者夯缺,如果有的話,將更新的數(shù)據(jù)發(fā)送給這些處于活躍狀態(tài)的觀察者甘耿。

3.2 observe()

當(dāng)調(diào)用的LiveData的observe(LifecycleOwner owner, Observer observer)方法時踊兜,根據(jù)情況會有不同的處理。如果LifecycleOwner處于DESTROYED狀態(tài)佳恬,那么這個調(diào)用將被完全忽略捏境。如果它不是DESTROYED于游,那么此時觀察者Observer將被添加到LiveData的觀察者列表中,同時該觀察者會LifecycleOwner綁定垫言,如果LifecycleOwner的生命周期狀態(tài)變成DESTROYED贰剥,那么和這個LifecycleOwner綁定的觀察者會自動被移除,這么做的好處就是可以避免很多可能會出現(xiàn)的內(nèi)存泄漏筷频。同時如果之前LiveData已經(jīng)被設(shè)置過數(shù)據(jù)了蚌成,那么觀察者會立刻接收到最新的數(shù)據(jù)。

如果之前LiveData沒有觀察者觀察它凛捏,那第一次接受觀察者會回調(diào)LiveData的onActive()方法担忧。這個方法里面可以執(zhí)行一些數(shù)據(jù)拉取操作,使數(shù)據(jù)處于處于最新狀態(tài)坯癣,回調(diào)這個方法意味著該LiveData正在被使用中瓶盛。

3.3 removeObserver()

這個方法有兩個重載方法 removeObserver(Observer observer)和 removeObservers(LifecycleOwner owner) 一個是移除觀察者,一個是移除和該LifecycleOwner所綁定的所有觀察者示罗。

如果LiveData的觀察者列表中不存在活躍觀察者了惩猫,也就是說沒有一個觀察者綁定的LifecycleOwner的Lifecycle處于 STARTED 或者RESUMED狀態(tài)。這時候LiveData會回調(diào)onInactive()鹉勒。這時候就算其持有的數(shù)據(jù)更新了帆锋,也不會發(fā)起通知。

3.4 其他方法

① hasActiveObservers():檢查LiveData中是否有活躍的觀察者禽额。

② hasObservers():檢查LiveData中是否有觀察者。

③ observeForever(Observer observer):用于將一個Observer添加到一個活躍列表中皮官,該列表將始終保持ACTIVE狀態(tài)脯倒,因此永遠(yuǎn)不會自動從Observer實例列表中移除它。要移除此Observer時必須手動調(diào)用removeObserver()捺氢。

④ postValue(T value):在主線程中給LiveData設(shè)置值藻丢。

Tips:LiveData中的setValue(T value)、postValue(T value)都是protected摄乒,也就是說只能自己或者它的繼承類進(jìn)行調(diào)用悠反。如果想在外面調(diào)用這些方法可以使用它的繼承類MutableLiveData。

4馍佑、ViewModel

4.1 ViewModel實現(xiàn)

ViewModel這個類設(shè)計出來的目的是為了解決configuration 改變時候斋否,Activity會重建,這時候里面的數(shù)據(jù)不易保存的問題拭荤。有了它Activity或者Fragment就可以不需要承擔(dān)保留數(shù)據(jù)的責(zé)任了茵臭,可以把這些事情交給ViewModel,做到很好的數(shù)據(jù)和視圖的解耦舅世。同時ViewModel會在configuration 更改時自動保留數(shù)據(jù)旦委。

ViewModel生命周期

官方給的建議是將LiveData和ViewModel配合來使用奇徒。當(dāng)不需要ViewModel時(比如Activity調(diào)用finish()方法),ViewModel會回調(diào)onCleared()方法缨硝,之后會銷毀自己摩钙。這一好處也是避免了內(nèi)存泄漏的情況發(fā)生。

Activity正常銷毀時的ViewModel

例子如下:


public class MyViewModel extends ViewModel {

    private MutableLiveDatam<Integer> mValue= new MutableLiveData<>(); 

    public LiveData getValue(){

        return  mValue;

    }

    public void setValue(Integer value ){

        mValue.setValue(value);

    }

}

Tips: ViewModel可能比它所涉及的一些Activity/Fragment實例存活時間更長查辩。因此不要保留 Activity的Context和View相關(guān)的任何引用腺律,不然可能引起內(nèi)存泄漏。如果想引用Application的Context宜肉,可以使用AndroidViewModel匀钧,可以通過其getApplication()來獲取。

4.2 ViewModel使用

ViewModel的創(chuàng)建不能通過簡單的new對象來進(jìn)行谬返。需要通過activity / fragment 獲取ViewModel實例之斯。為此,需要訪問一個名為ViewModelProviders的輔助類?遣铝,通過這樣獲取的ViewModel對于一個activity 只有一份:


MyViewModel mMyViewModel  = ViewModelProviders.of(getActivity()).get(MyViewModel .class);

ViewModelProviders這個類佑刷,本質(zhì)上其實是一個工廠類。這個類內(nèi)部包含了一個ViewModelStore實例酿炸,它負(fù)責(zé)存儲創(chuàng)建的ViewModels瘫絮。同時可以使用ViewModelProvider的get()方法來獲取作為參數(shù)傳入的ViewModel類型的實例。該方法源碼如下:


@NonNull@MainThreadpublicT get(@NonNull String key, @NonNull Class modelClass) {

    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {

        // 無需檢查之間返回

        return (T) viewModel;

    } else {

        if (viewModel != null) {

            //  輸出警告日志

        }

    }

    // 創(chuàng)建一個ViewModel 對象并存入ViewModelStore填硕。

    viewModel = mFactory.create(modelClass);

    mViewModelStore.put(key, viewModel);

    return (T) viewModel;

}

如源碼所示麦萤,當(dāng)調(diào)用此get()方法時,ViewModelProvider將檢查ViewModelStore是否已具有該類類型的現(xiàn)有ViewModel扁眯,如果是壮莹,則將返回它。但是姻檀,如果不存在命满,那么將創(chuàng)建一個新的ViewModel并將其添加到ViewModelStore中。

獲取到ViewModel 就可以使用里面的屬性和方法來進(jìn)行操作了绣版。下面說明下ViewModel存在的意義胶台。

4.3 ViewModel 意義

ViewModel 被設(shè)計出來,不僅為了解決上面所說的configuration改變時候能保留數(shù)據(jù)杂抽。其真正意義在于以下幾個方面:

① 職責(zé)分離:使Activity/Fragment不用再負(fù)責(zé)從某些數(shù)據(jù)源獲取數(shù)據(jù)诈唬,只需要負(fù)責(zé)展示數(shù)據(jù)就好,同時還消除了在配置更改時保留數(shù)據(jù)對象實例的引用的責(zé)任默怨。這兩個職責(zé)都轉(zhuǎn)給了ViewModel讯榕。

② 簡化對沒用數(shù)據(jù)的清理:當(dāng)Activity/Fragment負(fù)責(zé)清理數(shù)據(jù)的操作時,需要使用大量代碼來清理這些請求。但是將這些清理操作放到ViewModels onCleared()方法中愚屁,這些資源在Activity結(jié)束時會自動清除济竹。

③ 減少類的膨脹:由于職責(zé)的轉(zhuǎn)移,Activity/Fragment刪除了許多用于處理請求霎槐,狀態(tài)持久性和注銷數(shù)據(jù)的代碼送浊。這些代碼通常會導(dǎo)致Activity/Fragment變得非常臃腫,這樣的代碼會難以擴(kuò)展和維護(hù)丘跌。使用ViewModels可以幫助開發(fā)者緩解Activity/Fragment的膨脹袭景,使各個類的職責(zé)盡可能單一。

④ 容易測試:職責(zé)的分離會使測試這些職責(zé)更容易闭树,而且還可以產(chǎn)生更細(xì)粒度的測試用例耸棒。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市报辱,隨后出現(xiàn)的幾起案子与殃,更是在濱河造成了極大的恐慌,老刑警劉巖碍现,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幅疼,死亡現(xiàn)場離奇詭異,居然都是意外死亡昼接,警方通過查閱死者的電腦和手機(jī)爽篷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慢睡,“玉大人逐工,你說我怎么就攤上這事∫徽觯” “怎么了钻弄?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長者吁。 經(jīng)常有香客問我,道長饲帅,這世上最難降的妖魔是什么复凳? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮灶泵,結(jié)果婚禮上育八,老公的妹妹穿的比我還像新娘。我一直安慰自己赦邻,他們只是感情好髓棋,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般按声。 火紅的嫁衣襯著肌膚如雪膳犹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天签则,我揣著相機(jī)與錄音须床,去河邊找鬼。 笑死渐裂,一個胖子當(dāng)著我的面吹牛豺旬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柒凉,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼族阅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膝捞?” 一聲冷哼從身側(cè)響起坦刀,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绑警,沒想到半個月后求泰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡计盒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年渴频,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片北启。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡卜朗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咕村,到底是詐尸還是另有隱情场钉,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布懈涛,位于F島的核電站逛万,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏批钠。R本人自食惡果不足惜宇植,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望埋心。 院中可真熱鬧指郁,春花似錦、人聲如沸拷呆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腰懂,卻和暖如春梗逮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悯恍。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工库糠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涮毫。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓瞬欧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親罢防。 傳聞我的和親對象是個殘疾皇子艘虎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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