Android jetpack - LiveData 可觀察數(shù)據(jù)存儲(chǔ)類

一、前言

LiveData 可觀察數(shù)據(jù)存儲(chǔ)類屬于谷歌在2018推出Android jetpack(外網(wǎng))其中的軟件架構(gòu)組件中的一個(gè)。在谷歌開發(fā)者網(wǎng)站有詳細(xì)介紹LiveData(外網(wǎng))。本文以總結(jié)為目的,采用更加清晰和有條理的方式解讀LiveData。
閱讀前準(zhǔn)備
便于理解,如果你已經(jīng)知道什么是Lifecycle、觀察者模式ViewModel 更好,那么理解此篇文章便輕車熟路。
避免過多而雜的介紹,部分知識(shí)不詳述,請(qǐng)閱讀筆者鏈接文章或者Google。

全文就一個(gè)核心
LiveData是結(jié)合Lifecycle和觀察者模式的數(shù)據(jù)存儲(chǔ)類

二、LiveData

谷歌爸爸的介紹
LiveData是一種可觀察的數(shù)據(jù)存儲(chǔ)器類。與常規(guī)的可觀察類不同,LiveData 具有生命周期感知能力,意指它遵循其他應(yīng)用組件(如 Activity、Fragment 或 Service)的生命周期。這種感知能力可確保 LiveData 僅更新處于活躍生命周期狀態(tài)的應(yīng)用組件觀察者。

簡(jiǎn)單理解
LiveData就是結(jié)合生命周期+觀察者模式的數(shù)據(jù)存儲(chǔ)類。方便我們根據(jù)生命周期來監(jiān)聽數(shù)據(jù)變化的。由于這個(gè)結(jié)構(gòu)的特性,所以它擁有以下特點(diǎn)
1、確保界面符合數(shù)據(jù)狀態(tài);2、不會(huì)發(fā)生內(nèi)存泄露;3、不會(huì)因 Activity 停止而導(dǎo)致崩潰;4、不再需要手動(dòng)處理生命周期;5、數(shù)據(jù)始終保持最新狀態(tài);6、適當(dāng)?shù)呐渲酶模?、共享資源

導(dǎo)入

implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"

三、詳細(xì)介紹

LiveData介紹就如下幾個(gè)點(diǎn)

  • LiveData基本使用
  • LiveData自定義LiveData
  • LiveDa數(shù)據(jù)轉(zhuǎn)換
  • LiveData合并多個(gè)數(shù)據(jù)

3.1、基本使用

這里說下為啥別的文章或者官網(wǎng)都是結(jié)合ViewModel來講的例子,ViewModel就是存儲(chǔ)和管理界面數(shù)據(jù)的一個(gè)類。我們LiveData就是一種數(shù)據(jù)。如果不和ViewModel一起講,不影響我們理解LiveData的核心思想。但是并不推薦直接在Activity或者Fragment里直接寫LiveData變量來存儲(chǔ)數(shù)據(jù)。這會(huì)讓他們過于臃腫,所以還是用推薦存儲(chǔ)數(shù)據(jù)的方式ViewModel。
如果你還不了解ViewModel你可以簡(jiǎn)單理解為它就是存儲(chǔ)數(shù)據(jù)的一個(gè)地方。

LiveData在使用上除了和ViewModel結(jié)合以外無非觀察者模式三個(gè)步驟

  • 創(chuàng)建LiveData,即創(chuàng)建被觀察者
  • 添加Observe,添加觀察者
  • 數(shù)據(jù)改變是修改UI,觀察者監(jiān)聽
public class MyViewModel extends ViewModel {
    MutableLiveData<String> name;

    public MutableLiveData<String> getName() {
        if (name == null) {
            name = new MutableLiveData<>();
        }
        return name;
    }
}

    ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication());
    ViewModelProvider viewModelProvider = new ViewModelProvider(this, factory);
    viewModel = viewModelProvider.get(MyViewModel.class);
    viewModel.getName().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            textView.setText(s);
        }
    });

  viewModel.getName().setValue("yink");

1、MyViewModel就是我們創(chuàng)建LiveData的地方。
2、viewModel.getName()拿到LiveData對(duì)象,observer添加觀察者,this是Lifecycle生命周期組件。
3、當(dāng)數(shù)據(jù)變化setValue時(shí),會(huì)自動(dòng)調(diào)用觀察者的onChanged方法,我們就可以更新UI或其它操作了。

  • 其中在理解ViewModel上可不必太追究細(xì)節(jié),理解為創(chuàng)建對(duì)象即可
  • 至于observe的參數(shù)this參數(shù),帶入的是Lifecycle,不太理解生命周期請(qǐng)移步Lifecycle
  • 為啥用MutableLiveData對(duì)象是因?yàn)镸utableLiveData提供set,get方法。而LiveData只對(duì)外開放get方法,LiveData的set,get代碼如下:
protected void setValue(T value) {

public T getValue() {

3.2、自定義LiveData

public class MyLiveData<T> extends MutableLiveData<T> {
    @Override
    protected void onActive() {
        super.onActive();
        Log.d("yink","onActive");
    }

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

自定義LiveData的時(shí)候,跟我們其它繼承用法一樣。說下onActive和onInactive什么時(shí)候調(diào)用

  • onActive 兩個(gè)條件:添加了observe + 生命周期alive。
  • onInactive 移除了observe,或者生命周期非alive。
    例:點(diǎn)擊home到后臺(tái):onInactive; 重新進(jìn)入頁面:onActive ; 移除observe:onInactive

3.3、轉(zhuǎn)換LiveData

Livecycle給我提供了兩種方法來轉(zhuǎn)換LiveData
Transformations.map:把LiveData的值改變一下,目的是轉(zhuǎn)換值
Transformations.switchMap:每次改變值時(shí)提供一個(gè)全新的liveData對(duì)象并監(jiān)聽這個(gè)新的liveData對(duì)象
下面我用相同的例子來講解一下他們的區(qū)別,便于大家理解
我有一個(gè)MutableLiveData<Integer> adressNumber;來表示區(qū)號(hào),然后我們要轉(zhuǎn)換這個(gè)區(qū)號(hào)為當(dāng)前哪個(gè)市區(qū)。于是有下面的代碼

MutableLiveData<Integer> adressNumber;
    MutableLiveData<String> adressStringOne = (MutableLiveData<String>) Transformations.map(adressNumber, new Function<Integer, String>() {
        @Override
        public String apply(Integer input) {
            return getAdressString(input);
        }
    });

    MutableLiveData<String> adressStringTwo = (MutableLiveData<String>) Transformations.switchMap(adressNumber, new Function<Integer, LiveData<String>>() {
        @Override
        public LiveData<String> apply(Integer input) {
            MutableLiveData mutableLiveData = new MutableLiveData<String>();
            mutableLiveData.setValue(getAdressString(input));
            return mutableLiveData;
        }
    });

    private String getAdressString(Integer number) {
        if (number == 0755) return "深圳";
        return "其它";
    }

例子很簡(jiǎn)單,就是輸入?yún)^(qū)號(hào)0755,查詢出來是深圳。
map用法:
1、adressStringOne這個(gè)LiveData的值就是一個(gè)String,通過輸入的0755來轉(zhuǎn)換成”深圳“
2、adressStringOne對(duì)象沒有變,每次監(jiān)聽adressNumber來設(shè)置自己的值
switchMap用法:
1、adressStringTwo這個(gè)LiveData同樣擁有一個(gè)String值,通過輸入的0755來轉(zhuǎn)換成”深圳“
2、adressStringTwo和map用法不同的是,他拿到”深圳”這個(gè)值后,不是直接設(shè)置,而是new一個(gè)新的LiveData<String>,adressStringTwo來監(jiān)聽LiveData<String>變化并且和新的LiveData<String>的值保持同步。
原理
原理也比較簡(jiǎn)單,貼出源碼結(jié)合我上面的例子就很好理解了。

public interface Function<I, O> {
    O apply(I input);
}

@MainThread
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

1、Function.apply只是方便我們自己去轉(zhuǎn)換值
2、MediatorLiveData對(duì)象繼承自MutableLiveData它提供的addSource方法可以觀察LiveData對(duì)象,這也就是map和switchMap的關(guān)鍵實(shí)現(xiàn)。讓LiveData可以觀察LiveData
3、傳入source,被觀察者,它一變化就調(diào)用onChanged方法。在onChanged里MediatorLiveData設(shè)置新的值
4、流程簡(jiǎn)單總結(jié)就是:對(duì)象A來觀察B的值,根據(jù)B的變化,A設(shè)置一個(gè)新的值,A觀察B

@MainThread
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }

1、Function.applyMediatorLiveData和上面map一個(gè)用法
2、當(dāng)?shù)谝淮蝬變化,進(jìn)入onChanged,mSource = newLiveData,MediatorLiveData就會(huì)根據(jù)新的newLiveData來監(jiān)聽它的變化,addSource持值同步。
3、當(dāng)?shù)诙蝬變化,進(jìn)入onChanged,mSource = newLiveData根據(jù)java局部變量特性,newLiveData是不同于第一次x變化時(shí)的newLiveData對(duì)象。此時(shí)會(huì)result.removeSource(mSource);移除之前的監(jiān)聽,再重新監(jiān)聽這個(gè)新newLiveData的值,并且addSource保持值同步。
4、switchMapFunction的代碼從頭到尾,result.addSource了兩次,也就是有兩個(gè)監(jiān)聽
5、流程簡(jiǎn)單總結(jié)就是:A觀察B的值,B變化必然創(chuàng)建一個(gè)新的C,A觀察C。所以A同時(shí)觀察B和C,只不過這個(gè)B每次變化會(huì)導(dǎo)致A監(jiān)聽一個(gè)新的C
6、當(dāng)然如果你寫switchMapFunction.apply(x);實(shí)現(xiàn)的時(shí)候,返回的是同一個(gè)對(duì)象,那么C也不會(huì)變。

3.4、合并多個(gè)LiveData

合并多個(gè)LiveData目的很明確,比如一個(gè)按鈕需要兩個(gè)LiveData狀態(tài)來確定, 你就需要同時(shí)監(jiān)聽兩個(gè)LiveData才行。直接拋出一個(gè)例子,很簡(jiǎn)單就不詳解了。

        mediatorLiveData.addSource(addressNumber, new Observer() {
            @Override
            public void onChanged(Object o) {
                Address address = (Address)mediatorLiveData.getValue();
                address.setNum((Integer)o);
                mediatorLiveData.setValue(address);
            }
        });
        mediatorLiveData.addSource(addressString, new Observer() {
            @Override
            public void onChanged(Object o) {
                Address address = (Address)mediatorLiveData.getValue();
                address.setString((String) o);
                mediatorLiveData.setValue(address);
            }
        });
        mediatorLiveData.observe(this, new Observer() {
            @Override
            public void onChanged(Object o) {
                
            }
        });

class Address {
        Integer num;
        String string;
        ...
}

四、寫在最后

整體來看LiveData還是一個(gè)很簡(jiǎn)單的控件,它的核心思想 = Lifecycle + 觀察者模式。優(yōu)點(diǎn)離不開它的核心思想,簡(jiǎn)單來說生命周期有效性不用我們過多擔(dān)心了,還有就是觀察者模式的優(yōu)點(diǎn)讓我們刷新UI變的更便利,能給我們省去不少邏輯代碼。所以它還是很巧妙的一個(gè)控件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,687評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評(píng)論 2 374

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