可被感知的數據 - LiveData 原理詳解

前言

在了解LiveData并學習其原理之前,需要先知道Lifecycle的使用以及原理,否則下文某些部分可能較難理解。

傳送門-Lifecycle

約定

Observer : 下文無特殊說明都稱為觀察者
LifecycleOwner: 下文無物特殊說明均稱owner(被觀察者——具有生命周期)

LiveData是什么

引用源碼對于LiveData的注釋,個人感覺解釋得很到位

/**
 * LiveData is a data holder class that can be observed within a given lifecycle.
 * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
 * this observer will be notified about modifications of the wrapped data only if the paired
 * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
 */

語意大致如下:
        LiveData所持有數據可被感知。在LiveData里,觀察者與owner成對出現(當然有例外,后面再說)。在LiveData所持有包裝過的數據有變化時,觀察者處于active狀態時回得到通知。
owner狀態處于State.STARTED或State.RESUME時觀察者被認為是active狀態。

簡單來說,LiveData作為一種媒介去持有數據,在數據發生改變時,去通知監測owner并處于active狀態的觀察者作出應對。

案例

public class MyData extends LiveData<String> {

    private static final String TAG = "T-MyData";

    public  MyData(){
        setValue("hi");
    }

    @Override
    protected void onActive() {
        super.onActive();
        Log.d(TAG, "onActive ");
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        Log.d(TAG, "onInactive ");
    }

    public void changeValue(String value){
        setValue(value);
    }

}
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "T-MainActivity";

    MyData data = new MyData();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                data.changeValue(data.getValue() + "~");
            }
        });
        
        data.observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String value) {
                Log.d(TAG, "onChanged: " + value );
            }
        });

    }

}

案例行為:LiveData持有String數據,并在MainActivity(也就是owner)生命周期內,觀察者對觀察其數據。String初始為“hi”,每點擊頁面上的按鈕String在原有拼接上“~”。在數據改變時,onChanged()可接受到改變的數據。 在點擊三次案后讓MainActiviy進入不可見狀態
日志如下


LiveData日志.jpg

從日志上看數據已被感知,此外觸發了onActive()以及onInactive() , 二者在什么時候會被觸發,下文會有說明。

原理

重要函數

對于使用LiveData重要且包含需要注意事項,挑出以預警

  • setValue()
    protected void setValue(T value) {
        assertMainThread("setValue");
        .....
    }

此函數設置LiveData里的數據,并且執行在主線程里

  • postValue()
    protected void postValue(T value) {
        .....
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }

與setValue功能相同,可執行在非主線程
-getValue() : 獲取LiveData里的數據,但不保證一定接收到最新數據(比如在異步線程里更新數據被耽誤了)

注冊過程

前文所述,onwer與觀察者稱對出現。可通過observe注冊。此外,也可通過observeForever進行注冊,與observer會有差異。此處針對observe方式先做闡述,observeForver在后文再做比對。

  • LiveData.obsever()
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        // owner處于Destroy狀態已無注冊必要
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        // 將owner與觀察者進行綁定
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        // 一個觀察者僅僅針對一個onwer
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        // 這里的getLifecycle一般是Lifecycle里說的LifecycleRegistry
        owner.getLifecycle().addObserver(wrapper);
    }

從代碼上看,注冊時,先排查owner狀態是否合適,緊接著將owner與觀察者進行了綁定,且從拋出的異常可以知道,owner與觀察者的關系為一對多。此后將包裝好的ObserverWrapper交給LifecycleRegistry,這樣,就確保了再State.Event事件到達時,ObserverWrapper會接受到訊息。 (Lifecycle知識,忘了去這里->傳送門

綁定過程

   class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
            super(observer);
            mOwner = owner;
        }
      
      .....
}
 private abstract class ObserverWrapper {
        final Observer<T> mObserver;

        ObserverWrapper(Observer<T> observer) {
            mObserver = observer;
        }
    ......
    }

這里貼出了LifecycleBoundObserver與其父類ObserverWrapper的部分內容,可見兩者分別持有了owner與觀察者信息。

到這里,整個注冊過程完畢。過程很簡單,做個小結

  • 綁定owner與觀察者,信息收集入LifecycleBoundObserver
  • 將LifecycleBoundObserver交接給LifecycleRegistry進行注冊,以便Event事件到來時得以接收

感知過程

在Lifecycle知識里知道,Event最終會經過GenericLifecycleObserver的實現類作為中轉者經過處理,傳達到具體的觀察者。從之前的代碼截圖可以知道LifecycleBoundObserver是GenericLifecycleObserver的實現類,因此能接收到Event事件。

public interface GenericLifecycleObserver extends LifecycleObserver {
    void onStateChanged(LifecycleOwner source, Lifecycle.Event event);
}

GenericLifecycleObserver對于Lifecycle的體系來說,是個觀察者(針對Lifecycle),與實際的觀察者(針對Lifecycle),兩種是職責上的區別,因此,職責是可以同時抗在肩上的。
回顧一下Lifecycle的原理,代碼位置如下

當前位置
- LifecycleRegistry.addObserver()
-- ObserverWithState()
--- Lifecycling.getCallback()
static GenericLifecycleObserver getCallback(Object object) {
       .....
        if (object instanceof GenericLifecycleObserver) {
            return (GenericLifecycleObserver) object;
        }
      .....
    }

可見,如果觀察者(針對Lifecycle)是GenericLifecycleObserver的話,Event事件是由觀察者(針對Lifecycle)自行處理的,此類觀察者包攬了以上所述職責。(可以查看Lifecycle體系里的SingleGeneratedAdapterObserver會一目了然,如果都忘了傳送門,對于理解很重要)。

當前位置
- LifecycleBoundObserver.onStateChanged()

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            // 檢查owner狀態
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                // 已銷毀移除,避免內存泄漏
                removeObserver(mObserver);
                return;
            }
            // 檢測觀察者是否處于active狀態,并作出相應對策
            activeStateChanged(shouldBeActive());
        }
}


當前位置
- LifecycleBoundObserver. shouldBeActive()
        @Override
        boolean shouldBeActive() {
            // true標示owner至少處于State.STARTED狀態
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

不解釋

當前位置
- LifecycleBoundObserver. activeStateChanged()
        void activeStateChanged(boolean newActive) {
            // 同一狀態下不做處理
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, so we'd never dispatch anything to inactive
            // owner
            // 更新狀態
            mActive = newActive;
            // 是否有處于active狀態的觀察者
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
             1??
            if (wasInactive && mActive) {
                onActive();
            }
            2??
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            // 通知數據情況
            if (mActive) {
                dispatchingValue(this);
            }
        }

1和2標注部分是指,owner處于State.STARTED、State.RESUMED時觀察者為active狀態,而State.INITIALIZED、State.CREATED為inActive狀態,因此,同種狀態下僅做單次回調。

數據通知

當前位置
LifecycleBoundObserver. dispatchingValue()

    private void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                // 嘗試通知觀察者
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    // 嘗試通知觀察者
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

這里僅需知道dispatchingValue()會根據觀察者的不同,都會嘗試通知觀察者。

當前位置
- LifecycleBoundObserver. dispatchingValue()
-- considerNotify()

    private void considerNotify(ObserverWrapper observer) {
        // 觀察者處于inActive狀態
        if (!observer.mActive) {
            return;
        }
       // 源碼注釋為,檢查觀察者是否響應此次更改的意義在于,雖然觀察者可能
      // 處于響應的狀態,但此時并沒有接收到明確的Event通知,最好不要通知以
      // 保持良好的通知秩序
      
      //  這里可以理解為,防止素亂而引起的可能的內存泄漏的問題,也為了保證
      //  同一狀態下僅做單次通知
        if (!observer.shouldBeActive()) {
            //  這里回到了觸發considerNotify的起點,是為了等待,修復素亂
            observer.activeStateChanged(false);
            return;
        }
      // 數據沒有更改不做通知
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        // 觀察者回調
        observer.mObserver.onChanged((T) mData);
    }

除了代碼中注釋處的解釋,還要知道的信息是,LiveData擁有私有變量mVersion對持有數據版本做了維護,只有檢測到版本更新時才做通知。

此外,在setValue()是也能觸發considerNotify()

    protected void setValue(T value) {
        // 在主線程執行
        assertMainThread("setValue");
        // 數據版本更新
        mVersion++;
        mData = value;
        // 通知
        dispatchingValue(null);
    }

分發過程結束,小結如下:

  • ObserverWrapper與子類負責持有owner與觀察者信息,并實現GenericLifecycleObserver自行處理Event事件
  • Event事件到達時,根據owner的State做LiveData做相應處理,在觀察者處于active狀態時,回調onActive(); 在處于inActive時回調onInactive()
  • owner生命周期轉變時或更新數據時,LiveData向處于active狀態的觀察者進行通知

提醒

文章到這里還沒有結束,對于LiveData而言,注冊防止不僅提供了observe的注冊方式,還提供了observeForever的注冊方式

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer)
public void observeForever(@NonNull Observer<T> observer) 

僅從參數上來看,很容易想到觀察者并沒有針對指定的onwer進行綁定,從之前的分析來看,在onwer銷毀時,觀察者也會一并被銷毀,因此,此處能聯想到,較之Observe的注冊方式,observeForever會注冊的觀察者自身會擁有更廣闊的生命周期。

  • Adds the given observer to the observers list. This call is similar to * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which is always active. This means that the given observer will receive all events and will never be automatically removed. You should manually call {@link#removeObserver(Observer)} to stop observing this LiveData.

摘自源碼對于observeForever的注釋,大意如下:
此方法與observe相似,但是觀察者總會收到事件并且不會被自動移除,需要手動移除。

可見,在使用上以及原理處需要做甄別。

案例

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "T-MainActivity";

    MyData data = BActivity.data;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                data.changeValue(data.getValue() + "~");
            }
        });

        findViewById(R.id.startBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, BActivity.class);
                startActivity(intent);
            }
        });

    }

}

public class BActivity extends AppCompatActivity {

    private static final String TAG = "T-BActivity";

    public static MyData data = new MyData();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
        data.observeForever(new Observer<String>() {
            @Override
            public void onChanged(@Nullable String value) {
                Log.d(TAG, "I'am still here , value is : " + value);
            }
        });
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        finish();
    }
}

頁面描述:在B頁面有LiveData的靜態對象,在按下返回鍵時B會被銷毀。Main頁面有Click Me按鈕,點擊時LiveData的數據加上"~"拼接,并有另一個按鈕以啟動B頁面。
頁面操作:從Main啟動B,按下返回鍵,多次點擊Click Me按鈕,日志如下


日志2.jpg

容易證實,觀察者依舊存在。

observeForever原理

    public void observeForever(@NonNull Observer<T> observer) {
        AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        // 除了和observe()類似的檢測外,還不能是LifecycleBoundObserver類
        if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        wrapper.activeStateChanged(true);
    }

代碼上看,流程是類似的,不過observeForever()并不能使用observe()使用的LifecycleBoundObserver,并且沒有通過Lifecycle.LifecycleRegistry進行注冊,能猜測通過此方式注冊的觀察者,并沒有和實際的owner進行綁定(之前所說的沒有成對出現的情況)。答案會在AlwaysActiveObserver里找到

    private class AlwaysActiveObserver extends ObserverWrapper {

        AlwaysActiveObserver(Observer<T> observer) {
            super(observer);
        }

        @Override
        boolean shouldBeActive() {
            return true;
        }
    }

AlwaysActiveObserver很簡單,僅有shouldBeActive()方法 ,并且結合之前所述的通過Observe()注冊,能知道通過observeForever()注冊的觀察者性質如下:

  • 并沒有利用Lifycycle的機制,所以沒有對生命周期的感知
  • 行為就是簡單的觀察者模式的簡單監聽與回調
  • 有內存泄漏風險,記得手動移除

簡單原理圖

LiveData原理圖-2.png

圖片除了注冊過程以及感知過程外,還畫出了接收過程,因為理解Lifecycle原理真的對理解很重要

提示

文章到這里就結束了,以下部分為小插曲,感興趣歡迎閱讀

插曲

LiveData的優勢在于讓數據感知生命周期的變化以及實現數據的共享。在學習LiveData的過程中,有查閱過一些相應的博文,其中讓我很關注的地方是,有些地方指出,使用單例讓LiveData實現數據共享,并對此不再做必要的解釋,很容易引起混淆和誤解。
單例只是無可奈何之下的一種形式,是最差解。
LiveData的數據共享的精髓在于利用觀察者,而不是利用過長的生命周期。并且,LiveData所要解決的問題是,具有生命周期的組件如何在自身的生命周期里根據必要性的或者重要性的數據狀態,來調整自身的狀態,并實現生命周期監測與數據狀態的解耦。

筆者的呢喃:我都單例了,我都自我共享了,我還需要感知 0 0?

(筆者在??中使用了靜態對象LiveData僅僅是為了方便展示,實際運用中不要這么干)


在此感謝涂哥的指導和答疑


下一篇:ViewModel

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容