Android Jetpack 之 LiveData

[TOC]

概述

  • LiveData 是一个持有数据的类,它持有的数据是可以被观察者订阅的,当数据被修改时就会通知观察者。观察者可以是 Activity、Fragment、Service 等。
  • LiveData 能够感知观察者的生命周期,只有当观察者处于激活状态(STARTED、RESUMED)才会接收到数据更新的通知,在未激活时会自动解注册观察者,以减少内存泄漏。
  • 使用 LiveData 保存数据时,由于数据和组件是分离的,当组件重建时可以保证数据不会丢失。

优点

  • 确保 UI 界面始终和数据状态保持一致。
  • 没有内存泄漏,观察者绑定到 Lifecycle 对象并在其相关生命周期 destroyed 后自行解除绑定。
  • 不会因为 Activity 停止了而奔溃,如 Activity finish 了,它就不会收到任何 LiveData 事件了。
  • UI 组件只需观察相关数据,不需要停止或恢复观察,LiveData 会自动管理这些操作,因为 LiveData 可以感知生命周期状态的更改。
  • 在生命周期从非激活状态变为激活状态,始终保持最新数据,如后台 Activity 在返回到前台后可以立即收到最新数据。
  • 当配置发生更改(如屏幕旋转)而重建 Activity / Fragment,它会立即收到最新的可用数据。
  • LiveData 很适合用于组件(Activity / Fragment)之间的通信。

使用

导入依赖

添加相关依赖

LiveData 有两种使用方式,结合 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"

结合 ViewModel 使用

LiveData 是一个抽象类,它的实现子类有 MutableLiveData ,MediatorLiveData。在实际使用中,用得比较多的是 MutableLiveData。常常结合 ViewModel 一起使用。

public class TestViewModel extends ViewModel {

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

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

}

在Activity中创建ViewModel,监听ViewModel中的数据变化

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 数据传递:</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 携带参数:</font>

​ 同样是调用 ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) 方法,只不过,需要多传递一个 factory 参数。

做法:

实现 Factory 接口,重写 create 方法,在create 方法里面调用相应的构造函数,返回相应的实例。

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 中监听 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()
    }

    /**
     * 注册广播,监听 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 // 发送 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()) // 观察者收到数据更新的通知,打印 Wifi 信号强度
        })
    }
}
    

当组件(Activity)处于激活状态(onActive)时注册广播,处于非激活状态(onInactive)时注销广播。

源码解析

observe 注册流程

LiveData 通过 observe() 方法将被观察者 LifecycleOwner (Activity / Fragment) 和观察者 Observer 关联起来。

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 状态,则返回
        return;
    }

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

    // mObservers(类似 Map 的容器)的 putIfAbsent() 方法用于判断容器中的 observer(key)
    // 是否已有 wrapper(value)与之关联
    // 若已关联则直接返回关联值,否则关联后再返回 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 实现了 GenericLifecycleObserver 接口,而 GenericLifecycleObserver 又
    // 继承了 LifecycleObserver,所以 LifecycleBoundObserver 本质是一个 LifecycleObserver
    // 此处属于注册过程, Lifecycle 添加观察者 LifecycleObserver
    owner.getLifecycle().addObserver(wrapper);
}

感知生命周期变化

由上可知,LifecycleBoundObserver(LiveData 的内部类)是观察者,以下具体分析 LifecycleBoundObserver 的实现过程。

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() {
        // 判断是否处于激活状态
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }


    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        // 若 Lifecycle 处于 DESTROYED 状态,则移除 Observer 对象
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            // 移除观察者,在这个方法中会移除生命周期监听并且回调 activeStateChanged() 方法
            removeObserver(mObserver);
            return;
        }
        // 若处于激活状态,则调用 activeStateChanged() 方法
        activeStateChanged(shouldBeActive());
    }

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

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

当组件(Activity / Fragment)的生命周期发生改变时,onStateChanged() 方法将会被调用。若当前处于 DESTROYED 状态,则会移除观察者;若当前处于激活状态,则会调用 activeStateChanged() 方法。activeStateChanged() 方法位于父类 ObserverWrapper 中。

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) { // 激活状态的 observer 个数从 0 到 1
        onActive(); // 空实现,一般让子类去重写
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) { // 激活状态的 observer 个数从 1 到 0
        onInactive();  // 空实现,一般让子类去重写
    }
    if (mActive) { // 激活状态,向观察者发送 LiveData 的值
        dispatchingValue(this);
    }
}

分发Value :dispatchingValue

private void dispatchingValue(@Nullable ObserverWrapper initiator) {
    // ...
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            // 循环遍历 mObservers 这个 map , 向每一个观察者都发送新的数据
            for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    // ...
}

considerNotify 发送事件:

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

上面的 mObserver 正是调用 observe() 方法时传入的观察者。

更新数据方式

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

dispatchingValue如上小结分析的value分发方法

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的实现类)代码:

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

  //切换到主线程执行
    @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">两种更新方式的区别:</font>
  • setValue:更新数据是在调用setValue方法所在线程更新

  • postValue:更新数据是在主线程更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容