Jetpack LiveData 观察它 响应它

2,089 阅读8分钟

我正在参与掘金创作者训练营第4期,点击了解活动详情,一起学习吧!

前言

作为Android开发者,如果你接触过Jetpack,那么相信你对LiveData肯定不陌生,它以观察者模式为设计理念,常常与ViewModel一起配合使用,响应式编程,从而实现数据驱动UI

今天让我们秉持知其然又知其所以然态度来深入学习一下LiveData

使用方法

我们先来回顾一下其使用方法,举个简单的例子。

class MyViewModel : ViewModel() {

    //3. 更改被观察数据
    private val _text = MutableLiveData<String>().apply {
        value = "This is JcTest"
    }
    //1. 实例化
    val textLd: LiveData<String> = _text
}


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        
        ···省略代码···

        viewModel = ViewModelProvider(this)[MyViewModel::class.java]
        //2. 定义并注册观察者对象
        viewModel.textLd.observe(this, Observer {
            //4. 执行观察动作
            viewBinding.textTv.text = it
            //可通过 DataBinding 来进一步实现数据UI绑定
        })

    }

}

我们可以简单概括为 4 步:

  1. 实例化一个LiveData,定义好被观察数据的类型。
  2. 定义并注册观察者对象,需为生命周期组件。
  3. 更改被观察数据的值,通知到对应的观察者。
  4. 观察者对象获取到更新的新数据,并执行观察动作。

再知道其使用方法后,接下来,我们再进一步查看一下它的源码,看看它是如何设计的。

源码解析

我们按照使用方法来一步步分析。

构造函数

先来看一下它的构造函数:

public LiveData(T value) {
    mData = value;
    mVersion = START_VERSION + 1;
}

public LiveData() {
    mData = NOT_SET;
    mVersion = START_VERSION;
}

构造函数很简单,只有两个字段:

  • mData:观察的数据。
  • mVersion:版本号,每次数据更改都会 +1。

定义并注册观察者对象

其实这里可以分为两步,分别是定义与注册。

定义

先来看看定义。我们知道观察者模式都是有一个抽象观察者接口的,所以LiveData也不例外。

public interface Observer<T> {
    /**
     * 当数据发生更改时调用,t 为更改后的新数据。
     */
    void onChanged(T t);
}

这里,Observer就是抽象观察者接口,当观察的数据发生更改时,就会调用onChange(t)方法。

除此之外,LiveData还对Observer进行了包装。

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

ObserverWrapper就是观察者对象的包装类,那为什么需要对观察者对象进行包装一下呢?思考一下。

还有继承ObserverWrapper的绑定生命周期的观察者对象。

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() {
        //判断一下该观察者是否处于激活状态,也就是处于 onStart() - onStop()
        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;
        }
        //否则,调用activeStateChanged()方法,继续观察
        Lifecycle.State prevState = null;
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        //判断是否已绑定了生命周期组件
        return mOwner == owner;
    }

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

通过LifeCycle来获取到生命周期的状态,从而让观察者绑定生命周期。

注册

我们再来完整的看一下observe方法。

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    //检查一下当前是否位于主线程,如果位于子线程,则抛出异常
    assertMainThread("observe");

    //如果当前观察者不处于激活状态,则不执行观察动作,直接return
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        return;
    }

    //通过生命周期组件与观察者对象新构建一个 生命周期绑定的观察者对象
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //检查一下该观察者是否已存在于观察者对象集合中,若存在则直接返回集合中的值;若不存在,则加入到该集合中,返回null。
    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);
}

简单概括一下:首先会检查一下当前是否位于主线程,如果位于子线程,则抛出异常。然后检查一下当前观察者的声明周期状态,如果不处于激活状态,就不会注册该观察者。通过生命周期组件与观察者对象新构建一个绑定生命周期的观察者对象wrapper,然后检查一下wrapper是否已存在于观察者对象集合中,若存在则直接返回集合中的值,即已经注册的观察者existing;若不存在,则将wrapper加入到该集合中,并返回null。接下来会判断一下existing是否存在,若存在,检查一下其生命周期组件是否与当前一致,若不一致,则抛出异常;若existing不存在,则为其赋予生命周期感知能力。

这里关于生命周期的感知与获取都是通过Jetpack中的Lifecycle来实现,这里由于篇幅有限,就不展开了。

更新数据

在完成定义与注册观察者对象后,下一步就是更新被观察数据。

LiveData支持两种方式来更新数据,分别是 setValue()postValue(),这两种方式最主要的区别就是 setValue()是工作于主线程中的,是同步执行的,而 postValue() 是工作于子线程中的,是异步执行的。

我们分别来看看。

setValue

先来看看setValue

protected void setValue(T value) {
    //检查是否在主线程中,如果不是则会抛出异常
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    //分发数据,传入null就会去遍历所有的观察者进行分发数据
    dispatchingValue(null);
}

简单概括一下:首先会判断一下是否位于主线程中,如果不是则会抛出异常。然后调用dispatchingValue方法分发数据,这里传入null就会去遍历所有的观察者对象,然后一一对其进行分发数据。

postValue

接着,我们再来看看postValue方法。

protected void postValue(T value) {
    boolean postTask;
    //加对象锁来保证线程安全
    synchronized (mDataLock) {
        //通过mPendingData来判断是否在设置数据
        postTask = mPendingData == NOT_SET;
        //设置数据
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    //最终通过 Handler 切换到主线程执行该任务
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            //设置新的数据
            newValue = mPendingData;
            //将mPendingData重置
            mPendingData = NOT_SET;
        }
        //最终还是通过调用setValue()
        setValue((T) newValue);
    }
};

可以看到,postValue其实最终就是通过Handler切换到主线程来执行,最终还是调用setValue方法。

注意:如果你在主线程执行这个postTask前多次调用postValue(value)这个方法,只有最后一次的发送的value会被分发。

并且,如果你有postValue与setValue同时存在的情况话,如:

liveData.postValue("a");
liveData.setValue("b");

“b”会先被设置,然后主线程将用值“a”覆盖它。

为了保证线程安全,这里还用到了对象锁,如果你想进一步了解对象锁,可以点击我的另外一篇文章Android程序员重头学Synchronized进行查看。

指定观察工作

还记得上一步调用dispatchingValue进行分发数据吧,其实它最终会调用considerNotify方法。

private void considerNotify(ObserverWrapper observer) {
    //如果观察者不处于激活状态,则不需要通知
    if (!observer.mActive) {
        return;
    }
 
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //通知观察者对象数据更改
    observer.mObserver.onChanged((T) mData);
}

简单概括一下:判断一下当前观察者是否处于激活状态,再通过Observer.onChanged(mData)回调方法来通知观察者执行观察动作。

反思

为什么要对观察者对象包装一下

回到上述中提到的问题,为什么要对观察者对象包装一下?

其实也很简单,正如一开始所介绍,我们的观察者对象在Activity中所定义并注册,而Activity是有生命周期的,会被销毁回收。那这样,当Activity被销毁时,我们还需要去通知它吗?显示是不需要的。所以我们需要赋予其生命周期状态,让LiveData知道观察者对象的生命周期状态,文章中经常提起的激活状态,正是onStart()onStop(),如果观察者不处于激活状态,则不需要通知。

设计模式

LiveData的核心理念正是观察者模式:

  • 被观察者对象:LiveData里面的数据?通过setValuepostValue来触发,也就是LiveData里面的Value
  • 观察者对象:observe方法里的第二个参数,实现Observer接口。
  • 观察动作:具体Observer.onChanged 方法里的内容。

总结

通过本篇文章,我们回顾了LiveData的使用方法,并按照其使用步骤一步一步地查看了其源码,知道了LiveData是如何定义注册被观察者对象,如何更改观察数据,并根据观察者的生命周期状态,将观察数据分发给相应的观者者对象。相应的源码也很简单,多读几遍,肯定会让你加深对LiveData的理解。

其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。

另外,如果你觉得文章不错,对你有所帮助,请帮我点个赞,就当鼓励,谢谢~