Android Jetpack 之 LiveData

[TOC]

概述

  • LiveData 是一個持有數(shù)據(jù)的類祠锣,它持有的數(shù)據(jù)是可以被觀察者訂閱的绒障,當數(shù)據(jù)被修改時就會通知觀察者。觀察者可以是 Activity捂蕴、Fragment、Service 等闪幽。
  • LiveData 能夠感知觀察者的生命周期啥辨,只有當觀察者處于激活狀態(tài)(STARTED、RESUMED)才會接收到數(shù)據(jù)更新的通知盯腌,在未激活時會自動解注冊觀察者溉知,以減少內(nèi)存泄漏。
  • 使用 LiveData 保存數(shù)據(jù)時腕够,由于數(shù)據(jù)和組件是分離的级乍,當組件重建時可以保證數(shù)據(jù)不會丟失。

優(yōu)點

  • 確保 UI 界面始終和數(shù)據(jù)狀態(tài)保持一致帚湘。
  • 沒有內(nèi)存泄漏玫荣,觀察者綁定到 Lifecycle 對象并在其相關生命周期 destroyed 后自行解除綁定。
  • 不會因為 Activity 停止了而奔潰大诸,如 Activity finish 了捅厂,它就不會收到任何 LiveData 事件了。
  • UI 組件只需觀察相關數(shù)據(jù)资柔,不需要停止或恢復觀察焙贷,LiveData 會自動管理這些操作,因為 LiveData 可以感知生命周期狀態(tài)的更改贿堰。
  • 在生命周期從非激活狀態(tài)變?yōu)榧せ顮顟B(tài)辙芍,始終保持最新數(shù)據(jù),如后臺 Activity 在返回到前臺后可以立即收到最新數(shù)據(jù)羹与。
  • 當配置發(fā)生更改(如屏幕旋轉(zhuǎn))而重建 Activity / Fragment故硅,它會立即收到最新的可用數(shù)據(jù)。
  • LiveData 很適合用于組件(Activity / Fragment)之間的通信纵搁。

使用

導入依賴

添加相關依賴

LiveData 有兩種使用方式吃衅,結(jié)合 ViewModel 使用以及直接繼承 LiveData 類。

// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.1.0"
// alternatively, just ViewModel
implementation "android.arch.lifecycle:viewmodel:1.1.0"
// alternatively, just LiveData
implementation "android.arch.lifecycle:livedata:1.1.0"

結(jié)合 ViewModel 使用

LiveData 是一個抽象類诡渴,它的實現(xiàn)子類有 MutableLiveData ,MediatorLiveData。在實際使用中妄辩,用得比較多的是 MutableLiveData惑灵。常常結(jié)合 ViewModel 一起使用。

public class TestViewModel extends ViewModel {

    private MutableLiveData<String> mNameEvent = new MutableLiveData<>();

    public MutableLiveData<String> getNameEvent() {
        return mNameEvent;
    }

}

在Activity中創(chuàng)建ViewModel眼耀,監(jiān)聽ViewModel中的數(shù)據(jù)變化

mTestViewModel = ViewModelProviders.of(this).get(TestViewModel.class);
MutableLiveData<String> nameEvent = mTestViewModel.getNameEvent();
nameEvent.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
        Log.i(TAG, "onChanged: s = " + s);
        mTvName.setText(s);
    }
});

<font color = "#ff00e0">Activity英支、fragment 數(shù)據(jù)傳遞:</font>

對應activity、viewModel 獲取到的對象是相同的哮伟,所以可用來Activity干花、fragment通信

fragment中:

nameEvent = ViewModelProviders.of(getActivity()).get(HomeFragmentViewModel.class);
nameEvent.observe(this, new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
       ....
    }
});

<font color = "#ff0000">ViewModel 攜帶參數(shù):</font>

? 同樣是調(diào)用 ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) 方法,只不過楞黄,需要多傳遞一個 factory 參數(shù)池凄。

做法:

實現(xiàn) Factory 接口,重寫 create 方法雪侥,在create 方法里面調(diào)用相應的構(gòu)造函數(shù)外臂,返回相應的實例坑律。

public class TestViewModel extends ViewModel {

    private final String mKey;
    private MutableLiveData<String> mNameEvent = new MutableLiveData<>();

    public MutableLiveData<String> getNameEvent() {
        return mNameEvent;
    }

    public TestViewModel(String key) {
        mKey = key;
    }

    public static class Factory implements ViewModelProvider.Factory {
        private String mKey;

        public Factory(String key) {
            mKey = key;
        }

        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            return (T) new TestViewModel(mKey);
        }
    }

    public String getKey() {
        return mKey;
    }
}
ViewModelProviders.of(this, new TestViewModel.Factory(mkey)).get(TestViewModel.class)

直接繼承 LiveData 類

以下代碼場景:在 Activity 中監(jiān)聽 Wifi 信號強度。

class WifiLiveData private constructor(context: Context) : LiveData<Int>() {

    private var mContext: WeakReference<Context> = WeakReference(context)

    companion object {

        private var instance: WifiLiveData? = null

        fun getInstance(context: Context): WifiLiveData {
            if (instance == null) {
                instance = WifiLiveData(context)
            }
            return instance!!
        }
    }

    override fun onActive() {
        super.onActive()
        registerReceiver()
    }

    override fun onInactive() {
        super.onInactive()
        unregisterReceiver()
    }

    /**
     * 注冊廣播尤慰,監(jiān)聽 Wifi 信號強度
     */
    private fun registerReceiver() {
        val intentFilter = IntentFilter()
        intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION)
        mContext.get()!!.registerReceiver(mReceiver, intentFilter)
    }

    /**
     * 注銷廣播
     */
    private fun unregisterReceiver() {
        mContext.get()!!.unregisterReceiver(mReceiver)
    }

    private val mReceiver = object : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent) {
            when (intent.action) {
                WifiManager.RSSI_CHANGED_ACTION -> getWifiLevel()
            }
        }
    }

    private fun getWifiLevel() {
        val wifiManager = mContext.get()!!.applicationContext.getSystemService(android.content.Context.WIFI_SERVICE) as WifiManager
        val wifiInfo = wifiManager.connectionInfo
        val level = wifiInfo.rssi

        instance!!.value = level // 發(fā)送 Wifi 的信號強度給觀察者
    }
}

class LiveDataActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)

        withExtendsLiveDataTest()
    }

    /**
     * 直接繼承 LiveData 類
     */
    private fun withExtendsLiveDataTest() {
        WifiLiveData.getInstance(this).observe(this, Observer {
            Log.e("LiveDataActivity", it.toString()) // 觀察者收到數(shù)據(jù)更新的通知,打印 Wifi 信號強度
        })
    }
}
    

當組件(Activity)處于激活狀態(tài)(onActive)時注冊廣播雷蹂,處于非激活狀態(tài)(onInactive)時注銷廣播伟端。

源碼解析

observe 注冊流程

LiveData 通過 observe() 方法將被觀察者 LifecycleOwner (Activity / Fragment) 和觀察者 Observer 關聯(lián)起來。

LiveData.observe(LifecycleOwner owner , Observer<T> observer)

LiveData源碼:

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // 若 LifecycleOwner 處于 DESTROYED 狀態(tài)匪煌,則返回
        return;
    }

    // LifecycleBoundObserver 把 LifecycleOwner 對象和 Observer 對象包裝在一起
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

    // mObservers(類似 Map 的容器)的 putIfAbsent() 方法用于判斷容器中的 observer(key)
    // 是否已有 wrapper(value)與之關聯(lián)
    // 若已關聯(lián)則直接返回關聯(lián)值责蝠,否則關聯(lián)后再返回 wrapper
    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;
    }

    // 由于 LifecycleBoundObserver 實現(xiàn)了 GenericLifecycleObserver 接口,而 GenericLifecycleObserver 又
    // 繼承了 LifecycleObserver虐杯,所以 LifecycleBoundObserver 本質(zhì)是一個 LifecycleObserver
    // 此處屬于注冊過程玛歌, Lifecycle 添加觀察者 LifecycleObserver
    owner.getLifecycle().addObserver(wrapper);
}

感知生命周期變化

由上可知,LifecycleBoundObserver(LiveData 的內(nèi)部類)是觀察者擎椰,以下具體分析 LifecycleBoundObserver 的實現(xiàn)過程支子。

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

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

    @Override
    boolean shouldBeActive() {
        // 判斷是否處于激活狀態(tài)
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }


    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        // 若 Lifecycle 處于 DESTROYED 狀態(tài),則移除 Observer 對象
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            // 移除觀察者达舒,在這個方法中會移除生命周期監(jiān)聽并且回調(diào) activeStateChanged() 方法
            removeObserver(mObserver);
            return;
        }
        // 若處于激活狀態(tài)值朋,則調(diào)用 activeStateChanged() 方法
        activeStateChanged(shouldBeActive());
    }

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

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

當組件(Activity / Fragment)的生命周期發(fā)生改變時,onStateChanged() 方法將會被調(diào)用巩搏。若當前處于 DESTROYED 狀態(tài)昨登,則會移除觀察者;若當前處于激活狀態(tài)贯底,則會調(diào)用 activeStateChanged() 方法丰辣。activeStateChanged() 方法位于父類 ObserverWrapper 中撒强。

void activeStateChanged(boolean newActive) {
    // 若新舊狀態(tài)一致,則返回
    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) { // 激活狀態(tài)的 observer 個數(shù)從 0 到 1
        onActive(); // 空實現(xiàn)笙什,一般讓子類去重寫
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) { // 激活狀態(tài)的 observer 個數(shù)從 1 到 0
        onInactive();  // 空實現(xiàn)飘哨,一般讓子類去重寫
    }
    if (mActive) { // 激活狀態(tài),向觀察者發(fā)送 LiveData 的值
        dispatchingValue(this);
    }
}

分發(fā)Value :dispatchingValue

private void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // ...
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            // 循環(huán)遍歷 mObservers 這個 map , 向每一個觀察者都發(fā)送新的數(shù)據(jù)
            for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    // ...
}

considerNotify 發(fā)送事件:

private void considerNotify(ObserverWrapper observer) {
    // ...
    observer.mObserver.onChanged((T) mData);
}

上面的 mObserver 正是調(diào)用 observe() 方法時傳入的觀察者琐凭。

更新數(shù)據(jù)方式

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

dispatchingValue如上小結(jié)分析的value分發(fā)方法

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

ArchTaskExecutorpostToMainThread方法:

 @Override
    public void postToMainThread(Runnable runnable) {
      //mDelegate是 DefaultTaskExecutor 對象
        mDelegate.postToMainThread(runnable);
    }

DefaultTaskExecutor (是TaskExecutor的實現(xiàn)類)代碼:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class DefaultTaskExecutor extends TaskExecutor {
    private final Object mLock = new Object();
    private ExecutorService mDiskIO = Executors.newFixedThreadPool(2);

    @Nullable
    private volatile Handler mMainHandler;

    @Override
    public void executeOnDiskIO(Runnable runnable) {
        mDiskIO.execute(runnable);
    }

  //切換到主線程執(zhí)行
    @Override
    public void postToMainThread(Runnable runnable) {
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    mMainHandler = new Handler(Looper.getMainLooper());
                }
            }
        }
        //noinspection ConstantConditions
        mMainHandler.post(runnable);
    }

    @Override
    public boolean isMainThread() {
        return Looper.getMainLooper().getThread() == Thread.currentThread();
    }
}
<font color = "#ff00e0">兩種更新方式的區(qū)別:</font>
  • setValue:更新數(shù)據(jù)是在調(diào)用setValue方法所在線程更新

  • postValue:更新數(shù)據(jù)是在主線程更新

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芽隆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子统屈,更是在濱河造成了極大的恐慌胚吁,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愁憔,死亡現(xiàn)場離奇詭異腕扶,居然都是意外死亡,警方通過查閱死者的電腦和手機惩淳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門蕉毯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人思犁,你說我怎么就攤上這事代虾。” “怎么了激蹲?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵棉磨,是天一觀的道長。 經(jīng)常有香客問我学辱,道長乘瓤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任策泣,我火速辦了婚禮衙傀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萨咕。我一直安慰自己统抬,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布危队。 她就那樣靜靜地躺著聪建,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茫陆。 梳的紋絲不亂的頭發(fā)上金麸,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音簿盅,去河邊找鬼挥下。 笑死揍魂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的棚瘟。 我是一名探鬼主播愉烙,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼解取!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起返顺,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤禀苦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遂鹊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體振乏,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年秉扑,在試婚紗的時候發(fā)現(xiàn)自己被綠了慧邮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡舟陆,死狀恐怖误澳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秦躯,我是刑警寧澤忆谓,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站踱承,受9級特大地震影響倡缠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茎活,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一昙沦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧载荔,春花似錦盾饮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至煌珊,卻和暖如春号俐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背定庵。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工吏饿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留踪危,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓猪落,卻偏偏與公主長得像贞远,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子笨忌,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

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