Android Jetpack LiveData

導(dǎo)語(yǔ)

Jetpack簡(jiǎn)介及其它組件文章
還在擔(dān)心View層的內(nèi)存泄漏嗎狂魔?還在擔(dān)心一些空指針異常嗎?LiveData幫你解決這些問(wèn)題。

主要內(nèi)容

  • LiveData是什么
  • LiveData的基本使用
  • postValue和setValue有什么區(qū)別
  • observe是如何收到通知的
  • LiveData的粘性事件
  • 如何解決LiveData的粘性事件

具體內(nèi)容

LiveData是什么

  • LiveData是一個(gè)數(shù)據(jù)持有類
  • 能夠感知組件的生命周期
  • 持有的數(shù)據(jù)可以被觀察者觀察

簡(jiǎn)單地來(lái)說(shuō)就是當(dāng)LiveData持有的數(shù)據(jù)發(fā)生改變的時(shí)候题涨,觀察者在生命周期處于STARTED或RESUMED狀態(tài)時(shí),LiveData會(huì)更新通知給活躍的觀察者菠隆。

LiveData的基本使用

public class MainActivity extends AppCompatActivity {

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

        //創(chuàng)建一個(gè)LiveData
        MutableLiveData<String> mutableLiveData = new MutableLiveData<>();
        //注冊(cè)觀察者
        mutableLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.d("LiveData", s);
            }
        });
        //其它線程執(zhí)行完任務(wù)對(duì)LiveData進(jìn)行通知
        new Thread(new Runnable() {
            @Override
            public void run() {
                //postValue() 可以在任意線程使用
                mutableLiveData.postValue("通知");
                //setValue() 只能在主線程被調(diào)用
//                mutableLiveData.setValue("通知");
            }
        }).start();
    }

}

從上面的代碼可以看出譬重,LiveData的基本使用很簡(jiǎn)單,同時(shí)我在代碼中注釋講setValue()只能在主線程被調(diào)用簿训,接下來(lái)我們深入源碼再看一下咱娶。

postValue和setValue有什么區(qū)別

我們可以分別點(diǎn)擊postValue和setValue方法,進(jìn)入它們的源碼進(jìn)行觀察强品。

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 * <p>
 * This method must be called from the main thread. If you need set a value from a background
 * thread, you can use {@link #postValue(Object)}
 *
 * @param value The new value
 */
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

我們可以先看setValue()膘侮,可以看到setValue()有個(gè)注解@MainThread,方法上面也有注釋This method must be called from the main thread.的榛,都表明了這個(gè)方法只能在主線程被調(diào)用琼了。
我們?cè)俑櫼幌聀ostValue方法。postValue方法執(zhí)行了一行比較重要的代碼:ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);看樣子是交給主線程去執(zhí)行一個(gè)Runnable夫晌。我們進(jìn)入這個(gè)Runnable看一下雕薪。

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        setValue((T) newValue);
    }
};

我們可以看到,Runnable最終又調(diào)用了setValue()晓淀。
看到這里我們就知道了所袁,postValue()setValue()的作用是相同的,區(qū)別是postValue()可以在任意線程調(diào)用凶掰,setValue()只能在主線程調(diào)用燥爷。
因?yàn)?code>postValue()可以在任意線程調(diào)用,所以mPendingData使用了volatile關(guān)鍵字锄俄,同時(shí)通過(guò)synchronized對(duì)代碼塊進(jìn)行加鎖局劲,來(lái)保證線程安全。
可以通過(guò)setValue方法看到奶赠,mData才是我們真正的數(shù)據(jù)持有者鱼填。

observe是如何收到通知的

我們來(lái)直接看源碼,我們先看LiveData.observe()方法毅戈。

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    LiveData.LifecycleBoundObserver wrapper = new LiveData.LifecycleBoundObserver(owner, observer);
    LiveData.ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}

其中LifecycleBoundObserver持有了我們傳入的Observer<? super T> observer苹丸,最后放到了mObservers這個(gè)對(duì)象了愤惰,這個(gè)對(duì)象是一個(gè)Map,所以我們可以一對(duì)多赘理。
owner.getLifecycle().addObserver(wrapper)這個(gè)代碼不重點(diǎn)講了宦言,這個(gè)是Lifecycle提供的功能,如果不了解可以看我其它文章或者去網(wǎng)上找一下商模〉焱總之就是把wrapperLifecycleOwner的生命周期進(jìn)行了綁定。
我們?cè)賮?lái)看setValue()方法是如何通知的施流。

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

void dispatchingValue(@Nullable LiveData.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<? super T>, LiveData.ObserverWrapper>> iterator =
                 mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

private void considerNotify(LiveData.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);
}

這段代碼比較長(zhǎng)响疚,不要慌,很簡(jiǎn)單瞪醋,有些代碼不重要直接往下看忿晕。
直接看dispatchingValue(null)到底做了啥。首先我們傳入了null银受,所以會(huì)走else践盼,我們看到這里使用Iterator對(duì)mObservers進(jìn)行遍歷,之前說(shuō)到mObservers是一個(gè)map宾巍,然后取出值執(zhí)行considerNotify(iterator.next().getValue())方法咕幻,在considerNotify()里,最終調(diào)用observer.mObserver.onChanged((T) mData)蜀漆,把結(jié)果回調(diào)給我們業(yè)務(wù)層谅河。

LiveData的粘性事件

我們來(lái)看一段代碼咱旱,想一下Log會(huì)如何輸出确丢。

public class MainActivity extends AppCompatActivity {

    MutableLiveData<String> mutableLiveData;

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

        mutableLiveData = new MutableLiveData<>();
        mutableLiveData.postValue("----------------》通知1");
    }

    //點(diǎn)擊事件   寫在xml里了
    public void observe(View view) {
        mutableLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.d("LiveData", s);
            }
        });
        mutableLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.d("LiveData", s);
            }
        });
    }

}

我們有一個(gè)點(diǎn)擊事件,放到了xml里吐限,我們先postValue()鲜侥,通過(guò)點(diǎn)擊按鈕再注冊(cè)observer,會(huì)怎樣呢诸典?

2021-03-10 16:52:07.251 25476-25476/com.example.jetpack D/LiveData: ----------------》通知1
2021-03-10 16:52:07.251 25476-25476/com.example.jetpack D/LiveData: ----------------》通知1

居然也收到了通知描函,這是為什么呢?我們來(lái)通過(guò)源碼看一下狐粱。
我們?cè)?code>LiveData.observe()的時(shí)候舀寓,創(chuàng)建了LifecycleBoundObserverLifecycleBoundObserver繼承ObserverWrapper肌蜻,我們直接來(lái)看LifecycleBoundObserverObserverWrapper的代碼互墓。

class LifecycleBoundObserver extends LiveData.ObserverWrapper implements GenericLifecycleObserver {

    //...
    
    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }

    //...
    
}
private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;

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

    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

    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);
        }
    }
}

LifecycleBoundObserver最終還繼承了LifecycleEventObserverLifecycleEventObserver有一個(gè)方法onStateChanged()蒋搜,首先要了解onStateChanged()方法是Lifecycle提供的功能篡撵,如果不了解可以看我其它文章或者去網(wǎng)上找一下判莉。總之就是在Lifecycle的生命周期發(fā)生改變的時(shí)候調(diào)用育谬,所以當(dāng)LiveData.observer()的時(shí)候券盅,就會(huì)調(diào)用到onStateChanged(),然后再調(diào)用到activeStateChanged()膛檀,最終執(zhí)行了dispatchingValue(this)锰镀,這句很熟悉呀,就是和setValue()最終執(zhí)行的是一樣的代碼塊咖刃。不同的是傳入的參數(shù)不為空了互站,所以直接執(zhí)行considerNotify(initiator),不用遍歷了僵缺。我們?cè)賮?lái)看一下這個(gè)方法我們還沒(méi)有講到的地方胡桃。

private void considerNotify(LiveData.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);
}

我們一直向下走,直接來(lái)到considerNotify方法磕潮,重要的一行代碼是observer.mLastVersion >= mVersion翠胰,分別點(diǎn)擊兩個(gè)version去看一下,就明白了自脯,分別是ObserverWrapper的版本號(hào)和LiveData的版本號(hào)之景,ObserverWrapper的版本號(hào)大于LiveData的版本號(hào)時(shí),我們就返回膏潮,反之則執(zhí)行锻狗,也就是說(shuō)在什么情況下會(huì)執(zhí)行呢?
舉例我們剛才的事例代碼焕参,當(dāng)LiveData創(chuàng)建的時(shí)候轻纪,mVersion的版本號(hào)為-1,當(dāng)LiveData執(zhí)行setValue的時(shí)候叠纷,執(zhí)行mVersion++結(jié)果為0刻帚,剛創(chuàng)建ObserverWrapper的時(shí)候,mLastVersion的版本是-1涩嚣,這個(gè)時(shí)候-1 >= 0的結(jié)果為false崇众,所以向下執(zhí)行不會(huì)return,把mVersion賦值給ObserverWrappermLastVersion航厚,防止多次被回調(diào)顷歌,最終執(zhí)行observer.mObserver.onChanged((T) mData)

如何解決LiveData的粘性事件

網(wǎng)上大多數(shù)的方案是通過(guò)反射幔睬,在第一次observer的時(shí)候眯漩,修改ObserverWrappermLastVersion的值。
我的解決方案有點(diǎn)不太一樣溪窒,通過(guò)代理模式攔截observer坤塞。
我也上傳到github了冯勉,可以給俺點(diǎn)幾個(gè)小星星,謝謝摹芙。

/**
 * Created by guoshichao on 2021/3/10
 * 非粘性LiveData事件
 */
public class NonStickyLiveData<T> extends MutableLiveData<T> {

    static final int START_VERSION = -1;

    private int mVersion = START_VERSION;

    @Override
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        ObserverProxy observerProxy = new ObserverProxy<>(observer);
        observerProxy.preventNextEvent = mVersion > START_VERSION;
        super.observe(owner, observerProxy);
    }

    @Override
    public void setValue(T value) {
        mVersion++;
        super.setValue(value);
    }

    private class ObserverProxy<T> implements Observer<T> {

        @NonNull
        private final Observer<T> observer;
        private boolean preventNextEvent = false;

        ObserverProxy(@NonNull Observer<T> observer) {
            this.observer = observer;
        }

        @Override
        public void onChanged(@Nullable T t) {
            if (preventNextEvent) {
                preventNextEvent = false;
                return;
            }
            observer.onChanged(t);
        }
    }

}

如果這樣寫有問(wèn)題或者會(huì)出現(xiàn)bug灼狰,歡迎大家聯(lián)系我。

更多內(nèi)容戳這里(整理好的各種文集)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浮禾,一起剝皮案震驚了整個(gè)濱河市交胚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盈电,老刑警劉巖蝴簇,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異匆帚,居然都是意外死亡熬词,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門吸重,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)互拾,“玉大人,你說(shuō)我怎么就攤上這事嚎幸⊙湛螅” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嫉晶,是天一觀的道長(zhǎng)骑疆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)替废,這世上最難降的妖魔是什么箍铭? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮舶担,結(jié)果婚禮上坡疼,老公的妹妹穿的比我還像新娘。我一直安慰自己衣陶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布闸氮。 她就那樣靜靜地躺著剪况,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒲跨。 梳的紋絲不亂的頭發(fā)上译断,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音或悲,去河邊找鬼孙咪。 笑死堪唐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的翎蹈。 我是一名探鬼主播淮菠,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荤堪!你這毒婦竟也來(lái)了合陵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤澄阳,失蹤者是張志新(化名)和其女友劉穎拥知,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碎赢,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡低剔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肮塞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片户侥。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖峦嗤,靈堂內(nèi)的尸體忽然破棺而出蕊唐,到底是詐尸還是另有隱情,我是刑警寧澤烁设,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布替梨,位于F島的核電站,受9級(jí)特大地震影響装黑,放射性物質(zhì)發(fā)生泄漏副瀑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一恋谭、第九天 我趴在偏房一處隱蔽的房頂上張望糠睡。 院中可真熱鬧,春花似錦疚颊、人聲如沸狈孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)均抽。三九已至,卻和暖如春其掂,著一層夾襖步出監(jiān)牢的瞬間油挥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留深寥,地道東北人攘乒。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像惋鹅,于是被迫代替她去往敵國(guó)和親则酝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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