Jetpack之LiveData

196 阅读7分钟

简介

google官网对LiveData的有一句很简短的介绍:底层数据库更改时,通知视图

LiveData具有数据传递的功能,它是一个数据传输的工具类,数据可以在它身上进行流动,有点像Handler,它可以发送数据,而且别人可以监听到它发数据这个动作。LiveData还可以绑定到数据库,只要修改数据源(数据库)UI界面也会跟着改变。LiveData不会发生内存泄漏,它会随着Activity的生命周期一起把observer给解绑。

注意:LiveData 本身不是数据,它能够作为一个观察者,观察到 Activity 和 Fragment 生命周期的变化,同时它又是一个被观察者,它能够被别人观察到它自己的数据发生了生命周期的变化,所以它自己是带生命周期的 。

LiveData很少单独使用,LiveData使用时外层一般都会包装一个ViewModel,ViewModel可以帮我们处理数据,比如屏幕旋转时数据的保存,下面通过一个实例看下LiveData的用法

LiveData实例

首先定义了一个 ViewModel

public class NameViewModel extends ViewModel {
    private MutableLiveData<String> currentName;
    public int i = 0;
    public MutableLiveData<String> getCurrentName(){
            if(currentName==null){
                currentName=new MutableLiveData<>();
            }
            return currentName;
    }
}

在 Activity 中使用该 ViewModel

public class NameActivity extends AppCompatActivity {
    private NameViewModel model;
    ....
    //这里把 i 变量放在 Activity里,它的值便会随Activity的生命周期变化
    //这样当屏幕旋转时 它会归零,解决这个问题 只需要把该变量定义在 ViewModel里,也就是 NameViewModel
    private int i=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       ......
        model= ViewModelProviders.of(this).get(NameViewModel.class);
        //需要一个观察者来观察数据
        Observer observer=new Observer<String>(){
            @Override
            public void onChanged(String s) {
                nameTextView.setText(s);
            }
        };
        //订阅
        //LiveData 和 观察者绑定在一起了
        model.getCurrentName().observe(this,observer);
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                String anotherName="jett"+(model.i++);
                //LiveData 的set方法 必须在主线程,setValue 相当于发送了一个消息
                //告诉订阅者数据发生改变了
                //子线程调用 postValue
                model.getCurrentName().setValue(anotherName);
            }
        });
    }
}

源码分析

从绑定观察者开始入手

model.getCurrentName().observe(this,observer);

LiveData.java

@MainThread //这里第一个参数是 Activity,第二个参数是 observer
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");//先判断是不是在主线程,不是的话会抛异常
    //如果Activity的状态是 DESTROY 状态直接退出
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    //把观察者和被观察者一起包装在一个类里面,观察者就是我们自己定义的那个匿名内部类,
    //被观察者是 Activity
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //把包装类 以观察者自己为key存到一个集合中,这里是因为有可能会有多个观察者
    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;
    }
    //同Lifecycles那一节添加观察者的动作是一样的,之后就会绕到原来的代码了
    owner.getLifecycle().addObserver(wrapper);
}

绑定好了之后,来看一下发消息(调用setValue)怎么发的

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");//先判断是不是在主线程
    mVersion++;//LiveData里的一个变量,每一次setValue该变量都被加1
    mData = value;//这里的value我们传了一个字符串
    dispatchingValue(null);//分发
}

在看dispatchingValue源码之前,我们先来看一下LiveData 及其观察者中的两个重要变量 mVersion 、mLastVersion,在后续分析中将会用到

public abstract class LiveData<T> {

static final int START_VERSION = -1;

private int mVersion = START_VERSION;//LiveData中的一个重要变量

private abstract class ObserverWrapper {//LiveData的内部类
    final Observer<? super T> mObserver;
    boolean mActive;
    //observer中的 mLastVersion  的初始值也是 -1,
    //如果有多个观察者大家都等于 -1
    int mLastVersion = START_VERSION;//初始值是 -1

接下来我们进入 dispatchingValue 

//该方法第一次进来是传的空null
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {//第一次传参为null,会进入else
            //获取之前的集合的迭代器,把每一个观察者取出来
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                //getValue方法返回一个观察者,拿出来后发一个通知
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
//被观察者(Activity) setValue 的时候要发通知
private void considerNotify(ObserverWrapper observer) {

//该方法会先做一个状态判断,之后做version判断
//判断是不是活动状态,不是直接退出,这个活动状态表示的就是RESUMED
//Activity RESUMED 之后屏幕才会显示出来
    if (!observer.mActive) {
        return;//不是在活动状态证明你的界面是在后台,在后台的话消息就不会发给你
    }
    //如果是在前台会再次做一个判断
    //shouldActive 会判断当前Activity的当前状态是不是 RESUMED
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    //当我们第一次setValue执行到这里时 mVersion 在setValue方法中被+1后变为0
    //mLastVersion 的仍然为 -1,所以这里不会退出
    if (observer.mLastVersion >= mVersion) {
        return;
    }

//注意:上面的三个if 判断只要不通过就会一直在这跑
//它不会造成死循环,因为它肯定可以找到一个在前台的 Activity,代码是一个类似于死循环的代码

   // 这里将LiveData的mVersion 赋值给观察者的 mLastVersion 观察者的 mLastVersion 也变为 0 
    observer.mLastVersion = mVersion;

//上面三个 if 就是为了判断你的数据是不是最后一次更新的,这样就可以保证你的最新的数据能够
//更新到界面显示,当Activity没有在前台时是不会执行的,提高了效率

    //这个observer 的 onChanged方法是我们自己在NameActivity中实现了,可以用最新的数据来更新UI
    observer.mObserver.onChanged((T) mData);
}

我们进入 activeStateChanged 方法

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) {
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this);//再次迭代执行分发,这次将执行initiator != null的情况
    }
}

注意:initiator != null的情况 会进入considerNotify(initiator); 所以 dispatchingValue 和 considerNotify 这两个方法在来回地一直跑,一直执行

接下来我们看一下在子线程中调用的postValue方法

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }//前面的代码都是同步处理

    //核心代码:子线程取用的时候还是回归到主线程里面
    //用一个线程池,让线程切换到主线程来执行
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
public void postToMainThread(Runnable runnable) {
   //参数 runnable就是 mPostValueRunnable ,在它的run方法里执行了 setValue
    mDelegate.postToMainThread(runnable);
}

看一下 mPostValueRunnable 的定义

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        setValue((T) newValue);//当在其他线程里执行时,最终还是会调用到主线程执行setValue
    }
};

LiveData的用法

LiveData 的一个总线用法 在实际的开发中有很多 UI ,会有很多 LiveData跟它们统一再用,我们可以把多个这种 LiveData(它们全部放在内存里会很消耗内存) 放在一个总线里,做一个总线 LiveDataBus,简单说就是一个集合,如果说 Activity 要用的时候,都可以把自己要用的 LiveData放到 这个总线 LiveDataBus里,总线整个项目里有一个就够了

LiveData 正常的用法流程: 1. 先 new 出一个 LiveData---->2. 把 observer 绑定到该 LiveData----->3、执行 setValue (setValue其实是去执行 onChanged 方法) 

当我们使用总线时 LiveData的用法流程:(这里有个问题:先发消息后绑定) 1. 先 new 出一个 LiveData---->2. 执行 setValue (setValue其实是去执行 onChanged 方法)---->3.把 observer 绑定到该 LiveData(UI 有了) ------>执行下一个 setValue...... 所以上面问题的解决方案:我们只需要消费掉 第二步的 setValue 让它无效,之后UI已经显示出来了,再setValue就回归到正常流程里了,不会出问题了 失效的方法:上面分析源码的时候,considerNotify 遇到的那三个 if ,随便处理哪一个都可以,不过前两个是用来保活的,尽量不要修改,可以改变第三个 if 判断 我们可以让第三个 if 满足条件:这里可以修改 mLastVersion 变量,这样第一次操作就会直接返回 修改 mLastVersion 变量 可以使用 hook 技术勾到源码的运行层面上来

时序图

最后来一张时序图,整理一下思路。(注:addObserver的流程时序图已经在 Jetpack之Lifecycles 那篇文章中详细画过了,这里就省略了)