四喜德、LiveData

前言

本篇文章主要講解LiveData工作的原理逛绵,如果還不知道LiveData如何用的話欧引,請(qǐng)參考官方文檔频伤。
LiveData的講解涉及到了Lifecycle的知識(shí),如果你還不了解LifeCycle芝此,請(qǐng)參考文檔LifeCycle介紹憋肖。

介紹

LiveData是一個(gè)數(shù)據(jù)持有類因痛,它可以通過添加觀察者被其他組件觀察其變更。不同于普通的觀察者岸更,它最重要的特性就是遵從應(yīng)用程序的生命周期鸵膏,如在Activity中如果數(shù)據(jù)更新了但Activity已經(jīng)是destroy狀態(tài),LivaeData就不會(huì)通知Activity(observer)怎炊。當(dāng)然谭企。LiveData的優(yōu)點(diǎn)還有很多,如不會(huì)造成內(nèi)存泄漏等评肆。

LiveData通常會(huì)配合ViewModel來使用债查,ViewModel負(fù)責(zé)觸發(fā)數(shù)據(jù)的更新,更新會(huì)通知到LiveData瓜挽,然后LiveData再通知活躍狀態(tài)的觀察者攀操。

原理分析

下面直接看代碼:

public class UserProfileViewModel extends ViewModel {
    private String userId;
    private MutableLiveData<User> user;
    private UserRepository userRepo;

    public void init(String userId) {
        this.userId = userId;
        userRepo = new UserRepository();
        user = userRepo.getUser(userId);
    }

    public void refresh(String userId) {
        user = userRepo.getUser(userId);
    }

    public MutableLiveData<User> getUser() {
        return user;
    }

}

上面UserProfileViewModel內(nèi)部持有 UserRepository 中 MutableLiveData<User>的引用,并且提供了獲取 MutableLiveData 的方法 getUser()秸抚,UserRepository 負(fù)責(zé)從網(wǎng)絡(luò)或數(shù)據(jù)庫中獲取數(shù)據(jù)并封裝成 MutableLiveData 然后提供給 ViewModel速和。

我們?cè)?UserProfileFragment 中為 MutableLiveData<User> 注冊(cè)觀察者,如下:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    String userId = getArguments().getString(UID_KEY);
    viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
    viewModel.init(userId);
    //標(biāo)注1
    viewModel.getUser().observe(UserProfileFragment.this, new Observer<User>() {
        @Override
        public void onChanged(@Nullable User user) {
            if (user != null) {
                tvUser.setText(user.toString());
            }
        }
    });
}

看標(biāo)注1處剥汤,viewModel.getUser()獲取到 MutableLiveData<User> 也就是我們的 LiveData颠放,然后調(diào)用 LiveData的observer方法,并把UserProfileFragment作為參數(shù)傳遞進(jìn)去吭敢。observer() 方法就是我們分析的入口了碰凶,接下來我們看LiveData的observer()方法都做了什么:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    //標(biāo)注1
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    //標(biāo)注2
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    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);
}

可以看到,UserProfileFragment 是作為 LifeCycleOwner 參數(shù)傳進(jìn)來的鹿驼,如果你的support包版本大于等于26.1.0欲低,support包中的 Fragment 會(huì)默認(rèn)繼承自 LifecycleOwner,而 LifecycleOwner 可獲取到該組件的 LifeCycle畜晰,也就知道了 UserProfileFragment 組件的生命周期(在這里默認(rèn)大家已經(jīng)了解過LifeCycle了)砾莱。

看標(biāo)注1處,如果我們的 UserProfileFragment 組件已經(jīng)是destroy狀態(tài)的話凄鼻,將直接返回腊瑟,不會(huì)被加入觀察者行列。如果不是destroy狀態(tài)块蚌,就到標(biāo)注2處闰非,新建一個(gè) LifecycleBoundObserver 將我們的 LifecycleOwner 和 observer保存起來,然后調(diào)用 mObservers.putIfAbsent(observer, wrapper) 將observer和wrapper分別作為key和value存入Map中峭范,putIfAbsent()方法會(huì)判斷如果 value 已經(jīng)能夠存在财松,就返回,否則返回null纱控。
如果返回existing為null辆毡,說明以前沒有添加過這個(gè)觀察者政敢,就將 LifecycleBoundObserver 作為 owner 生命周期的觀察者,也就是作為 UserProfileFragment 生命周期的觀察者胚迫。

我們看下LifecycleBoundObserver 源碼:

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

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
        super(observer);
        mOwner = owner;
    }

    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

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

    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }

    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}

代碼并不多喷户,LifecycleBoundObserver 繼承自 ObserverWrapper 并實(shí)現(xiàn)了 GenericLifecycleObserver接口,而 GenericLifecycleObserver 接口又繼承自 LifecycleObserver 接口访锻,那么根據(jù) Lifecycle 的特性褪尝,實(shí)現(xiàn)了LifecycleObserver接口并且加入 LifecycleOwner 的觀察者里就可以感知或主動(dòng)獲取 LifecycleOwner 的狀態(tài)。

好了期犬,看完了觀察者河哑,那么我們的LiveData什么時(shí)候會(huì)通知觀察者呢?不用想龟虎,肯定是數(shù)據(jù)更新的時(shí)候璃谨,而數(shù)據(jù)的更新是我們代碼自己控制的,如請(qǐng)求網(wǎng)絡(luò)返回User信息后鲤妥,我們會(huì)主動(dòng)將User放入MutableLiveData中佳吞,這里我在UserRepository中直接模擬網(wǎng)絡(luò)請(qǐng)求如下:

public class UserRepository {
    final MutableLiveData<User> data = new MutableLiveData<>();

    public MutableLiveData<User> getUser(final String userId) {
        if ("xiasm".equals(userId)) {
            data.setValue(new User(userId, "夏勝明"));
        } else if ("123456".equals(userId)) {
            data.setValue(new User(userId, "哈哈哈"));
        } else {
            data.setValue(new User(userId, "unknow"));
        }
        return data;
    }
}

當(dāng)調(diào)用getUser()方法的時(shí)候,我們調(diào)用MutableLiveData的setValue()方法將數(shù)據(jù)放入LiveData中棉安,這里MutableLiveData實(shí)際上就是繼承自LiveData底扳,沒有什么特別:

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

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

setValue()在放入U(xiǎn)ser的時(shí)候必須在主線程,否則會(huì)報(bào)錯(cuò)贡耽,而postValue則沒有這個(gè)檢查衷模,而是會(huì)把數(shù)據(jù)傳入到主線程。我們直接看setValue()方法:

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

首先調(diào)用assertMainThread()檢查是否在主線程蒲赂,接著將要更新的數(shù)據(jù)賦給mData阱冶,然后調(diào)用 dispatchingValue()方法并傳入null,將數(shù)據(jù)分發(fā)給各個(gè)觀察者滥嘴,如我們的 UserProfileFragment木蹬。看 dispatchingValue()方法實(shí)現(xiàn):

private void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        //標(biāo)注1
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            //標(biāo)注2
            for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

從標(biāo)注1可以看出氏涩,dispatchingValue()參數(shù)傳null和不傳null的區(qū)別就是如果傳null將會(huì)通知所有的觀察者届囚,反之僅僅通知傳入的觀察者有梆。我們直接看標(biāo)注2是尖,通知所有的觀察者通過遍歷 mObservers ,將所有的 ObserverWrapper 拿到泥耀,實(shí)際上就是我們上面提到的 LifecycleBoundObserver饺汹,通知觀察者調(diào)用considerNotify()方法,這個(gè)方法就是通知的具體實(shí)現(xiàn)了痰催。

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

如果觀察者不是活躍狀態(tài)兜辞,將不會(huì)通知此觀察者迎瞧,看最后一行,observer.mObserver.onChanged((T) mData)逸吵,observer.mObserver就是我們調(diào)用LiveData的observer()方法傳入的 Observer凶硅,然后調(diào)用 Observer 的 onChanged((T) mData)方法,將保存的數(shù)據(jù)mData傳入扫皱,也就實(shí)現(xiàn)了更新足绅。在看下我們實(shí)現(xiàn)的Observer:

viewModel.getUser().observe(UserProfileFragment.this, new Observer<User>() {
    @Override
    public void onChanged(@Nullable User user) {
        if (user != null) {
            tvUser.setText(user.toString());
        }
    }
});

如果哪個(gè)控件要根據(jù)user的變更而及時(shí)更新,就在onChanged()方法里處理就可以了韩脑。到這里氢妈,LiveData已經(jīng)能夠分析完了,其實(shí)LiveData的實(shí)現(xiàn)還是要依賴于Lifecycle段多。

原文鏈接:http://www.reibang.com/p/21bb6c0f8a5a

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末首量,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子进苍,更是在濱河造成了極大的恐慌加缘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件觉啊,死亡現(xiàn)場(chǎng)離奇詭異生百,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)柄延,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門蚀浆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搜吧,你說我怎么就攤上這事市俊。” “怎么了滤奈?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵摆昧,是天一觀的道長。 經(jīng)常有香客問我蜒程,道長绅你,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任昭躺,我火速辦了婚禮忌锯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘领炫。我一直安慰自己偶垮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著似舵,像睡著了一般脚猾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上砚哗,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天龙助,我揣著相機(jī)與錄音,去河邊找鬼蛛芥。 笑死泌参,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的常空。 我是一名探鬼主播沽一,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼漓糙!你這毒婦竟也來了铣缠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤昆禽,失蹤者是張志新(化名)和其女友劉穎蝗蛙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體醉鳖,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捡硅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盗棵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壮韭。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纹因,靈堂內(nèi)的尸體忽然破棺而出喷屋,到底是詐尸還是另有隱情,我是刑警寧澤瞭恰,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布屯曹,位于F島的核電站,受9級(jí)特大地震影響惊畏,放射性物質(zhì)發(fā)生泄漏恶耽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一颜启、第九天 我趴在偏房一處隱蔽的房頂上張望偷俭。 院中可真熱鬧,春花似錦农曲、人聲如沸社搅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽形葬。三九已至,卻和暖如春暮的,著一層夾襖步出監(jiān)牢的瞬間笙以,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工冻辩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猖腕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓恨闪,卻偏偏與公主長得像倘感,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咙咽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354