Android架構(gòu)組件之LiveData

如果你看過(guò)了Android架構(gòu)組件之Lifecycle,可以立馬投入到LiveData組件的學(xué)習(xí)中,同樣的,LiveData也是Google I/O 大會(huì)上發(fā)布的架構(gòu)組件膛腐,ListData是一個(gè)可被觀察的數(shù)據(jù)持有類,為我們什么需要使用LiveData?主要有以下幾個(gè)有點(diǎn):
更多參考

一鼎俘,保證數(shù)據(jù)與界面的實(shí)時(shí)更新

LiveData采用了觀察者模式設(shè)計(jì)哲身,其中LiveData是被觀察者,當(dāng)數(shù)據(jù)發(fā)生變化時(shí)會(huì)通知觀察者進(jìn)行數(shù)據(jù)更新贸伐。通過(guò)這點(diǎn)勘天,可以確保數(shù)據(jù)和界面的實(shí)時(shí)性。

二捉邢,有效避免內(nèi)存泄漏

這是因?yàn)?code>LiveData能夠感知到組件的生命周期脯丝,當(dāng)組件狀態(tài)處于DESTROYED狀態(tài)時(shí),觀察者對(duì)象會(huì)被remove伏伐。

三宠进,Activity/Fragment銷毀掉時(shí)不會(huì)引起崩潰

這是因?yàn)榻M件處于非激活狀態(tài)時(shí),在界面不會(huì)收到來(lái)自LiveData的數(shù)據(jù)變化通知藐翎。這樣規(guī)避了很多因?yàn)轫?yè)面銷毀之后砰苍,修改UI導(dǎo)致的crash潦匈。

四,不需要手動(dòng)處理生命周期

LiveData能夠感知組件的生命周期赚导,所以設(shè)置LiveData組件的生命周期狀態(tài)。

五赤惊,始終能夠保持最新數(shù)據(jù)

生命周期從非活躍狀態(tài)切換到活躍狀態(tài)的時(shí)候吼旧,能夠?qū)崟r(shí)的接收最新的數(shù)據(jù)。

六未舟,能夠應(yīng)對(duì)配置更改

由于LiveData保存數(shù)據(jù)的時(shí)候圈暗,組件和數(shù)據(jù)是分離的,所以在配置更改(如橫豎屏切換等)的時(shí)候裕膀,即便組件被重新創(chuàng)建员串,因?yàn)閿?shù)據(jù)還保存在LiveData中,這樣也能夠做到實(shí)時(shí)的更新昼扛。

七寸齐,資源共享

單例模式擴(kuò)展LiveData對(duì)象并包裝成系統(tǒng)服務(wù),以便在應(yīng)用程序中進(jìn)行共享抄谐,需要該資源的只需要觀察LiveData即可渺鹦。

LiveData的使用

相關(guān)Gradle配置參考

通常使用LiveData有三個(gè)步驟:
1,創(chuàng)建LiveData實(shí)例來(lái)保存數(shù)據(jù)蛹含,常常是配合ViewModel一起工作毅厚;
2,定義一個(gè)Observer的觀察者對(duì)象浦箱,如果有數(shù)據(jù)更新會(huì)通過(guò)觀察者的onChanged()方法來(lái)同步到UI上面吸耿;
3,將觀察者Observer通過(guò)observe()方法進(jìn)行綁定酷窥。

LiveData有兩種使用方法:一種是直接使用咽安,如接下來(lái)的例子;還有一種是繼承LiveData的實(shí)現(xiàn)資源共享的方式竖幔。
直接使用的時(shí)候板乙,LiveData一般和ViewModel一起使用。
首先定義個(gè)MyNameViewModel

class MyNameViewModel : ViewModel() {
    // Create a LiveData with a String
    private var mCurrentName: MutableLiveData<String>? = null
    // Create a LiveData with a String list
    private var mNameListData: MutableLiveData<List<String>>? = null

    open fun currentName(): MutableLiveData<String> {
        if (mCurrentName == null) {
            mCurrentName = MutableLiveData()
        }
        return mCurrentName as MutableLiveData<String>
    }

    open fun nameList(): MutableLiveData<List<String>> {
        if (mNameListData == null) {
            mNameListData = MutableLiveData()
        }
        return mNameListData as MutableLiveData<List<String>>
    }
}
class FirstActivity : AppCompatActivity() {
    companion object {
        val TAG = FirstActivity.javaClass.simpleName
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val myNameViewModel = ViewModelProviders.of(this).get(MyNameViewModel::class.java)
        myNameViewModel.currentName().observe(this, Observer {
            print(it)
        })

        myNameViewModel.nameList().observe(this, Observer {
            if (it != null) {
                for (item in it) {
                    print(item)
                }
            }
        })
    }

ViewModel里面拳氢,定義兩個(gè)方法募逞,currentName,和nameList兩個(gè)方法,并返回LiveData對(duì)象馋评。然后在activity中通過(guò)ViewModelProviders.of(this).get(MyNameViewModel::class.java)拿到viewModel,最后通過(guò)observe設(shè)置監(jiān)聽(tīng)放接,observe方法里面的兩個(gè)參數(shù)LifecycleOwner ownerObserver<T>,最后在onChanged方法中回調(diào)數(shù)據(jù)(這里kotlin代碼使用的是lamdba表達(dá)式)留特。

用兩個(gè)按鈕模仿修改ViewModel中保存的LiveData數(shù)據(jù):

   btn_change_name.setOnClickListener {
            myNameViewModel.currentName().setValue("Hubery")
   }

    btn_update_list.setOnClickListener {
          var nameList = ArrayList<String>()
            for (i in 0..9) {
                nameList.add("Hubery<$i>")
            }
            myNameViewModel.nameList().setValue(nameList)
    }

現(xiàn)在來(lái)看看LiveData資源共享纠脾,也就是繼承LiveData的例子

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val mStockManager: StockManager

    private val mListener = object : SimplePriceListener() {
        fun onPriceChanged(price: BigDecimal) {
            setValue(price)
        }
    }

    init {
        mStockManager = StockManager(symbol)
    }

    override fun onActive() {
        mStockManager.requestPriceUpdates(mListener)
    }

    override fun onInactive() {
        mStockManager.removeUpdates(mListener)
    }
}

當(dāng)LiveData對(duì)象具有活動(dòng)的觀察者時(shí)調(diào)用OnActive方法玛瘸。LiveData中的數(shù)據(jù)會(huì)調(diào)用setValue方法去更新。
當(dāng)LiveData在沒(méi)有任何的Observer監(jiān)聽(tīng)的時(shí)候苟蹈,會(huì)調(diào)用Inactive方法糊渊,在這里的例子會(huì)removeUpdate方法。

LiveData的原理

借鑒ShymanZhu同學(xué)的關(guān)系圖

LiveData_one.png

LiveData:是LiveData組件里面非常核心的一個(gè)類,主要實(shí)現(xiàn)了observe方法用于注冊(cè)監(jiān)聽(tīng)慧脱,setValue用于主線程設(shè)置值渺绒,而postValue子線程和主線程都可以。

MutabeLiveData:繼承了LiveData菱鸥,LiveData是一個(gè)抽象類不能直接使用宗兼,在子類里面重寫(xiě)了postValuesetValue兩個(gè)方法;

MediatorLiveDataMediatorLiveData繼承了MutabeLiveData氮采;

LifecycleBoundObserverLifecycleBoundObserverLiveData的內(nèi)部類殷绍,
它繼承了ObserverWrapper并實(shí)現(xiàn)了GenericLifecycleObserver,而這個(gè)GenericLifecycleObserver又實(shí)現(xiàn)了LifecycleObserver,有沒(méi)有很熟悉鹊漠?在Lifecycle組件中通過(guò)LifecycleObserver便可以觀察到LifecycleOwner中持有的Lifecycle對(duì)象的生命周期變化主到。

ObserverLiveData有數(shù)據(jù)更新的時(shí)候就是通過(guò)Observer接口的onChanged方法告知界面(Activity,F(xiàn)ragment)贸呢。

再次感謝ShymanZhu同學(xué)的時(shí)序圖

LiveData_two.png

上面大致的思路是:在Fragment中調(diào)用observe()方法的時(shí)候镰烧,會(huì)先在方法內(nèi)創(chuàng)建一個(gè)LifecycleBoundObserver對(duì)象,然后通過(guò)getLifecycle().addObserver()將這個(gè)創(chuàng)建好的對(duì)象添加進(jìn)去楞陷。當(dāng)生命周期會(huì)發(fā)生改變的時(shí)候怔鳖,會(huì)調(diào)用相應(yīng)的方法,移除觀察者或者通知觀察者更新數(shù)據(jù)固蛾;另外的調(diào)用LiveDatasetValue()结执、postValue()方法后,也會(huì)通知觀察者更新數(shù)據(jù)艾凯。

我們首先根據(jù)上面的思路理一下献幔,首選需要注冊(cè)觀察者,創(chuàng)建LifecycleBoundObserver對(duì)象趾诗,生命周期發(fā)生變化之后通知觀察者修改數(shù)據(jù)蜡感。

添加觀察者

添加觀察者有兩個(gè)方法可以調(diào)用,observeobserveForever恃泪,先來(lái)看看這兩個(gè)方法的實(shí)現(xiàn):

   @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        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);
    }

observe方法中需要將LifecycleOwner傳入郑兴,而LifecycleOwner的實(shí)現(xiàn)類可以通過(guò)getLifecycle(),拿到Lifecycle的生命周期贝乎;而observeForever則不需要傳入:

   @MainThread
    public void observeForever(@NonNull Observer<T> observer) {
        AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        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);
    }

通過(guò)observeForever()添加的觀察者情连,會(huì)永久收到數(shù)據(jù)變化的回調(diào),除非用戶手動(dòng)removeObserve()觀察者會(huì)一直收到數(shù)據(jù)的變化的回調(diào)通知览效。

生命周期變化

先看看LifecycleBoundObserver 類的源碼實(shí)現(xiàn):

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

添加了觀察者之后却舀,生命周期發(fā)生改變的時(shí)候就會(huì)調(diào)用onStateChanged()方法虫几,當(dāng)前的狀態(tài)處于DESTROYED的時(shí)候,觀察者會(huì)被remove挽拔,當(dāng)當(dāng)前的狀態(tài)為active的時(shí)候辆脸,調(diào)用activeStateChanged()方法。

LiveData的數(shù)據(jù)更新

上面我們提到過(guò)篱昔,LiveData的數(shù)據(jù)更新有兩種方式每强,第一種就是使用setValue的方式只能在主線程也就是UI線程里面調(diào)用,另外一種就是postValue的方式可以在主線程或者子線程里面調(diào)用州刽。

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

setValuepostValueLiveData中的實(shí)現(xiàn):

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

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

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

通過(guò)源碼看到,postValue最后也會(huì)調(diào)用setValue方法浪箭,去修改LiveData中的值穗椅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奶栖,隨后出現(xiàn)的幾起案子匹表,更是在濱河造成了極大的恐慌,老刑警劉巖宣鄙,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袍镀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡冻晤,警方通過(guò)查閱死者的電腦和手機(jī)苇羡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鼻弧,“玉大人设江,你說(shuō)我怎么就攤上這事∪列” “怎么了叉存?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)度帮。 經(jīng)常有香客問(wèn)我歼捏,道長(zhǎng),這世上最難降的妖魔是什么笨篷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任瞳秽,我火速辦了婚禮,結(jié)果婚禮上冕屯,老公的妹妹穿的比我還像新娘寂诱。我一直安慰自己,他們只是感情好安聘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布痰洒。 她就那樣靜靜地躺著瓢棒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丘喻。 梳的紋絲不亂的頭發(fā)上脯宿,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音泉粉,去河邊找鬼连霉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嗡靡,可吹牛的內(nèi)容都是我干的跺撼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼讨彼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歉井!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起哈误,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哩至,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蜜自,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體菩貌,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年重荠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箭阶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晚缩,死狀恐怖尾膊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荞彼,我是刑警寧澤冈敛,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站鸣皂,受9級(jí)特大地震影響抓谴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寞缝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一癌压、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荆陆,春花似錦滩届、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棠枉。三九已至,卻和暖如春泡挺,著一層夾襖步出監(jiān)牢的瞬間辈讶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工娄猫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贱除,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓媳溺,卻偏偏與公主長(zhǎng)得像月幌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悬蔽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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