LiveDataBus实现原理#用法详解#LiveData扩展
LiveDataBus实现原理#LiveData源码分析
一、liveDataBus简单实现
public class LiveDataBus {
private final Map<String, MutableLiveData<Object>> mBus;
private LiveDataBus() {
mBus = new ArrayMap<>();
}
private static abstract class SingleHolder {
private static final LiveDataBus DATA_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingleHolder.DATA_BUS;
}
public <T> MutableLiveData<T> getChannel(String target, Class<T> type) {
if (!mBus.containsKey(target)) {
mBus.put(target, new MutableLiveData<>());
}
return (MutableLiveData<T>) mBus.get(target);
}
public MutableLiveData<Object> getChannel(String target) {
return getChannel(target, Object.class);
}
}
到这里我们其实已经实现了liveDataBus的基本功能,我们进行如下测试:
在第一个activity中调用:
LiveDataBus.get().getChannel("myfirst1").setValue("我给你发数据了,你收到了吗");
在第二个activity中注册并接受消息
LiveDataBus.get().getChannel("myfirst1", String.class).observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
Toast.makeText(SecondActivity.this, "我收到了蛋疼的消息" + s, Toast.LENGTH_SHORT).show();
}
});
这个时候你会发现在第二个activity打开时会出现toast,但是细心的同学会发现,我是在打开第二个activity之前发的消息,在第二个activity打开的时候订阅的,也就是说,我后订阅却收到了你之前发的消息,那如果你们之前发了几千条消息了,我刚刚订阅,难道要我先处理这几千条数据(这一点很像粘性广播的特性)? 显然,这不是我们想要的效果,我们要的结果是observer在被注册之后才能接收消息,而且接收到的消息也应该是在订阅之后发的消息。
二、liveData源码解析
那么为什么会出现这种情况呢?抱着疑惑我们翻看一下liveData的源码:
首先我们看到liveData持有了一个ObserverWrapper的集合,那么这个ObserverWrapper是什么东东呢?接着看:
我们看到ObserverWrapper其实是LiveData的一个内部类,他把观察者observer包裹了,并且记录了当前observer的状态(mActive是否活跃)、上一个版本(mLastVersion)。
我们再看一下我们调用的observe方法:
在这里我们看到了我们的observer被包裹成了LifecycleBoundObserver,他其实继承了ObserverWrapper。
接下来我们看看我们发送数据时做了些啥:
当我们调用setValue方法时,它会调用dispatchingValue方法完成:
接着调用了considerNotify(initiator):
如上图,我们看到当observer.mLastVersion >= mVersion时方法就直接return了,没有再执行onChanged()方法,所以我们可以猜想我们是不是可以通过控制这两个变量的值来实现我们想要的结果呢?于是乎我们先来捋捋这两个变量
那么mVersion怎么赋值的:
static final int START_VERSION = -1;
private int mVersion = START_VERSION;
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;//每次我们调用setValue时,mVersion加1
mData = value;
dispatchingValue(null);
}
那么mLastVersion 又是怎么赋值的呢:
我们看到mLastVersion每次都是赋值为START_VERSION,而这个常量我们在上面看到他的值是-1,而且在上图中我们可以看到在considerNotify方法中每次调用onChanged方法之前,都会把mVersion赋值给observer.mLastVersion。
好看完源码,我们思考一下,我们能不能再创建新的ObserverWrapper的时候,直接把mVersion的值赋给mLastVersion,这样就符合(observer.mLastVersion >= mVersion)这一条件了,就不会再继续执行onChanged方法了。
好的分析完了,那么怎么做呢,想必很多同学看到这里已经猜到了,那就是反射。
三、hook实现observer只接受订阅之后发送的事件:
/**
* author : geyuecang
* date : 2019/4/4 14:20
* desc : desc
*/
public class BusMutableLiveData<T> extends MutableLiveData<T> {
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, observer);
hook(observer);
}
private void hook(Observer<T> observer) {
Class<LiveData> liveDataClass = LiveData.class;
try {
//获取field private SafeIterableMap<Observer<T>, ObserverWrapper> mObservers
Field mObservers = liveDataClass.getDeclaredField("mObservers");
mObservers.setAccessible(true);
//获取SafeIterableMap集合mObservers
Object observers = mObservers.get(this);
Class<?> observersClass = observers.getClass();
//获取SafeIterableMap的get(Object obj)方法
Method methodGet = observersClass.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
//获取到observer在集合中对应的ObserverWrapper对象
Object objectWrapperEntry = methodGet.invoke(observers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("ObserverWrapper can not be null");
}
//获取ObserverWrapper的Class对象 LifecycleBoundObserver extends ObserverWrapper
Class<?> wrapperClass = objectWrapper.getClass().getSuperclass();
//获取ObserverWrapper的field mLastVersion
Field mLastVersion = wrapperClass.getDeclaredField("mLastVersion");
mLastVersion.setAccessible(true);
//获取liveData的field mVersion
Field mVersion = liveDataClass.getDeclaredField("mVersion");
mVersion.setAccessible(true);
Object mV = mVersion.get(this);
//把当前ListData的mVersion赋值给 ObserverWrapper的field mLastVersion
mLastVersion.set(objectWrapper, mV);
mObservers.setAccessible(false);
methodGet.setAccessible(false);
mLastVersion.setAccessible(false);
mVersion.setAccessible(false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后我们把LiveDataBus重的MutableLiveData替换成自己定义的BusMutableLiveData,Now,轻松加愉快,我们就实现了想要的效果。