前言
LiveData其实已经不是一个新鲜的东西了,我是在使用MVVM框架的时候开始接触到LiveData的,LiveData用起来确实很方便。
LiveData是一个具有感知组件生命周期的类,如Activity、Fragment、Service,只要和LiveData进行了绑定,LiveData就能感知到它们的生命周期。
LiveData的基本使用我们就不讲了,因为它的使用非常简单,不懂的话网上一搜一大把。
那么,LiveData有什么好处呢?
1、监听与其绑定的界面的生命周期,因此使用LiveData我们就不需要手动管理它的生命周期了
2、组件能及时响应LiveData的数据变化,组件总能拿到LiveData的最新数据。当然,被绑定的组件响应LiveData是有一定的前提的,那就是LiveData数据发生变化 且 组件处于活跃状态
也就是说,LiveData数据即使发生了变化,也不一定会响应onChanged函数,因为它必须要求LiveData数据所在的界面处于活跃状态,才会响应onChanged函数。
3、 生命周期自动解绑,因为其能够在组件销毁的时候自动解绑,大大降低了应用产生内存泄露的概率
LiveData确实能解决内存泄漏问题,但是如果我们使用不当,其实还是会出现内存泄漏的,
例如,有一个Activity,Activity包含了一个Fragment,Fragment中有一个LiveData,因此Fragment引用了LiveData。
然后LiveData通过observe方法把Activity当作owner进行了绑定,那么这时候,LiveData的生命周期将和Activity一样。如果这时候因为某种原因,Fragment被销毁了,那么LiveData将不会被销毁,因为它被Activity引用着。LiveData本该回收却无法被回收,那么LiveData就发生内存泄漏了。
源码分析
下面我们进入源码看看LiveData的原理。
observe方法
LiveData是如何监听与其绑定的界面的生命周期的?
这就得从LiveData的与组件的绑定方法开始看了,所以我们来看看LiveData的observe方法。
// LiveData.java;
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
//如果owner的生命周期为DESTROYED,直接return
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
//将外部传进的observer对象封装成一个LifecycleBoundObserver对象
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//将observer和wrapper存放到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);
}
首先,将外部传进的observer对象封装成LifecycleBoundObserver对象,对象名称为wrapper,其实LifecycleBoundObserver就是一个可以监听生命周期的类,我们可以看看LifecycleBoundObserver类的构造方法:
LiveData.LifecycleBoundObserver类:
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
// …………………………
}
@Deprecated
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public interface GenericLifecycleObserver extends LifecycleEventObserver { }
public interface LifecycleEventObserver extends LifecycleObserver {
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
首先,我们也可以看到,LifecycleBoundObserver实现了GenericLifecycleObserver这个接口,而实际上,这个接口最终继承自LifecycleObserver这个接口的,
LifecycleObserver是不是有点眼熟?
没错,它是我们在使用lifecycle的时候应该addObserver时需要传递的参数。
所以,我们通过new LifecycleBoundObserver(owner, observer)方法得到的wrapper实际上就是一个LifecycleObserver。
在LifecycleBoundObserver类构造方法中,将observer传给其父类使用,而其父类是ObserverWrapper,
我们看看ObserverWrapper的构造方法:
// LiveData.ObserverWrapper类:
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
// ………………………………
}
可以看到,实际上只是把observer赋值给ObserverWrapper类的mObserver成员变量罢了。
我们继续回到LiveData的observe方法:
// LiveData.java;
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
//如果owner的生命周期为DESTROYED,直接return
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
//将外部传进的observer对象封装成一个LifecycleBoundObserver对象
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//将observer和wrapper存放到mObservers中
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);
}
mObservers.putIfAbsent(observer, wrapper)这行代码,将observer作为键,wrapper作为值存放到mObservers中,并且会返回一个ObserverWrapper对象。
mObservers是一个由链表实现的支持键值对存储的数据结构,同时,它支持在遍历的过程中删除任意元素,我们可以暂且把它理解为保存observer对象的集合。
如果ObserverWrapper不为null,则observe这个方法会直接return。ObserverWrapper在什么情况不为null呢?
如果观察者observer已经在mObservers这个列表中,并且observer已经有另一个所有者owner,就会不为null。
这是为了防止同一个observer被多个LifecycleOwner绑定,即一个LiveData中的同一个observer只能跟一个LifecycleOwner绑定。否则会抛出“Cannot add the same observer with different lifecycles”的异常。
在observe方法的最后,调用了owner.getLifecycle().addObserver(wrapper)方法,使得wrapper和owner的生命周期进行了绑定。
也就是说,observer此时就可以监听到owner对应的界面的生命周期的变化了!!
setValue方法
那么,setValue方法应该是我们用得最多的LiveData方法了,很明显它是用来更新LiveData的数据的,那么它是怎么实现的呢?我们来看下:
// LiveData.java:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
首先看到,setValue方法有个@MainThread注解,说明这个方法只能在主线程中使用,然后在setValue中,将value赋值给mData变量,同时,mVersion变量加1,
mVersion就是数据的版本,说白了就是用来标记数据是否变化的,后面我们还会见到,这里先略过。
方法最后,调用了dispatchingValue(null)方法,我们进入该方法:
// LiveData.java:
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;
}
可以看到,dispatchingValue方法中,有个while循环,在while循环里面,由于initiator为null,所有走else分支的代码,这里又有个for循环,这个for循环是用来遍历
mObservers中的observer对象的,
然后通过considerNotify方法将遍历到的observer对象进行处理。
我们看看considerNotify方法:
// LiveData.java:
private void considerNotify(ObserverWrapper observer) {
//observer对象不处于活跃状态,直接return
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
可以看到,首先做了一个判断,外部传入的observer对象是否处于活跃状态,不处于活跃状态,则直接return这个方法。从这里就可看出,
即使数据更新了,但如果界面不处于活跃状态,也不会对数据更新做响应。
然后往下看,我又看到了mVersion这个变量,以及一个mLastVersion变量,
判断observer.mLastVersion这个变量如果小于mVersion,会把mVersion的值赋值给observer.mLastVersion变量,
这种情况就说明数据是新的数据,就可以执行observer的onChanged方法把mData回调出去了。
postValue方法
我们先来看postValue方法。
// LiveData.java:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
postValue方法中,外部传进来的value会赋值给mPendingData,方法的最后会调用postToMainThread方法,postToMainThread,看这个方法的命名应该也能看出来一些东西,就是将这个runnable发布到主线程处理,我们进入postToMainThread详细看看:
// ArchTaskExecutor.java:
@Override
public void postToMainThread(Runnable runnable) {
mDelegate.postToMainThread(runnable);
}
它通过调用mDelegate的postToMainThread方法,mDelegate实际上就是DefaultTaskExecutor这个类,那么我们再进入这个类的postToMainThread看看:
DefaultTaskExecutor.java:
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = new Handler(Looper.getMainLooper());
}
}
}
//noinspection ConstantConditions
mMainHandler.post(runnable);
}
在这个方法中,先判断mMainHandler对象是否为null,如果为null,就创建一个,注意,它是通过Looper.getMainLooper()参数来创建的,说明这个这个mMainHandler就是主线程的handler对象。
最后调用mMainHandler.post(runnable)方法,将runnable对象交给主线程的handler来处理。
那么runnable执行了什么东西呢?
我们回到postValue方法,发现runnable就是mPostValueRunnable,我们看mPostValueRunnable是怎么定义的:
// LiveData.java:
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
我们看到它的run方法,先定义了个newValue变量,然后加锁对newValue赋值为mPendingData变量,mPendingData是在postValue方法内第三行赋值的,前面已经看到过了,然后将mPendingData置为NOT_SET。最后调用setValue方法。
所以,postValue方法实际上就是将线程切到主线程去处理setValue方法。
LiveData对生命周期的响应
举个例子:
有一个界面对一个LiveData对象进行了监听,当界面处于非活跃状态时,如果我们更新了LiveData的数据,那么界面并不会马上响应LiveData的变化(因为界面处于非活跃状态嘛)。当界面重新回到活跃状态时,刚刚更新的LiveData数据会马上进行响应。
为什么界面重新回到活跃状态时(比如activity从onPause状态回到onResume状态),刚刚更新的LiveData数据会马上进行响应呢?
前面已经介绍过,在LiveData的observe方法中,通过owner.getLifecycle().addObserver(wrapper)方法,会使得wrapper和owner的生命周期进行了绑定,
也就是说,owner生命周期变化,wrapper就能在onStateChanged这个方法中监听到。
假设owner是个Activity,那么当Activity处于非活跃状态时,对LiveData进行了setValue操作,则在considerNotify方法的开头会直接被return,也就走不到observer.mObserver.onChanged方法了,也就不能接收到LiveData的数据变化。
当Activity回到活跃状态,wrapper(即LifecycleBoundObserver)的onStateChanged方法就会被回调:
// LiveData.LifecycleBoundObserver类:
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
如果Activity处于活跃状态时,就会执行到onStateChanged中的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();
}
//如果处理活跃状态,就执行dispatchingValue(this)方法
if (mActive) {
dispatchingValue(this);
}
}
首先可以看到,mActive在这里被赋值。然后看到最后三行,判断如果处于活跃状态,就执行dispatchingValue(this)方法,那么这时又回到了setValue的那个流程中,也就是说,Activity生命周期变成活跃状态后,又能触发LiveData的值的更新了。
LiveData的自动解绑
我们知道,LiveData能大大降低内存泄漏出现的可能,原因就是因为它能够自动跟它绑定的组件进行解绑,那么这是怎么做到的呢?还是看到LifecycleBoundObserver这个内部类:
LiveData.java:
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
onStateChanged方法中,如果监听到owner生命处于DESTROYED时,会执行LiveData类中的removeObserver方法,它的实现如下:
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
//执行LifecycleBoundObserver的detachObserver方法
removed.detachObserver();
//执行ObserverWrapper的activeStateChanged方法
removed.activeStateChanged(false);
}
removeObserver方法的主要流程分三个步骤:
1、首先从mObservers集合中移除当前的observer对象
2、执行removed.detachObserver(),这行代码实际上会执行LifecycleBoundObserver的detachObserver方法,这里是真正将mOwner与mObserver解绑的地方
3、执行removed.activeStateChanged(false),这行代码实际上会执行ObserverWrapper的activeStateChanged方法,并把false传入,这个方法前面已经看过了,就不详细讲了
LiveData的mData
这里简单的介前面已经介绍过,在setValue方法中,value会赋值给LiveData的mData这个成员变量,我们我看看这个mData。
private volatile Object mData = NOT_SET;
可以看到,它是被volatile修饰的,对java并发有一定了解应该都知道,volatile是用来保证变量的可见性的,防止并发的情况下,多个线程对同一个变量进行修改,出现数据数据不一致的问题。
假设有一个场景,我想用两个线程对一个公共变量x进行加1操作(x的初始值是0),我希望得到的结果是x等于2。
其实,大多数情况下,是能正常得到2这个值的,但是!。
有种情况,线程1和线程2同时从主内存中拿到了x,并把x拷贝到自己的工作内存进行操作,两个线程都对x这个值进行了加1操作,则在这两个线程的工作内存中,x的值都变成了1,
对值进行操作完毕后,然后线程1和线程2就把x的值写回到主内存中,那么此时主内存中的x的值必然是1。
所以在这种情况,数据就不是我想要的了,那么怎样保证这个值一定是2呢?那就是使用volatile了。
由此我们也看出。LiveData中的mData是线程安全的。
LiveData跨组件通信
我们已经知道LiveData可以及时通知组件数据发生变化,还能自动解除绑定,但是LiveData的用途还不止这些。
不知道大家有没有用过EventBus,这个东西可以代替Android传统的Intent、Handler、Broadcast或接口回调,在Fragment、Activity、Service线程之间传递数据,执行方法。EventBus最大的特点就是简洁、解耦,它的框架思想就是消息的发布和订阅,它通过解耦发布者和订阅者简化Android事件传递。
其实,通过LiveData,我们也能实现类似于EventBus的框架,并且使用后不需要手动解除绑定,更加方便和优雅。那么,怎么实现呢?
跨组件通信初探
首先能想到,可以将一个LiveData设置为static的,这样不同的Activity或Fragment就都能访问这个变量,那么监听这个变量值变化也不成问题。这个方法确实能实现LiveData跨Activity/Fragment通信。
举个例子:假设有两个界面,分别是Activity1和Activity2,Activity1通过LiveData发送数据,然后在Activity2中接受数据。
那么我们可以新建一个MyStaticLiveData类,用来保存公共的LiveData:
class MyStaticLiveData {
companion object {
val liveData = MutableLiveData<String>()
}
}
然后Activity1中发送数据,并且有个button用来跳转到Activity2:
class Activity1 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
MyStaticLiveData.liveData.value = "data from LiveDataActivity."
}
fun toLiveData2Activity(view: View) {
startActivity(Intent(this, Activity2::class.java))
}
}
Activity2中,将公共的LiveData和Activity2进行绑定监听,这样就能接受到Activity1发送过来的数据了,并且我们也不需要在onDestroy中解绑LiveData,因为它会自动解绑。
class Activity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data2)
MyStaticLiveData.liveData.observe(this, {
tv2.text = it
})
}
}
这样看来,确实实现了EventBus粘性事件的功能,并且用法更加简洁。
但是它的缺点也是很明显的,它不够优雅,而且,每当我想搞个新的LiveData的时候,都需要在MyStaticLiveData中手动创建,另外,没有一个东西统一来管理这些LiveData,并且,每个LiveData都是static的,所以,开发中不建议这么写,这里只做演示用,不建议用到实际开发场景中 !!!。
另外,在开发过程中,粘性事件大部分情况下我们都是不需要用到的。而更多的场景是,我们希望监听者能在注册监听后才开始接收发送过来的消息,在注册前发送的消息一律不接收。
数据倒灌
我们刚刚演示的例子,业内有另一种更常见的叫法,数据倒灌。
一句话概括就是,先给LiveData设置了value,然后监听者才开始对LiveData进行监听,这时LiveData的value会立马回调给监听者。
产生数据倒灌的原因
这还得从源码的角度进行分析,我们先看setValue方法:
// LiveData.java:
......................................
static final int START_VERSION = -1;
private int mVersion = START_VERSION;
......................................
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
注意这里的mVersion,在setValue中进行了自增操作。另外,mVersion的值默认为-1。所以当我们第一次调用setValue时,LiveData中的mVersion的值等于0。
当调用LiveData进行observe时,我们在上面的源码也分析过,observe最终会走到considerNotify方法:
// LiveData.java:
private void considerNotify(ObserverWrapper observer) {
......................................
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
private abstract class ObserverWrapper {
......................................
int mLastVersion = START_VERSION;
......................................
}
可以看到,当observer的mLastVersion小于mVersion时就会把之前的数据回调给监听者。另外,observer中的mLastVersion默认值也为-1,所以在considerNotify中,很明显,mLastVersion小于mVersion(-1 < 0),所以会执行onChanged方法把数据回调给监听者。
如何解决数据倒灌
目前解决方案有非常多,这里只说一种,那就是通过反射干预Version值的方式。根据之前的分析,只需要在LiveData绑定owner的时候把Wrapper的version设置成跟LiveData的version一致即可。
网友评论