Jetpack系列-LiveData使用和源码分析

1 简介和简单使用

1.1 简介

LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData具有生命周期感知能力,意指它遵循其他应用组件(如Activity、Fragment或Service)的生命周期。这种感知能力可确保LiveData仅更新处于活跃生命周期(onStart、onResume)状态的应用组件观察者。LiveData通常和ViewModel相结合使用。

LiveData依赖于Lifecycle,需要使用Lifecycle提供的状态来判断程序的活跃状态。

如果观察者(由Observer类表示)的生命周期处于STARTED或RESUMED状态,则LiveData会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察LiveData对象而注册的非活跃观察者不会收到更改通知。

Google官网:https://developer.android.google.cn/topic/libraries/architecture/livedata?hl=zh_cn

1.2 简单使用

引入依赖,按需所取。

def lifecycle_version = "2.4.1"
def arch_version = "2.1.0"

// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"

// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

// Annotation processor
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

// optional - helpers for implementing LifecycleOwner in a Service
implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

// optional - ReactiveStreams support for LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version"

// optional - Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$arch_version"

创建一个单例StrLiveData,里边会实例化一个MutableLiveData,用来存储String类型的数据。

object StrLiveData {
    //懒加载
    val info: MutableLiveData<String> by lazy { MutableLiveData() }
}

在Activity中使用。

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

        val tvContent = findViewById<TextView>(R.id.tv_content)

        //观察数据变化,界面可见时,才会给TextView设置数据
        StrLiveData.info.observe(this) {
            tvContent.text = it
        }

        //观察数据变化,不管界面可见不可见,都给TextView设置数据
        //observeForever不会考虑生命周期,任何状态下都会执行
        StrLiveData.info.observeForever {
            tvContent.text = it
        }

        //主线程修改数据
        StrLiveData.info.value = "在主线程修改的数据"

        thread {
            Thread.sleep(2000)
            //子线程修改数据
            StrLiveData.info.postValue("在子线程修改的数据")
        }
    }
}

XML文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".jetpack.livedata.demo1.LiveDataActivity">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

1.3 关系框架

2 源码分析

2.1 setValue 主线程修改数据

首先在业务层单例中实例化了一个MutableLiveData,MutableLiveData继承LiveData,只保留setValue和postValue两个设置数据的方法,简化使用。

public class MutableLiveData<T> extends LiveData<T> {

    /**
     * Creates a MutableLiveData initialized with the given {@code value}.
     *
     * @param value initial value
     */
    public MutableLiveData(T value) {
        super(value);
    }

    /**
     * Creates a MutableLiveData with no value assigned to it.
     */
    public MutableLiveData() {
        super();
    }

    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

当业务层调用LiveData的setValue方法时,在主线程修改数据。

//主线程修改数据
StrLiveData.info.value = "在主线程修改的数据"</pre>

setValue方法首先会检查当前是否是主线程,然后会对mVersion+1,接着分发数据。

<pre data-language="java" id="QU8SA" class="ne-codeblock language-java" style="border: 1px solid #e8e8e8; border-radius: 2px; background: #f9f9f9; padding: 16px; font-size: 13px; color: #595959">@MainThread
protected void setValue(T value) {
    //检查是否是主线程
    assertMainThread("setValue");
    //mVersion+1
    mVersion++;
    mData = value;
    //分发数据
    dispatchingValue(null);
}

mVersion是LiveData粘性数据特性的关键,它的初始值是-1。

static final int START_VERSION = -1;

public LiveData() {
     mData = NOT_SET;
     //LiveData实例化的时候给mVersion赋值为-1
     mVersion = START_VERSION;
}

分发数据的时候有两种情况,一种是调用LiveData.observe(),另一种是调用LiveData.observeForever()。ObserverWrapper是观察者的包装类。

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        ...
        //initiator(观察者)不为空
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            //initiator(观察者)为空,需要遍历所有的观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                 mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
       ...
}

接下来调用considerNotify方法。然后对当前是否是活跃状态进行检查,也就是mActive是否为true。为true才会继续执行。就这对mVersion进行对比,由于在setValue的时候mVersion++之后变为0,mLastVersion为-1,所以这里observer.mLastVersion >= mVersion不成立,数据会正常分发出去,这就是造成粘性数据特征的原因。

如果调用LiveData.observe(),那么当Lifecycle状态为STARTED和RESUMED,也就是Activity/Fragment生命周期为onStart和onResume的时候,mActive=true,其他状态和生命周期下,mActive=fale;如果调用LiveData.observeForever(),mActive一直为true,这就实现了调用LiveData.observeForever()时候,观察者不会去考虑生命周期和状态,所有数据都会无条件感知。

private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        //判断mActivie是否为true。
        //调用LiveData.observe()时会根据状态和生命周期计算mActive的值;
        //调用LiveData.observeForever()时mActive一直为true。
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //判断mVersion
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //回调数据给观察者
        observer.mObserver.onChanged((T) mData);
}

2.2 postValue 子线程修改数据

当业务层调用LiveData的postValue方法时,在子线程修改数据。

thread {
    ...
    //子线程修改数据
    StrLiveData.info.postValue("在子线程修改的数据")
}

postValue方法中最终也会通过Handler切换到主线程。

protected void postValue(T value) {
    boolean postTask;
    //加锁,防止线程安全问题
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    //切换到主线程
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

在ArchTaskExecutor中,会实例化一个DefaultTaskExecutor,然后调用DefaultTaskExecutor的postToMainThread方法。

private ArchTaskExecutor() {
    mDefaultTaskExecutor = new DefaultTaskExecutor();
    mDelegate = mDefaultTaskExecutor;
}

@Override
public void postToMainThread(Runnable runnable) {
    mDelegate.postToMainThread(runnable);
}

DefaultTaskExecutor使用Handler进行线程间通信,实现子线程到主线程切换。

@Override
public void postToMainThread(Runnable runnable) {
    if (mMainHandler == null) {
        synchronized (mLock) {
            if (mMainHandler == null) {
                mMainHandler = new Handler(Looper.getMainLooper());
            }
        }
    }
    //noinspection ConstantConditions
    mMainHandler.post(runnable);
}

而在postToMainThread中传入的Runnable中,最红也调用了setValue,接下来的逻辑就和主线程中修改数据一致了。

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //最终也是调用setValue
        setValue((T) newValue);
    }
};

2.3 observe 根据生命周期感知数据变化

调用LiveData的observe( LifecycleOwner owner, Observer<? super T> observer)方法时,传入了一个被观察者和观察者。

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    //检查是否是主线程
    assertMainThread("observe");
    //获取Lifecycle当前状态,如果是销毁状态,那就忽略,不再继续往下进行
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        return;
    }
    //创建一个LifecycleBoundObserver对象,包装了LifecycleOwner和Observer。
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //ObserverWrapper包装了LifecycleBoundObserver和Observer
    //把Observer和LifecycleBoundObserver存入map
    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);
}

LifecycleBoundObserver是LiveData的内部类,继承了ObserverWrapper,并且实现LifecycleObserver。传入了一个被观察者和观察者。

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

    //传入被观察者和观察者
    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

    @Override
    boolean shouldBeActive() {
        //从Lifecycle获取到当前是否是活跃状态(UI可见状态)。
        //Activity/Fragment生命周期onStart和onResume,Lifecycle对应状态为STARTED和RESUMED时,处于活跃状态。
        //这里使用了一个比较,也就是状态在STARTED和STARTED之后时,就处于活跃状态。
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
                               @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        if (currentState == DESTROYED) {
            //页面销毁时,移除观察者
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            //调用ObserverWrapper的activeStateChanged,再调用dispatchingValue分发数据。
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

    @Override
    void detachObserver() {
        //观察者和被观察者解除绑定
        mOwner.getLifecycle().removeObserver(this);
    }
}

ObserverWrapper是LiveData的抽象内部类。

private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;

    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }

    //抽象方法,判断是否处于活跃状态
    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        mActive = newActive;
        changeActiveCounter(mActive ? 1 : -1);
        if (mActive) {
            //处于活跃状态,发送最新的值
            dispatchingValue(this);
        }
    }
}

2.3 observeForever 无条件感知数据变化

在调用LiveData的observeForever方法时,没有传入被观察者,值传入了观察者,这就决定了调用observeForever之后无法根据Activity/Fragment的生命周期去判断合适感知数据,只能无条件的接收所有数据。这里用AlwaysActiveObserver对观察者进行了包装。

@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    //判断是否是主线程
    assertMainThread("observeForever");
    //创建一个AlwaysActiveObserver对象,对Observer进行包装
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    //ObserverWrapper包装了AlwaysActiveObserver和Observer
    //把Observer和AlwaysActiveObserver存入map
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                                           + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    //这里传入的mActive一直是true,所以消息一直能无条件回调给观察者
    wrapper.activeStateChanged(true);
}

再看AlwaysActiveObserver,shouldBeActive固定返回true。所以在considerNotify方法中,一直能执行到observer.mObserver.onChanged((T) mData),把数据回调给观察者。

private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<? super T> observer) {
        super(observer);
    }

    @Override
    boolean shouldBeActive() {
        return true;
    }
}

3 流程图

根据上边的源码分析,画出以下流程图。


关注木水小站 (zhangmushui.cn)和公众号【木水Code】,及时获取更多技术干货。

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

推荐阅读更多精彩内容