Android Jetpack组件架构: LiveDate的使用和原理
导言
继Lifecycle组件之后我们接下来要介绍的就是LiveDate组件,所谓LiveDate字面意思上就是有声明的数据,当数据有改动时该组件可以感知到这个操作并将该事件通知到其观察者,这样我们就可以在观察者中做出一些处理,一般都是用来更新UI的操作。这样就实现了当数据改变时U界面自动更新的效果。
LiveDate的使用
LiveDate + ViewModel 实现UI感知数据变化
首先我们来介绍使用LiveDate的基本姿势,也就是LiveDate配合ViewModel使用,这符合MVVM架构的设计,至于ViewModel我们将在之后的篇章中介绍到,这里先简单使用一下它。
首先我们创建一个ViewModel类:
class SimpViewModel: ViewModel() {
//LiveDate的实例--在ViewModel中创建
val mLiveDate = MutableLiveData<String>()
}
里面存储了一个LiveDate的实例,MutableLiveData
是LiveDate
的实现类,后面的String泛型代表该LiveDate中存储的数据是String类型的。紧接着我们在Activity中使用,这里我们启用了ViewBinding进行布局的自动绑定:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
//懒加载
private val mBinding:ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private lateinit var mViewModel:SimpViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
//创建出ViewModel对象
mViewModel = ViewModelProvider(this).get(SimpViewModel::class.java)
//获得LiveDate对象
val mLiveDate = mViewModel.mLiveDate
//观察LiveDate,一旦有数据改变就触发观察方法更新UI
mLiveDate.observe(this,{
mBinding.simpleLiveDate.text = it
Log.d(TAG, "在线程:${Thread.currentThread().name}中运行")
})
GlobalScope.launch{
//延迟5s
delay(5000)
mLiveDate.postValue("LifeDate已更新")
}
}
}
可以看到我们首先是创建出了一个ViewModel对象然后获取到了它的LiveDate实例,接着我们就调用到了LiveDate的observe
方法,该方法是用于为LiveDate对象添加一个观察者的,第一个参数为LifecycleOwner
对象,这个我们在Lifecycle的介绍中提到过了,是一个具有生命周期的对象,传入的意义是什么呢?这主要是为了在观察的Activity进入休眠状态时不再通知其观察者,这样可以实现正确通知观察者的效果。传入的第二个对象就是一个观察者了,我们需要实现其Oberserve方法,这个方法就是在LiveDate的数据变化时触发的。
最后我们创建了一个协程,先延时五秒钟然后通过postValue
方法改变了LiveDate中的值,具体我们还有一个setValue
方法也可以改变LiveDate的值,不同之处在于postValue
方法可以在非主线程中调用而setValue
方法必须在主线程中调用,在这个observe方法中我们还额外打印出来这个回调方法是在哪个线程中执行的,通过打印我们也可以发现是在主线程中被调用的,这也在意料之中,因为只能在主线程中更新UI:
更改LiveDate的数据
接下来我们将在LiveDate更新完的数据传递到观察者对象之前对该数据进行更改,相当于是对setValue
或者postValue
设置的值进行额外的处理再传递给Observe方法执行。这里我们以一个根据当前温度来判断天气的例子,先在ViewModel中新建一个MapLiveDate:
class SimpViewModel: ViewModel() {
//LiveDate的实例--在ViewModel中创建
val mLiveDate = MutableLiveData<Double>()
val mMapLiveDate = Transformations.map(mLiveDate,{
val s1 = when(it) {
in -20.0 .. 0.0 -> "很冷"
in 0.0 .. 10.0 -> "冷"
in 10.0 .. 15.0 -> "较冷"
in 15.0 .. 28.0 -> "温暖"
in 28.0..Double.MAX_VALUE -> "热"
else -> { "您是人吗" }
}
"当前天气:${s1}"
}
)
}
这个LiveDate是通过Transformations.map
方法创建出来的,第一个参数就是它跟踪的LiveDate,一旦它跟踪的LiveDate发生了数据更新事件,该LiveDate就会将变化后的数据捕获并可以在其方法中对捕获的数据进行处理,比如说在这里我们就根据传入的Double类型的参数判断天气并将其转化成String类型的参数传递给该LiveDate的Observe方法。
然后我们再更改Activity中的代码:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val mBinding:ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private lateinit var mViewModel:SimpViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
//创建出ViewModel对象
mViewModel = ViewModelProvider(this).get(SimpViewModel::class.java)
//获得LiveDate对象
val mLiveDate = mViewModel.mLiveDate
val mMapDate = mViewModel.mMapLiveDate
//观察LiveDate,一旦有数据改变就触发观察方法更新UI
mLiveDate.observe(this,{
// mBinding.simpleLiveDate.text = it.toString()
Log.d(TAG, "更新之后的数据为:${it.toString()}")
Log.d(TAG, "在线程:${Thread.currentThread().name}中运行")
})
mMapDate.observe(this,{
mBinding.simpleLiveDate.text = it
})
GlobalScope.launch{
//延迟5s
delay(3000)
mLiveDate.postValue(10.0)
}
}
}
不同之处在于这里我们是在我们新创建出来的MapLiveDate中更新UI的,按照道理来说这个mMapLiveDate中收到的参数就是我们之前根据Double将其转化成String之后的结果,我们来看运行结果:
可以看到这里更新的UI就是我们转化之后的数据了,那前一个Observer接收到的是什么参数呢:
可以看到前一个Observer接收到的参数还是一样的。
合并多个LiveDate的数据源
这个所谓合并多个LiveDate实际上就是用一个大的LiveDate来监听多个其他小的LiveDate,来达到任何一个小LiveDate发生改变时大的LiveDate都能监听到的效果。话不多说,直接上代码,viewModel:
private const val TAG = "SimpViewModel"
class SimpViewModel: ViewModel() {
//LiveDate的实例--在ViewModel中创建
val mLiveDate = MutableLiveData<Double>()
val mLiveDate2 = MutableLiveData<Double>()
val mMapLiveDate = Transformations.map(mLiveDate,{
val s1 = when(it) {
in -20.0 .. 0.0 -> "很冷"
in 0.0 .. 10.0 -> "冷"
in 10.0 .. 15.0 -> "较冷"
in 15.0 .. 28.0 -> "温暖"
in 28.0..Double.MAX_VALUE -> "热"
else -> { "您是人吗" }
}
"当前天气:${s1}"
}
)
val mMergyLiveDate = MediatorLiveData<Double>()
init {
mMergyLiveDate.apply {
addSource(mLiveDate, {
Log.d(TAG, ":接收到了mLiveDate的数据:${it}")
})
addSource(mLiveDate2, {
Log.d(TAG, ":接收到了mLiveDate2的数据:${it}")
})
}
}
}
Activity:
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
private val mBinding:ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private lateinit var mViewModel:SimpViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
//创建出ViewModel对象
mViewModel = ViewModelProvider(this).get(SimpViewModel::class.java)
//获得LiveDate对象
val mLiveDate = mViewModel.mLiveDate
val mLiveData2 = mViewModel.mLiveDate2
val mMapDate = mViewModel.mMapLiveDate
val mMergyLiveDate = mViewModel.mMergyLiveDate
//观察LiveDate,一旦有数据改变就触发观察方法更新UI
mLiveDate.observe(this,{
Log.d(TAG, "LiveDate1 更新之后的数据为:${it.toString()}")
Log.d(TAG, "在线程:${Thread.currentThread().name}中运行")
})
mLiveData2.observe(this,{
Log.d(TAG, "LiveDate2 更新之后的数据为:${it.toString()}")
})
mMapDate.observe(this,{
mBinding.simpleLiveDate.text = it
})
mMergyLiveDate.observe(this,{
Log.d(TAG, "接收到的数据为:${it}")
})
GlobalScope.launch{
//延迟5s
mLiveDate.postValue(10.0)
delay(3000)
mLiveData2.postValue(15.0)
}
}
}
可以在打印日志中看到,这两个LiveDate发生改变时MergyLiveDate都感受到了:
以上就是所有有关LiveDate的基础使用了,接下来我们来分析LiveDate的原理。
LiveDate的原理
首先我们直接点开源文件,它的开头也有一大段注释:
这大段的注释中有几个比较重要的信息,首先是Observer观察者只有在处于Active
状态时才能感受到LiveDate中数据的变化,至于这个Active
状态,是在Lifecycle组件的状态处于Lifecycle.State.STARTED
或者Lifecycle.State.RESUMED
时Observer才处于
Active
状态,这也就是我们一开始说的LiveDate是依赖于Lifecycle组件框架的原因。当宿主Lifecycle的状态处于Lifecycle.State.DESTROYED
时,Observer将会被自动移除,这个功能显然可以避免内存泄漏的问题。
Observe开始观察
首先我们来分析Observe
方法,该方法的作用就是为LiveDate设置一个观察者:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");//判断是否运行在主线程中
if (owner.getLifecycle().getCurrentState() == DESTROYED) {//如果宿主Lifecycle当前的状态为DESTROYED
// ignore
return;//说明当前LiveDate不是处于Active状态,不进行任何处理,直接返回
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //将宿主Owner和观察者Observer包装成一个装饰类
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); //判断LiveDate中是否已经有Observer
if (existing != null && !existing.isAttachedTo(owner)) { //如果LiveDate中的Observer列表中已经存在了
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles"); //报错,一个Observer不能多次观察一个LiveDate
}
if (existing != null) {
return;
}
//如果原有的列表中不存在的话,说明之前没有注册过,将其添加进Observers列表中
owner.getLifecycle().addObserver(wrapper);
}
在上面的方法中已经对大概的逻辑进行了一些注释了,首先这个方法是需要在主线程执行的,不然就会抛出异常。之后会首先判断当前LiveDate是否处于Active
状态,如果不是Active
状态那么就直接返回,此时不需要进行数据的观察。接下来会将LifecycleOwner和传入的Observer包装成一个LifecycleBoundObserver
对象,关于这个类我们等等再来细看。之后调用到mObservers.putIfAbsent
方法,mObservers
是一个可以安全迭代(即可以在迭代时安全修改)的Map:
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
至于putIfAbsent
方法和一般的put
方法有一些差别,如果传入的key对应的value已经存在,那么将不会进行替换,并且返回已经存在的value,如果传入的key对应的value不存在的话则会将key和value添加进入Map中并且返回null
.
所以这一个方法完成的是两个目的,首先是在Map中添加元素,其次是判断传入的key在之前是否已经被添加过了,如果LiveDate中的Observer列表中已经存在了就直接抛出异常,因为一个Observer不允许被多次添加进入一个LiveDate的观察者列表中。如果之前不存在的话,则会在最后调用addObserver
方法将其添加进入Observer列表中,不过这里我们可以发现这里将其添加进入的是Lifecycle
的列表,而不是LiveDate的列表(LiveDate的列表在之前的putIfAbsent方法
中已经添加了。)
LifecycleBoundObserver类 和 ObserverWrapper类
我们先来看LifecycleBoundObserver这个类的整个结构:
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() {
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;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
首先可以看到它实现的是LifecycleEventObserver
接口,还记得这个接口吗?在上一篇关于Lifecycle的文章中提到过:
既然已经知道了是LifecycleEventObserver接口的话,那么其实我们就可以很清楚的知道这个Observe事件并不是由LiveDate自身分发的,它的分发是和Lifecycle中的默认观察者一样的,也就是说是通过Lifecycle的Observer列表进行分发的。所以我们接下来直接看它的onStateChanged
方法就好了,这个方法是在Lifecycle进行事件分发时触发的。
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();//获得当前宿主的生命周期状态
if (currentState == DESTROYED) { //如果宿主已经消失,那么将其从Lifecycle的观察列表中移除
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null; //之前的状态
while (prevState != currentState) { //当之前的状态 不等于 当前状态
prevState = currentState; //将状态迭代
//这里调用`activeStateChanged方法`
activeStateChanged(shouldBeActive());//`shouldBeActive`方法获得的是当前状态是否应该是Active状态
//更新当前状态
currentState = mOwner.getLifecycle().getCurrentState();
}
}
具体的逻辑也已经在上面标注出来了,唯一的问题就是activeStateChanged
方法,我们来看这个方法,这个方法是在LiveDate的内部抽象类ObserverWrapper
中定义的,他也是上面的LifecycleBoundObserver
的父类,所以接下来又要看ObserverWrapper
类了:
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;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
}
这个类也是和它的名字相符合,整个一个就是对Observer进行了装饰,所以整个装饰下来就是这样的:
我们的Observer经过重重装饰就已经实现了LifecycleEventObserver
接口,从而其方法也可以由整个Lifecycle框架进行分发了。回到正题,来看它的activeStateChanged
方法,如果Active状态并没有发生改变的话就直接返回,不作任何处理。否则他会调用到changeActiveCounter
方法,并且如果当前新状态为Active的话还会进行值的分发。
LiveDate更改观察者状态
前面提到了会调用changeActiveCounter
方法,这个方法是在LiveDate中定义的,具体如下:
void changeActiveCounter(int change) {
int previousActiveCount = mActiveCount; //获取之前的ActiveCount的值
mActiveCount += change; //更新ActiveCount的值
if (mChangingActiveState) { //如果当前有线程正在处理的话就直接返回
return;
}
mChangingActiveState = true;
try {
while (previousActiveCount != mActiveCount) { //当之前的ActiveCount的值与现在的ActiveCount的值不等的话
boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;//如果之前的ActiveCount为0且现在的ActiveCount值大于0
boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;//如果之前的ActiveCount大于0且当前的ActiveCount值等于0
previousActiveCount = mActiveCount;//更新之前的ActiveCount的值
if (needToCallActive) {//如果需要调用onActive方法的话
onActive();
} else if (needToCallInactive) {//如果需要调用onInActive方法的话
onInactive();
}
}
} finally {
mChangingActiveState = false;
}
}
该方法做的事情主要就是维护mActiveCount的值,这个mActiveCount就是有多少个之前处于Active状态下的Observer的数量,如果观察该LiveDate的观察者全都不是处于Active状态的话就调用onInactive()
回调,并且此时逻辑上LiveDate是处于非Active状态的,反之就会回调onActive
方法,这个方法默认是没有实现的。
LiveDate分发Value
接下来我们再来看dispatchingValue
方法:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
可以看到这个方法一开始的处理也是和之前的changeActiveCounter方法类似,主要是为了防止并发修改。接下来的核心逻辑其实就是considerNotify
方法:
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) { //如果当前传入的Observer不是Active状态就直接返回
return;
}
if (!observer.shouldBeActive()) {//如果传入的observer不应该处于Active状态,也就是说当前是Active状态但是之后不应该处于Active状态
observer.activeStateChanged(false);//更新Observer的active状态
return;
}
if (observer.mLastVersion >= mVersion) {//如果observer的上一个Version值大于当前的Version值
return; //直接返回
}
observer.mLastVersion = mVersion; //更新Version值
observer.mObserver.onChanged((T) mData); //触发回调方法
}
整个逻辑也很清楚首先会更新当前的Active状态,之后根据当前的状态和接下来的状态(此处的状态特指Active状态)来进行Observer标志位的设置,这里还涉及到一个Version值的概念,我们之后再提。最后一行就是触发我们写入的onChanged方法了,也就是我们传入的回调方法。不过这里只是触发我们回调的一种情况,就是宿主的生命周期变化时会将触发回调,我们先来简单总结一下:
通过set/postValue方法触发回调
接下来是我们最重要的一个部分,那就是通过setValue
或者postValue
方法触发我们的回调,这次我们先来看postValue
方法
protected void postValue(T value) {
boolean postTask;
//同步方法,进行上锁
synchronized (mDataLock) { //pendingData,意思就是尚未被发送的数据
postTask = mPendingData == NOT_SET; //此处判断本次投递是不是挂起的任务,第一次总是true
mPendingData = value;//设置挂起数据为当前数据
}
//将锁释放
if (!postTask) { //如果不是挂起任务的话,也就是当上次任务还没有投递完成时
return; //直接返回
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
这里涉及到了mPendingDate
这个成员变量,我们会在后面比较setValue
方法中再次提到,这里会首先通过线程池的postToMainThread
方法投递mPostValueRunnable
的任务,这个任务是在LiveData创建的:
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {//上锁
newValue = mPendingData;//将新值设置为mPendingData,这个值在之前的postValue方法中设置
mPendingData = NOT_SET;//重新将Pending位设置为NOT_SET
}
setValue((T) newValue);//调用setValue方法
}
};
至于postToMainThread
方法,就是将runnable通过Handler机制投递到主线程中执行,然后调用其setValue
方法修改Value的值。我们可以设想一下当只有一条线程执行一次postValue
的情况,首先会将mDataLock
进行上锁,然后通过Handler将任务发送到主线程执行,这个runnable运行时也会上锁,然后将新值取出,将mPendingData
转为NOT_SET
状态。最后执行setValue
方法更改新值。
在多线程的情况下,我们再来分析,当有两条线程同时执行postValue方法时首先先进入的那条线程会将设置postTask
时的代码块先进行上锁,这样第二条线程只能阻塞在代码块之前;然后第一条线程执行完值的设置时将锁给释放,然后第二条线程就进入了值的设置的阶段,这个时候由于锁被第二条线程给持有了,所以第一条线程在执行runnable时就会被阻塞,第二条线程设置完值之后再将锁给释放,这样第一条线程就执行了runnable方法。不过这个时候第一条线程设置的值实际上是第二条线程中传入的值,因为这个mPendingData
是volatile
状态的:
volatile Object mPendingData = NOT_SET;
总之,postValue方法保证了在多线程的情况下总是只有一个最新的值被执行具体的setValue
操作,这样做的好处就是可以提高性能,且不会在主线程的消息队列中堆积过多任务,导致主线程的阻塞。好了看完了postValue
方法之后我们再来看setValue
方法:
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
首先这个方法也是一个只能在主线程调用的方法,其次它维护了一个记录值变化的记录值mVersion
,也就是说每次成功对LiveData中的数据进行修改后mVersion
的值都会发生变化,以此来判断是否需要分发回调事件,最后触发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 {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
具体来说会进入到后面那段for循环中:
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
很显然是对每个观察者都调用了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);
}
这里判断观察者是否需要执行回调就是通过开始的三个if条件来判断,当Observer满足以下三个条件之一时将不会执行回调:
- Observer不处于Active状态
- Observer不应该处于Activie状态,也就是说当前是Active状态但马上就不是Active状态了
- 上一次的Observer的Version值大于等于当前的Version值(正常修改之后Version值应该会变大)
当不满足上面三个条件时才会执行回调方法,至于Observer状态的设置是由Lifecycle进行管理的。不过这里思考一个问题,当一个LiveData先执行改变Value,然后一个Observer再观察时也是满足执行回调方法的情况的,也就是说LiveData默认是实现了粘性事件的,如果我们不想要粘性事件的话,思考一下该如何做。我们可以在每次observer
进入到Active状态时的mLastVersion为绑定的LiveData的mVersion值。
事件分发的总结
老规矩还是以一张图总结setValue
或者postValue
方法: