LiveData的分析與簡單使用

  • 簡介

    • LiveData是androidx.lifecycle-livedata包下的一個抽象類,實現(xiàn)了一種關(guān)聯(lián)了生命周期的簡單觀察者模式,主要的功能就是用于視圖層與數(shù)據(jù)間的單向一對多通知,一個LiveData會持有一個可觀察的Data對象,一開始是處于NOT_SET狀態(tài),當用戶通過setValue方法更新后,LiveData會遍歷所有的observer(處于Active狀態(tài)的),通知他們Data的更新.LiveData的行為會在Lifecycle范圍內(nèi),避免很多Activity Leak和空指針
      本文代碼樣例都是kotlin,LiveData的源碼是java
  • LiveData的使用

      //聲明一個LiveData
      val data:MutableLiveData<String> = MutableLiveData()
      //在需要的地方訂閱,observer的onChange方法會回調(diào)
      data.observe(lifecycleOwner,observer)
      //可以和retrofit配合使用
     interface ApiService {
        @GET("/api/get")
        fun getChannels():LiveData<ApiResponse<Page<Media>>>
      }
      //可以和room配合使用
      @Dao
     interface BorrowModelDao {
    
          @Query("select * from s_borrow_moel")
         fun getAllBorrowedItems(): LiveData<List<BorrowModel>>
    
      }
      //配合ViewModel保存數(shù)據(jù)/監(jiān)聽數(shù)據(jù)更改
      class MyViewModel:ViewModel(){
        val data:MutableLiveData<String> = MutableLiveData()
      }
    
  • LiveData Tricks

    • Transformations.map方法轉(zhuǎn)換LiveData的數(shù)據(jù)類型
      比如我們用Retrofit獲取了一個User類數(shù)據(jù):LiveData<User>,但是我們只想把User.name暴露給外部觀察者,那么我們可以這樣操作
    private val userLiveData:MutableLiveData<User> = MutableLiveData()
    val userNames:LiveData<String> = Transformations
    .map(userLiveData){
        user ->
        user.name
    }
    

    Transformations在androidx.lifecycle包下

    • MediatorLiveData
      MediatorLiveData可以同時觀察多個LiveData的變化
        //聲明
        val mediatorLiveData:MediatorLiveData<String> = MediatorLiveData()
        //其他合適的地方添加source
        mediatorLiveData.addSource(stringFromNetwork){
            mediatorLiveData.value = it
        }
        mediatorLiveData.addSource(stringFromDatabase){
            mediatorLiveData.value = it
        }
      
  • 配合ViewModel使用

    • 我們知道ViewModel可以在一個LifecycleOwneronCreateOnDestroy之間保存實例不受到屏幕旋轉(zhuǎn)等ConfigChange的影響
      所以可以通過Activity/Fragment持有ViewModel,而ViewModel持有各類LiveData,Activity/Fragment注冊觀察需要的數(shù)據(jù),實現(xiàn)數(shù)據(jù)與UI的同步,而且不會因此發(fā)生Activity泄漏,甚至可以用ViewModel來進行Activity/Fragment之間的通訊
  • 關(guān)于Lifecycle

    • Lifecycle組成

      • LifecycleOwner: Activity/Fragment都是LifecycleOwner(support包下的AppCompatActivity和對應的Fragment),他是androidx.lifecycle:lifecycle-common包下的一個借口,只有g(shù)etLifecycle()一個方法,返回一個Lifecycle對象
      • Lifecycle: 而這個Lifecycle就是維護生命周期state的主體,默認實現(xiàn)是LifecycleRegistry,這個說起來也挺長的,以后有空再新的文章里仔細說明,反正現(xiàn)在讀者只要知道我們常用的AppCompatActivity/Fragment都是可以拿到生命周期Lifecycle的就行了
      • LifecycleObserver: 也是一個接口,不過它一個抽象方法也沒有,如果我們需要一個新的自定義的觀察
        者,只要實現(xiàn)這個接口,然后在需要對應生命周期方法回調(diào)的方法上添加
        OnLifecycleEvent注解并添加對應注解就好了,LiveData有一個
        LifecycleBoundObserver內(nèi)部類實現(xiàn)了這個接口,不過繼承關(guān)系是這樣的
        LifecycleObserver <--GenericLifecycleObserver <- LifecycleBoundObserver,
        LifecycleBoundObserver會在onStateChange收到回調(diào),不依賴于注解,不過讀
        者現(xiàn)在只要知道在Lifecyclestate發(fā)生改變的時候,LifecycleObserver會收
        到通知(void onStateChanged(LifecycleOwner source, Lifecycle.Event event);)
xianyu.gif
  • LiveData類分析

    • 簡單的類結(jié)構(gòu)
      有幾個內(nèi)部類,都是ObserverWrapper的子類,用于普通的觀察者(帶生命周期)的LifecycleBounderObserver,和不帶的AlwaysActiveObserver..
      abstract class LiveDate{
    
        class LifecycleBounderObserver extends ObserverWrapper implements GenericLifecycleObserver{
    
        }
    
        private abstract ObserverWrapper{
    
        }
    
        private class AlwaysActiveObserver extends ObserverWrapper {
    
        }
      }
    
  • observe入手分析

    • 傳入?yún)?shù): LifecycleOwner , Observer類(簡單的回調(diào)通知接口)
    • 首先通過LifecycleOwner獲取Lifecycle,判斷時候是DESTROYED狀態(tài),是的話直接return
      ObserverLifecycleOwner包裝為LifecycleBoundObserver對象
    • LiveData維護了一個SafeIterableMap<Observer<? super T>, ObserverWrapper> 對象mObservers,保存了Observer和對應的Wrapper,存入LifecycleBoundObserver對象后判斷是否已存在并且是否對應當前LifecycleOwner,做出相應的處理,當mObservers中沒有時,直接給LifecycleOwner的Lifecycle注冊該LifecycleBoundObserver
    • 所以說實際上是我們傳入的LifecycleOwner保存的Lifecycle注冊了一個觀察者,LifecycleBoundObserver會在state 等于 DESTROYED的時候移除該觀察者,在其他狀態(tài)的時候調(diào)用activeStateChange()方法
    • 先比較當前mActive和新active,賦值給mActive,然后通過LiveData類的mActivieCount是否為0來判斷是不是完全沒有通知過,如果是進入非active狀態(tài)mActiveCount就-1,否則+1,原先未active現(xiàn)在active了,調(diào)用onActive方法,新的mActivieCount == 0并且 新active的狀態(tài)為false就調(diào)用onInactive方法,然后判斷mActive,true則會調(diào)用dispatchValue方法,基本上mActivieCount只會有0 -> 1 ,1 -> 0兩個狀態(tài)變化,所以onActive是LiveData觀察者數(shù)量從無到有時回調(diào),onInactive反之
      private abstract class ObserverWrapper {
       final Observer<? super T> mObserver;
       boolean mActive;
       int mLastVersion = START_VERSION;
       void activeStateChanged(boolean newActive) {
           if (newActive == mActive) {
               return;
           }
           // immediately set active state, so we'd never dispatch anything to inactive
           // owner
           mActive = newActive;
           boolean wasInactive = LiveData.this.mActiveCount == 0;
           LiveData.this.mActiveCount += mActive ? 1 : -1;
           if (wasInactive && mActive) {
               onActive();
           }
           if (LiveData.this.mActiveCount == 0 && !mActive) {
               onInactive();
           }
           if (mActive) {
               dispatchingValue(this);
           }
       }
      

}
```

  • 上面羅里吧嗦一大推讀者可能一臉懵逼,反正結(jié)論就是,LiveData狀態(tài)由沒有觀察者到有觀察者會走onActive方法,反之會走onInactive方法,不管原先狀態(tài)如何,只要新狀態(tài)是活躍就會走dispatchValue
  • onInactive/onActiveLiveData里兩個open protected的方法,只在MediatorLiveData里有實現(xiàn),我還沒有仔細研究MediatorLiveData,大家可以去看看源碼
  • 所以LifecycleBoundObserver新狀態(tài)為活躍時,會調(diào)用外部類LiveDatadispatchValue方法,傳入的參數(shù)為ObserverWrapper,也就是LifecycleBoundObserver的父類(LifecycleBoundObserver繼承了ObserverWrapper,實現(xiàn)了GenericLifecycleObserver),在這要注意下內(nèi)部類(非靜態(tài))是可以拿到外部類實例的,LiveData.this.XXX,忘記的同學復習下,我是好久沒用java,突然看到有點懵逼..
//傳入?yún)?shù)是Observer,有的話就分發(fā)給它,為null就對所有Observers分發(fā)數(shù)據(jù)
void dispatchingValue(@Nullable ObserverWrapper initiator) {
  //如果正在分發(fā)數(shù)據(jù),就標記為分發(fā)無效,分發(fā)中的會因此跳出循環(huán)
   if (mDispatchingValue) {
     //標記為分發(fā)無效
       mDispatchInvalidated = true;
       return;
   }
   //標記為分發(fā)中
   mDispatchingValue = true;
   do {
       mDispatchInvalidated = false;
       if (initiator != null) {
           considerNotify(initiator);
           initiator = null;
       } else {
           for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                   mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                //嘗試分發(fā)
               considerNotify(iterator.next().getValue());
               //標記為無效時會跳出循環(huán),在while循環(huán)中等待一次mDispatchInvalidated再次變?yōu)閠rue時再次進入for循環(huán)
               if (mDispatchInvalidated) {
                   break;
               }
           }
       }
       //沒有被標記無效時while循環(huán)就執(zhí)行一次完事,否則會再次執(zhí)行
   } while (mDispatchInvalidated);
   mDispatchingValue = false;
}
  • LiveData中的mDispatchingValue/mDispatchInvalidated在此處保證不會重復分發(fā)或者分發(fā)舊的value,當setValue分發(fā)調(diào)用dispatchValue(null)時會遍歷所有的Observer分發(fā)新值,否則只分發(fā)給傳入的ObserverWrapper,這里說明下:LiveData維護了mDispatchingValue/mDispatchInvalidated,而Observer可能會有多個,都持有LiveData對象(因為是內(nèi)部類),所以等待的observer對應的LiveData會獲得mDispatchInvalidated = true,中斷進行中的分發(fā),讓舊數(shù)據(jù)不再分發(fā)
  • considerNotify: 第一步檢查ObserverWrapper是否mActive,第二步檢查shouldBeActive(),第三步檢查ObserverWrapper的版本observer.mLastVersion >= mVersion,最后才通知更新,調(diào)用Observer的onChange方法
  • ObserverWrapper中的mLastVersion /和LiveData的mVersion用來防止再入活躍狀態(tài)后重復分發(fā)
  • ObserverWrapper中的mActive用來控制該觀察者是否需要分發(fā)(activeStateChanged(boolean)方法的作用下)
  • considerNotify方法

    先上代碼
    private void considerNotify(ObserverWrapper observer) {
         if (!observer.mActive) {
             return;
         }
         // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
         //
         // we still first check observer.active to keep it as the entrance for events. So even if
         // the observer moved to an active state, if we've not received that event, we better not
         // notify for a more predictable notification order.
         if (!observer.shouldBeActive()) {
             observer.activeStateChanged(false);
             return;
         }
         if (observer.mLastVersion >= mVersion) {
             return;
         }
         observer.mLastVersion = mVersion;
         //noinspection unchecked
         observer.mObserver.onChanged((T) mData);
     }
    
    
    很簡單的邏輯,就是
    1. 第一步判斷是否active,這個mActive屬性在幾種情況下會為true
      a. Observer是通過observeForever注冊的
      b. Observer的shouldBeActive返回true
      我們看看方法代碼,只有綁定的Lifecycle的State大于STARTED才會為true
            @Override
            boolean shouldBeActive() {
                return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
            }
mActive會在生命周期變化時會被多次檢查
2. 第二步再次檢查`shouldBeActive()`狀態(tài)
3. 第三步檢查`mLastVersion`和`mVersion`  
    這個version是在setValue時被+1的,檢查一下防止重復分發(fā),完成后修改lastVersion然后回調(diào)`onChange(T)`
  • setValue/postValue方法的分析

    按慣例先看看代碼,postValue的代碼也好理解,只是給賦值操作加了一個鎖,并把setValue放到主線程執(zhí)行,使多個postValue只有最后一個的value生效

    private final Runnable mPostValueRunnable = new Runnable() {
       @Override
       public void run() {
           Object newValue;
           synchronized (mDataLock) {
               newValue = mPendingData;
               mPendingData = NOT_SET;
           }
           //noinspection unchecked
           setValue((T) newValue);
       }
    };
    protected void postValue(T value) {
       boolean postTask;
       synchronized (mDataLock) {
           postTask = mPendingData == NOT_SET;
           mPendingData = value;
       }
       if (!postTask) {
           return;
       }
       ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    

    想象一下如果兩個線程T-A,T-B 里都執(zhí)行了postValue傳入不同的值A(chǔ),B,先會在方法內(nèi)的mDataLock鎖住的代碼塊競爭執(zhí)行,比如A先執(zhí)行,mPendingData先被賦值為A,postTask => true,然后賦值為B,也就是說第一次的賦值才會通過同步塊之后的if判斷,執(zhí)行分發(fā)runnable,線程T-B只是修改了mPendingValue的值

    然后會有一個主線程上的同步代碼塊(也是同一個鎖),將新值執(zhí)行setValue,mPendingValue重設為初始值,也就是說取得值實際上是后一個線程的B,后面分發(fā)完成重設為NOT_SET,后面的PostValue又進入原來的邏輯了

    postValue的注釋也很清楚了: If you called this method multiple times before a main thread executed a posted task, only
    the last value would be dispatched.

  • 基本流程

    • observe() -> 新建Observer -> Observer的StateChange -> dispatchValue -> considerNotify
    • setValue/postValue -> dispatchValue -> considerNotify
  • 總結(jié)

    LiveData的結(jié)構(gòu)是比較簡單的,使用時注意不要多次注冊觀察者,一般如果在Activity中使用,就在OnCreate方法注冊,Activity被關(guān)閉或回收后觀察者會自動解除訂閱,所以不會發(fā)生泄漏,我們也可以自己繼承LiveData做更多的定制來實現(xiàn)自己的業(yè)務邏輯,配合Tranformations工具類和MediatorLiveData可以實現(xiàn)很多比較復雜的邏輯

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市怒竿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枉层,死亡現(xiàn)場離奇詭異没隘,居然都是意外死亡,警方通過查閱死者的電腦和手機爆侣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幢妄,“玉大人兔仰,你說我怎么就攤上這事〗对В” “怎么了乎赴?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長潮尝。 經(jīng)常有香客問我榕吼,道長,這世上最難降的妖魔是什么勉失? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任羹蚣,我火速辦了婚禮,結(jié)果婚禮上乱凿,老公的妹妹穿的比我還像新娘顽素。我一直安慰自己,他們只是感情好徒蟆,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布胁出。 她就那樣靜靜地躺著,像睡著了一般段审。 火紅的嫁衣襯著肌膚如雪全蝶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音裸诽,去河邊找鬼嫂用。 笑死,一個胖子當著我的面吹牛丈冬,可吹牛的內(nèi)容都是我干的嘱函。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼埂蕊,長吁一口氣:“原來是場噩夢啊……” “哼往弓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蓄氧,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤函似,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后喉童,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撇寞,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年堂氯,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔑担。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡咽白,死狀恐怖啤握,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晶框,我是刑警寧澤排抬,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站授段,受9級特大地震影響蹲蒲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侵贵,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一卷拘、第九天 我趴在偏房一處隱蔽的房頂上張望烦粒。 院中可真熱鬧,春花似錦锻弓、人聲如沸掩宜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牺汤。三九已至辽旋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背补胚。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工码耐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溶其。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓骚腥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瓶逃。 傳聞我的和親對象是個殘疾皇子束铭,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345