LiveData源码分析

228 阅读9分钟

概论

LiveData 是基于观察者的消息订阅/分发组件,能感知宿主(Activity/Fragment)的生命周期,这种感知能力可确保 LiveData 仅将消息分发给与活跃状态的观察者,即只有处于活跃状态的观察者才能收到消息,同时,LiveData的跨线程通信的能力也是其一大特点。

基于生命周期,其实就是在当前宿主 LifecycleOnwer 注册一个 Observer,那么宿主每次生命周期的变化,都会回调给观察者的 onStateChange() 方法,即便是刚刚注册的观察者宿主也会回调 onStateChange() 方法,会有一个状态同步的过程,LiveData 也是利用这个能力,巧妙实现了当宿主销毁的时候,自动移除注册进来的 Observer,从而避免了手动移除的麻烦。更不会造成内存泄漏,这个也是它的核心思想。

理解LiveData的原理,我们先从使用过程开始谈起。LiveData是一个abstract的抽象类,我们通常使用它的子类 MutableLiveData 来实现通信。

public abstract class LiveData<T> {
    ......
}

MutableLiveData则很简单,只是简单的继承了 LiveData, 同时修改了 LiveData 中发送数据的方法postValue() 和 setValue()的调用权限。

LiveData中,方法postValue() 和 setValue()的权限都是protected,因此我们在跨出 LiveData 的package包之后,就无法再直接方便的使用了。

而 MutableLiveData则是将这个权限修改为 public:

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

    public MutableLiveData(T value) {
        super(value);
    }

    public MutableLiveData() {
        super();
    }

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

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

通常,我们在使用MutableLiveData时,初始化过程如下:

MutableLiveData<String> liveData = new MutableLiveData<>();

从前面的源码中我们可以看到,MutableLiveData的构造方法什么也没做,直接调用了父类LiveData的构造方法,LiveData的构造方法如下:

/**  
* Creates a LiveData initialized with the given {@code value}.  
* @param value initial value  
*/  
public LiveData(T value) {  
mData = value;  
mVersion = START_VERSION + 1;  
}  
  
/**  
* Creates a LiveData with no value assigned to it.  
*/  
public LiveData() {  
mData = NOT_SET;  
mVersion = START_VERSION;  
}

在构造方法中,完成了变量的初始化,其中,mData 是一个 volatile关键字修饰的 Object 类型的对象,这个变量在我们使用postValue()方法时非常关键,后面会细说。 mVersion是一个int型的变量。 mData和mVersio具体如下:

    private volatile Object mData;
    private int mVersion;

然后就是利用 MutableLiveData 的对象 liveData 发送数据,发送数据有两种方式,分别是postValue() 和 setValue()。我们先看看setValue()。

setValue()

public class MutableLiveData<T> extends LiveData<T> {
    ......
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

在这里,MutableLiveData的 setValue()方法也是直接调用父类 LiveData 的set方法。我们进去看看:

    // 此方法必须从主线程调用。如果你需要从后台线程设置一个值,则要使用 postValue(value) 方法
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }

在这里,我们可以看到该方法被添加了“@MainThread”的注解,确实如注释所说,只能在主线程里面使用。然后方法里面主要做了三件事:

  1. 调用assertMainThread()方法:鉴定调用该方法的当前线程是否主线程;
  2. 更新 mVersion 和 mData 两个对象的值;
  3. 调用 dispatchingValue() 方法开始处理任务。

这里先看看 assertMainThread()方法 :

    static void assertMainThread(String methodName) {
        if (!ArchTaskExecutor.getInstance().isMainThread()) {
            throw new IllegalStateException("Cannot invoke " + methodName + " on a background" + " thread");
        }
    }

这个很简单,没什么说头。再来看看 dispatchingValue(null) 方法:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
	// 变量mDispatchingValue初始化时为false;分发Value值的过程中被赋值为true;
	// 分发Value值操作完成之后,再次被重置为false;
	if (mDispatchingValue) {// mDispatchingValue为true,表示当前正在执行Value值分发操作。
		mDispatchInvalidated = true;
		return;
	}
	mDispatchingValue = true;
	do {
		mDispatchInvalidated = false;
		// setValue()传的 initiator 是null,因此这里执行 else{}分支
		if (initiator != null) {
			considerNotify(initiator);
			initiator = null;
		} else {
			for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
					mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
				// iterator.next().getValue()获取到的是一个 ObserverWrapper 对象
				considerNotify(iterator.next().getValue());
				if (mDispatchInvalidated) {
					break;
				}
			}
		}
	} while (mDispatchInvalidated);
	mDispatchingValue = false;
}

上面的代码,最重要的就是 considerNotify()方法,总前面调用 setValue()方法开始,一路跟踪下来,可以发现,这里的入参 initiator 是null空的。因此执行 else{ ...... }分支。重点不在这里,我们直接分析 considerNotify()方法:

private void considerNotify(ObserverWrapper observer) {
	if (!observer.mActive) {
		return;
	}
	if (!observer.shouldBeActive()) {
		observer.activeStateChanged(false);
		return;
	}
	if (observer.mLastVersion >= mVersion) {// 这里通过判断两次 Version 的值,来消除旧数据的影响。
		return;
	}
	observer.mLastVersion = mVersion;
	observer.mObserver.onChanged((T) mData); // 最关键的地方
}

在上面最后一行代码中,onChanged()是不是有点熟悉?在哪里见过的似曾相识之感?不卖关子了,这个方法就是这里的这个:

liveData.observe(this, new Observer<String>() {
	@Override
	public void onChanged(String s) {
		// TODO:
	}
});

是的,observer.mObserver.onChanged((T) mData)这个调用,最后就是调用到这里来的。

然后就是 开发者 在 onChanged()方法中,处理逻辑。setValue()方法是在主线程中执行,来一次数据,就回调一次数据。不涉及跨线程的调度工作,因此很简单。

关于 observer.mObserver添加监听对象的过程,我们放在最后讨论。

postValue()

postValue()的调用如下:

public class MutableLiveData<T> extends LiveData<T> {
    ......
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    ......
}

LiveData中 postValue()实现如下:

protected void postValue(T value) {
	boolean postTask;
	synchronized (mDataLock) {
		// mPendingData初始值就是 NOT_SET;之后缓存的是 postValue()
		// 方法传进来的 Value值,在完成分发之后,又被重置为 NOT_SET;
		postTask = mPendingData == NOT_SET;
		// 将要向下分发的值,缓存到 mPendingData
		mPendingData = value;
	}
	// mPendingData当前缓存的值,跟初始值 NOT_SET 不相同,则表示当前正在执行分发操作,不继续向下分发;
	// 因为系统会在 mPostValueRunnable 任务中将 mPendingData 重置为NOT_SET;
	if (!postTask) {
		return;
	}
	// 切换到主线程,调用 setValue()方法执行分发任务。
	ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

在这里,我们发现 postValue()方法没有 setValue()方法那样的注解“@MainThread”,说明 postValue()方法的使用不受线程的限制。 在这里的 postValue()方法中,出现了几个重要的变量:

  1. postTask,这个变量是 postValue()方法的一个关键点,我们稍后细说。
  2. synchronized:在这里,postValue()方法使用了 synchronized 同步关键字,结合前面的解释,说明 postValue()方法是线程安全的。
  3. mDataLock:这是一个 final类型的Object对象:final Object mDataLock = new Object();主要作用就是在当前(Mutable)LiveData中,通过synchronized 关键字实现线程安全
  4. NOT_SET 和 mPendingData:

NOT_SET是一个static + final类型的 Object对象:static final Object NOT_SET = new Object();

mPendingData 是一个volatile 关键字修饰的Object对象:volatile Object mPendingData = NOT_SET; 它保证并发编程时变量的可见性和有序性(禁止指令重排序),不能保证原子性。

可见性的验证:

public class Test1 {
	int number = 0;
	public void add(){    this.number = 10;    }
	public static void main(String[] args) {
		Test1 test1 = new Test1();
		//创建第一个线程
		new Thread(() -> {
			System.out.println(Thread.currentThread().getName()+"开始执行时,number = " + test1.number);
			try{ Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}
			test1.add(); //暂停3秒后,修改number的值。
			System.out.println(Thread.currentThread().getName()+"执行add()方法之后,number = " + test1.number);

		},"Thread_One").start();
		//第二个是main线程
		while (test1.number == 0){    //如果第二个main线程 可以监测到number值的改变,就会跳出当前循环,执行后续程序。    }
		System.out.println(Thread.currentThread().getName() + "程序结束!");
	}
}

代码会在运行时,卡死在 循环while (test1.number == 0)里,跳不出来。

加上volatile关键字之后:

public class Test2 {
	volatile int number = 0;
	public void add(){    this.number = 10;    }
	public static void main(String[] args) {
		Test2 test2 = new Test2();
		//创建第一个线程
		new Thread(() -> {
			System.out.println(Thread.currentThread().getName()+"开始执行时,number = " + test2.number);
			try{ Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}
			test2.add();//暂停3秒后,修改number的值。
			System.out.println(Thread.currentThread().getName()+"执行add()方法之后,number = " + test2.number);
		},"Thread_One").start();
		//第二个是main线程
		while (test2.number == 0){    // 由于变量number上加了volatile关键字,使得main线程可以监测到number值的改变,从而结束循环。    }
		System.out.println(Thread.currentThread().getName()+"程序运行结束!");
	}
}

代码会在运行时,从循环while (test1.number == 0)中正常结束。

从代码:postValue()方法的局部代码:

synchronized (mDataLock) {
    postTask = mPendingData == NOT_SET;
    mPendingData = value;
}    

可以发现:当 mPendingData 值等于初始值,也就是 NOT_SET 时时,postTask 被设置为true,然后 mPendingData 被赋值为 我们通过postValue()方法传递进来的 value。

当 mPendingData不等于初始值,也就是 mPendingData != NOT_SET时,postTask值为false。

那么 postTask 和 mPendingData 在哪里会用到那? mPendingData又是在哪里被reset为初始值呢?我们继续往下看。

  1. if(){}判断
if (!postTask) {
	return;
}

在这里,postTask值为false,直接return,通过postValue()方法传递的值,直接在这里被拦截掉,不会被传出去。

  1. ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);

这里,执行 mPostValueRunnable 任务,其实本质上就是通过Handle发送消息,以实现跨线程通信,执行任务:

这里,ArchTaskExecutor是 TaskExecutor 的子类,采用了装饰模式实现。postToMainThread()方法的真实实现是在 DefaultTaskExecutor.java中。

@Nullable
private volatile Handler mMainHandler;
@Override
public void postToMainThread(Runnable runnable) {
	if (mMainHandler == null) {
		synchronized (mLock) {
			if (mMainHandler == null) {
				mMainHandler = createAsync(Looper.getMainLooper());
			}
		}
	}
	mMainHandler.post(runnable);
}

mMainHandler就是一个Handle,调用post方法执行任务。

  1. mPostValueRunnable任务:
private final Runnable mPostValueRunnable = new Runnable() {
	@SuppressWarnings("unchecked")
	@Override
	public void run() {
		Object newValue;
		synchronized (mDataLock) {
			newValue = mPendingData;
			mPendingData = NOT_SET;
		}
		setValue((T) newValue);
	}
};

在这里,同样是通过 mDataLock 对象来给 mPostValueRunnable任务添加同步锁,上一次的任务还没有执行,这里就先等待。其实这里有一个瑕疵,synchronized同步锁应该将 Object newValue 对象一并包含进去,否则,后面执行 setValue((T) newValue) 时,newValue有报NPE空指针的风险。

在synchronized同步锁里面,先是将 mPendingData 的值赋值给 newValue,然后再将 mPendingData 对象给reset为初始值,也就是 NOT_SET。

最后是调用 setValue((T) newValue),执行任务,这里开始,跟前面 setValue()方法的分析是一样的了。

LiveData通信,setValue()和 postValue()总结起来就是:

setValue():

a、调用assertMainThread()方法鉴定调用该方法的当前线程是否主线程;
b、在considerNotify()方法中,会判断新来的入参值与上一次的参数值的关系,如果是旧的,以前老数据,就不下发了。如果是新数据,就先缓存新的数据,然后再调用
   observer.mObserver.onChanged((T) mData)回调,下发数据。
   
   

postValue():

a、先在同步方法中判断上一次的数据处理完没有,如果还没有处理完,新的数据就直接被拦截,不下发了。同时,将最新的数据缓存到内存中, 供下次数据发送过来时,判断使用。
b、调用DefaultTaskExecutor.java装饰模式返回来的Handler对象,执行 mPostValueRunnable 任务,实现跨线程通信。
c、在 mPostValueRunnable 任务中,显示将新的值暂存到临时变量 newValue,然后重置变量 mPendingData 为初始值。
d、最后调用 setValue((T) newValue) 方法完成数据分发。