Android Jetpack 之 LiveData---入門使用

前言

最近在學習 Google 推出的框架Jetpack析显,雖然目前網上的資料已經很多了哀托,但為了加深印象和邊動手練習跟著學習,所以站在了下面的巨人的肩膀上移斩,并根據當前最新的API 和編寫實際Demo,記錄下一些學習筆記,大部分是參考巨人們的咬腋,整理和休整羹膳,加入自己理解和更新吧,學習領略了Android Jetpack組件的一點魅力

目前學習筆記系列為:

日常鳴謝巨人

Google官方博文

CSDN 博主Alex@W

正題

LiveData 簡介

  • LiveData是一個具有生命周期感知特性的可觀察的數據保持類根竿,使用LiveData保存數據時陵像,在每次訂閱數據更新時會自動回調設置的觀察者從而更新數據,真正的實現了數據驅動的效果
  • LiveData的創(chuàng)建基本會在ViewModel中犀填,從而使數據在界面銷毀時繼續(xù)保持
  • LiveData 認為觀察者的生命周期處于STARTED狀態(tài)或RESUMED狀態(tài)下蠢壹,表示觀察者處于活動狀態(tài),LiveData只通知活躍的觀察者更新數據
  • 注冊一個實現該LifecycleOwner 接口的對象配對的觀察者九巡,當相應Lifecycle對象的狀態(tài)改變?yōu)镈ESTROYED時需移除觀察者

LiveData 的簡單使用

依賴添加

    def lifecycle_version = "2.0.0"

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

    implementation 'android.arch.lifecycle:extensions:1.1.1'

創(chuàng)建一個LiveData實例來保存某種類型的數據

class TestViewModel : ViewModel() {

    var mCurrent: MutableLiveData<String>? = null
        get() {
            if (field == null) {
                field = MutableLiveData()
            }
            return field
        }
}

確保存儲LiveData對象是在ViewModel中而不 是Activity 或 Fragment 中

創(chuàng)建一個定義onChanged()方法的Observer對象,在數據變化時回調

        // 創(chuàng)建觀察者對象
        val nameObservable = Observer<String> {
            ld_tv.text = it // onChange() 方法中的操作
        }

使用observe() 方法將觀察者對象附加到LiveData對象蹂季,這將觀察對象向LiveData對象訂閱冕广,以便通知其更改

        // 創(chuàng)建viewModel 對象
        mModel = ViewModelProviders.of(this).get(TestViewModel::class.java)

        // mCurrent 訂閱觀察
        mModel.mCurrent!!.observe(this,nameObservable)
  1. 第一個參數傳遞的this就是LifecycleOwner 的實例,LiveData會根據Owner的聲明周期自動作相應的處理
  1. observe()將nameObservable作參數調用后偿洁,如果LiveData數據有值撒汉,onChange()立即調用獲取最新值
  1. 可以使用observeForever(Observer)方法注冊一個沒有關聯的生命周期所有者對象的觀察者,在這種情況下涕滋,觀察者被認為總是活動的睬辐,因此總是通知修改,可以刪除這些觀察員調用removeObserver(Observer) 方法

LiveData允許UI控制器觀察員訂閱更新

        //設置點擊事件宾肺,修改LiveData中的數據
        ld_btn.setOnClickListener{
            mModel.mCurrent!!.value = "AAAAAA"
        }

當LiveData對象保存的數據發(fā)生變化時溯饵,UI會自動響應更新

觀察者在從非活動狀態(tài)變?yōu)榛顒訝顟B(tài)時也會收到更新

運行結果

點擊按鈕后改變LiveData的數據,界面的TextView也隨之改變

擴展LiveData

package andjun.com.mykt2019.java.livedata

import android.arch.lifecycle.LiveData
import android.content.Context
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import androidx.annotation.MainThread

/**
 *   author  : AndJun
 *   time    : 2019/3/12
 *   desc    :
 */

class MyLocationLiveData(context: Context) : LiveData<Location>() {

    private var locationManager: LocationManager? = null

    init {
        locationManager = context.getSystemService(
                Context.LOCATION_SERVICE) as LocationManager
    }

    @MainThread
    companion object {
        private var sInstance: MyLocationLiveData? = null
        fun getInstance(context: Context): MyLocationLiveData? {
            if (sInstance == null) {
                sInstance = MyLocationLiveData(context.applicationContext)
            }
            return sInstance
        }
    }

    private var listener: LocationListener = object : LocationListener {
        override fun onLocationChanged(location: Location?) {
        }

        override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
            setValue(location)
        }

        override fun onProviderEnabled(provider: String?) {
        }

        override fun onProviderDisabled(provider: String?) {
        }
    }

    override fun onActive() {
        locationManager!!.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0.toLong(), 0.toFloat(), listener)
    }

    override fun onInactive() {
        locationManager!!.removeUpdates(listener)
    }

}
  • onActive() :當LiveData對象具有活動觀察者時調用該方法
    -** onInactive()** :當LiveData對象沒有任何活動觀察者時調用該方法
  • setValue(T) :方法更新LiveData實例的值锨用,并通知任何活動的觀察者有關更改的信息
  • postValue(T value)
    : 其實是在非主線程中更新數據丰刊,其實他傳遞的Runnable中也是調用了setValue()方法,其余的操作只是通過ArchTaskExecutor和DefaultTaskExecutor將操作切換到主線程
調用流程如下:

setValue(T value) --> dispatchingValue(null) --> considerNotify(iterator.next().getValue()) --> observer.mObserver.onChanged((T) mData)

所以 這里就去到了 Observer 觀察者的回調中增拥,去更新具體操作

上面代碼使用單例提供LocationLiveData可以在Activity和Fragment之間實現共享啄巧,在onActive和onInactive中完成監(jiān)聽的注冊和取消寻歧,當位置改變時回調Listener并調用setValue()設置LiveData的值,從而自動更新觀察者中的數據

使用擴展LIveData

擴展的LiveData使用和正常使用一樣秩仆,observe()方法將Fragmrnt(它是一個實例LifecycleOwner)作為第一個參數傳遞码泛,使觀察者綁定到Fragment的生命周期,如果Lifecycle對象未處于活動狀態(tài)澄耍,則即使值發(fā)生更改弟晚,也不會調用觀察者,在之后Lifecycle的對象被Destroy后逾苫,觀察者被自動刪除

簡易源碼分析

  • 加觀察者observe方法開始
observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer)
  • 從參數中看到了LifecycleOwner卿城,看到 從參數中看到了LifecycleOwner 應該會想到這里的作用是處理生命周期改變(可以參考Lifecy 使用,源碼分析)铅搓,在observe方法中執(zhí)行了以下邏輯
  1. 在observe中首先判斷了當前Lifecycler的狀態(tài)瑟押,當Destroy時即觀察者不處于活躍狀態(tài),不用接收數據

  2. 創(chuàng)建LifecycleBoundObserver實例保存?zhèn)魅氲腖ifecycleOwner和Observer星掰,并保存在mObservers

  3. 添加LifecycleOwner的觀察者多望,響應生命周期的變化

  • 在 observe 方法中的,ObserverWrapperLifecycleBoundObserver
ObserverWrapper

        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
 
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();  // 當Owner為活躍狀態(tài)時回調onActive()
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive(); // 當Owner未活躍狀態(tài)時回調onInactive()
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }
        
        
        

LifecycleBoundObserver


        // 實現GenericLifecycleObserver 當生命周期改變時回調onStateChanged
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver); // DESTROYED時移除觀察者
                return;
            }
            activeStateChanged(shouldBeActive());
        }
 
 
        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }

ObserverWrapper 在Owner活躍狀態(tài)改變時回調onActive和onInactive方法氢烘,LifecycleBoundObserver主要利用Lifecycler的生命周期觀察者GenericLifecycleObserver怀偷,前面設置了owner.getLifecycle().addObserver(wrapper)后,當生命周期改變時會回調onStateChange()方法播玖,在生命周期為Destroy時移除Observer

小結

LiveData 整個過程就是兩部分

  • 一是使用LifeCycleOwner感知聲明周期的變化
  • 第二就是儲存并遍歷Observer椎工,在數據改變時回調所有的觀察者
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜀踏,隨后出現的幾起案子维蒙,更是在濱河造成了極大的恐慌,老刑警劉巖果覆,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颅痊,死亡現場離奇詭異,居然都是意外死亡局待,警方通過查閱死者的電腦和手機斑响,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钳榨,“玉大人舰罚,你說我怎么就攤上這事≈乇粒” “怎么了沸停?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昭卓。 經常有香客問我愤钾,道長瘟滨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任能颁,我火速辦了婚禮杂瘸,結果婚禮上,老公的妹妹穿的比我還像新娘伙菊。我一直安慰自己败玉,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布镜硕。 她就那樣靜靜地躺著运翼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兴枯。 梳的紋絲不亂的頭發(fā)上血淌,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音财剖,去河邊找鬼悠夯。 笑死,一個胖子當著我的面吹牛躺坟,可吹牛的內容都是我干的沦补。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼咪橙,長吁一口氣:“原來是場噩夢啊……” “哼夕膀!你這毒婦竟也來了?” 一聲冷哼從身側響起匣摘,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤店诗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后音榜,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡捧弃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年赠叼,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片违霞。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘴办,死狀恐怖,靈堂內的尸體忽然破棺而出买鸽,到底是詐尸還是另有隱情涧郊,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布眼五,位于F島的核電站妆艘,受9級特大地震影響彤灶,放射性物質發(fā)生泄漏。R本人自食惡果不足惜批旺,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一幌陕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汽煮,春花似錦搏熄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鞋囊,卻和暖如春止后,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背失暴。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工坯门, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逗扒。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓古戴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矩肩。 傳聞我的和親對象是個殘疾皇子现恼,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345