Android消息总线的演进之路:⽤LiveDataBus替代RxBus、
EventBus
背景
对于Android系统来说,消息传递是最基本的组件,每⼀个App内的不同页⾯,不同组件都在进⾏消息传递。消息传递既可以⽤于Android四⼤组件之间的通信,也可⽤于异步线程和主线程之间的通信。对于Android开发者来说,经常使⽤的消息传递⽅式有很多种,从最早使⽤的Handler、BroadcastReceiver、接⼝回调,到近⼏年流⾏的通信总线类框架EventBus、RxBus。Android消息传递框架,总在不断的演进之中。
从EventBus说起
EventBus是⼀个Android事件发布/订阅框架,通过解耦发布者和订阅者简化Android事件传递。EventBus可以代替Android传统的Intent、Handler、Broadcast或接⼝回调,在Fragment、Activity、Service线程之间传递数据,执⾏⽅法。
EventBus最⼤的特点就是简洁、解耦。在没有EventBus之前我们通常⽤⼴播来实现监听,或者⾃定义接⼝函数回调,有的场景我们也可以直接⽤Intent携带简单数据,或者在线程之间通过Handler处理消息传
递。但⽆论是⼴播还是Handler机制远远不能满⾜我们⾼效的开发。EventBus简化了应⽤程序内各组件间、组件与后台线程间的通信。EventBus⼀经推出,便受到⼴⼤开发者的推崇。
现在看来,EventBus给Android开发者世界带来了⼀种新的框架和思想,就是消息的发布和订阅。这种思想在其后很多框架中都得到了应⽤。
图⽚摘⾃EventBus GitHub主页
发布/订阅模式
订阅发布模式定义了⼀种“⼀对多”的依赖关系,让多个订阅者对象同时监听某⼀个主题对象。这个主题对象在⾃⾝状态变化时,会通知所有订阅者对象,使它们能够⾃动更新⾃⼰的状态。
RxBus的出现
RxBus不是⼀个库,⽽是⼀个⽂件,实现只有短短30⾏代码。RxBus本⾝不需要过多分析,它的强⼤完全来⾃于它基于的RxJava技术。响应式编程(Reactive Programming)技术这⼏年特别⽕,RxJava是它在Java上的实作。RxJava天⽣就是发布/订阅模式,⽽且很容易处理线程切换。所以,RxBus凭借区区30⾏代码,就敢挑战EventBus“江湖⽼⼤”的地位。
RxBus原理
在RxJava中有个Subject类,它继承Observable类,同时实现了Observer接⼝,因此Subject可以同时担当订阅者和被订阅者的⾓⾊,我们使⽤Subject的⼦类PublishSubject来创建⼀个Subject对象(PublishSubject只有被订阅后才会把接收到的事件⽴刻发送给订阅者),在需要接收事件的地⽅,订阅该Subject对象,之后如果Subject对象接收到事件,则会发射给该订阅者,此时Subject对象充当被订阅者的⾓⾊。
完成了订阅,在需要发送事件的地⽅将事件发送给之前被订阅的Subject对象,则此时Subject对象作为订阅者接收事件,然后会⽴刻将事件转发给订阅该Subject对象的订阅者,以便订阅者处理相应事件,到这⾥就完成了事件的发送与处理。
最后就是取消订阅的操作了,RxJava中,订阅操作会返回⼀个Subscription对象,以便在合适的时机取消订阅,防⽌内存泄漏,如果⼀个类产⽣多个Subscription对象,我们可以⽤⼀个CompositeSubscription存储起来,以进⾏批量的取消订阅。
RxBus有很多实现,如:
AndroidKnife/RxBus()
Blankj/RxBus()
其实正如前⾯所说的,RxBus的原理是如此简单,我们⾃⼰都可以写出⼀个RxBus的实现:
基于RxJava1的RxBus实现:
public final class RxBus {
private final Subject<Object, Object> bus;
private RxBus() {
bus = new SerializedSubject<>(ate());
}
private static class SingletonHolder {
private static final RxBus defaultRxBus = new RxBus();
}
public static RxBus getInstance() {
return SingletonHolder.defaultRxBus;
}
/*
* 发送
*/
public void post(Object o) {
}
/*
* 是否有Observable订阅
*/
public boolean hasObservable() {
return bus.hasObservers();
}
/*
* 转换为特定类型的Obserbale
*/
public <T> Observable<T> toObservable(Class<T> type) {
return bus.ofType(type);
}
}
基于RxJava2的RxBus实现:
public final class RxBus2 {
private final Subject<Object> bus;
private RxBus2() {
// toSerialized method made bus thread safe
bus = ate().toSerialized();
}
public static RxBus2 getInstance() {
return Holder.BUS;
}
private static class Holder {
private static final RxBus2 BUS = new RxBus2();
}
public void post(Object obj) {
}
public <T> Observable<T> toObservable(Class<T> tClass) {
return bus.ofType(tClass);
}
public Observable<Object> toObservable() {
return bus;
}
public boolean hasObservers() {
return bus.hasObservers();
}
}
引⼊LiveDataBus的想法
从LiveData谈起
LiveData是Android Architecture Components提出的框架。LiveData是⼀个可以被观察的数据持有类,它可以感知并遵循Activity、Fragment或Service等组件的⽣命周期。正是由于LiveData对组件⽣命周期可感知特点,因此可以做到仅在组件处于⽣命周期的激活状态时才更新UI数据。
LiveData需要⼀个观察者对象,⼀般是Observer类的具体实现。当观察者的⽣命周期处于STARTED或RESUMED状态时,LiveData会通知观察者数据变化;在观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。
LiveData的优点
UI和实时数据保持⼀致,因为LiveData采⽤的是观察者模式,这样⼀来就可以在数据发⽣改变时获得通知,更新UI。
避免内存泄漏,观察者被绑定到组件的⽣命周期上,当被绑定的组件销毁(destroy)时,观察者会⽴刻⾃动清理⾃⾝的数据。
不会再产⽣由于Activity处于stop状态⽽引起的崩溃,例如:当Activity处于后台状态时,是不会收到LiveData的任何事件的。
不需要再解决⽣命周期带来的问题,LiveData可以感知被绑定的组件的⽣命周期,只有在活跃状态才会通知数据变化。
实时数据刷新,当组件处于活跃状态或者从不活跃状态到活跃状态时总是能收到最新的数据。
解决Configuration Change问题,在屏幕发⽣旋转或者被回收再次启动,⽴刻就能收到最新的数据。
谈⼀谈Android Architecture Components
Android Architecture Components的核⼼是Lifecycle、LiveData、ViewModel 以及 Room,通过它可以⾮常优雅的让数据与界⾯进⾏交互,并做⼀些持久化的操作,⾼度解耦,⾃动管理⽣命周期,⽽且不⽤担⼼内存泄漏的问题。
Room ⼀个强⼤的SQLite对象映射库。
ViewModel⼀类对象,它⽤于为UI组件提供数据,在设备配置发⽣变更时依旧可以存活。
LiveData ⼀个可感知⽣命周期、可被观察的数据容器,它可以存储数据,还会在数据发⽣改变时进⾏提醒。
Lifecycle包含LifeCycleOwer和LifecycleObserver,分别是⽣命周期所有者和⽣命周期感知者。
Android Architecture Components的特点
数据驱动型编程变化的永远是数据,界⾯⽆需更改。
感知⽣命周期,防⽌内存泄漏
⾼度解耦数据,界⾯⾼度分离。
数据持久化数据、ViewModel不与 UI的⽣命周期挂钩,不会因为界⾯的重建⽽销毁。
重点:为什么使⽤LiveData构建数据通信总线LiveDataBus
使⽤LiveData的理由
LiveData具有的这种可观察性和⽣命周期感知的能⼒,使其⾮常适合作为Android通信总线的基础构件。
使⽤者不⽤显⽰调⽤反注册⽅法。由于LiveData具有⽣命周期感知能⼒,所以LiveDataBus只需要调⽤注册回调⽅法,⽽不需要显⽰的调⽤反注册⽅法。这样带来的好处不仅可以编写更少的代码,⽽且可以完全杜绝其他通信总线类框架(如EventBus、RxBus)忘记调⽤反注册所带来的内存泄漏的风险。
为什么要⽤LiveDataBus替代EventBus和RxBus
LiveDataBus的实现及其简单,相对EventBus复杂的实现,LiveDataBus只需要⼀个类就可以实现。
LiveDataBus可以减⼩APK包的⼤⼩,由于LiveDataBus只依赖Android官⽅Android Architecture Components组件的LiveData,没有其他依赖,本⾝实现只有⼀个类。作为⽐较,EventBus JAR包⼤⼩为57kb,RxBus依赖RxJava和RxAndroid,其中RxJava2包⼤⼩
2.2MB,RxJava1包⼤⼩1.1MB,RxAndroid包⼤⼩9kb。使⽤LiveDataBus可以⼤⼤减⼩APK包的⼤⼩。
LiveDataBus依赖⽅⽀持更好,LiveDataBus只依赖Android官⽅Android Architecture Components组件的LiveData,相⽐RxBus依赖的RxJava和RxAndroid,依赖⽅⽀持更好。
LiveDataBus具有⽣命周期感知,LiveDataBus具有⽣命周期感知,在Android系统中使⽤调⽤者不需要调⽤反注册,相⽐EventBus和RxBus使⽤更为⽅便,并且没有内存泄漏风险。
LiveDataBus的设计和架构
LiveDataBus的组成
消息消息可以是任何的Object,可以定义不同类型的消息,如Boolean、String。也可以定义⾃定义类型的消息。
消息通道LiveData扮演了消息通道的⾓⾊,不同的消息通道⽤不同的名字区分,名字是String类型的,可以通过名字获取到⼀个LiveData消息通道。
消息总线消息总线通过单例实现,不同的消息通道存放在⼀个HashMap中。
订阅订阅者通过getChannel获取消息通道,然后调⽤observe订阅这个通道的消息。
发布发布者通过getChannel获取消息通道,然后调⽤setValue或者postValue发布消息。
LiveDataBus原理图
LiveDataBus的实现
第⼀个实现:
public final class LiveDataBus {
private final Map<String, MutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DATA_BUS = new LiveDataBus();
android最新版}
public static LiveDataBus get() {
return SingletonHolder.DATA_BUS;
}
public <T> MutableLiveData<T> getChannel(String target, Class<T> type) {
if (!ainsKey(target)) {
bus.put(target, new MutableLiveData<>());
}
return (MutableLiveData<T>) (target);
}
public MutableLiveData<Object> getChannel(String target) {
return getChannel(target, Object.class);
}
}
短短⼆⼗⾏代码,就实现了⼀个通信总线的全部功能,并且还具有⽣命周期感知功能,并且使⽤起来也及其简单:
注册订阅:
<().getChannel("key_test", Boolean.class)
.observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean aBoolean) {
}
});
发送消息:
<().getChannel("key_test").setValue(true);
我们发送了⼀个名为"key_test",值为true的事件。
这个时候订阅者就会收到消息,并作相应的处理,⾮常简单。
问题出现
对于LiveDataBus的第⼀版实现,我们发现,在使⽤这个LiveDataBus的过程中,订阅者会收到订阅之前发布的消息。对于⼀个消息总线来说,这是不可接受的。⽆论EventBus或者RxBus,订阅⽅都不会收到订阅之前发出的消息。对于⼀个消息总线,LiveDataBus必须要解决这个问题。
问题分析
怎么解决这个问题呢?先分析下原因:
当LifeCircleOwner的状态发⽣变化的时候,会调⽤LiveData.ObserverWrapper的activeStateChanged函数,如果这个时候ObserverWrapper 的状态是active,就会调⽤LiveData的dispatchingValue。
在LiveData的dispatchingValue中,⼜会调⽤LiveData的considerNotify⽅法。
在LiveData的considerNotify⽅法中,红框中的逻辑是关键,如果ObserverWrapper的mLastVersion⼩
于LiveData的mVersion,就会去回调mObserver的onChanged⽅法。⽽每个新的订阅者,其version都是-1,LiveData⼀旦设置过其version是⼤于-1的(每次LiveData设置值都会使其version加1),这样就会导致LiveDataBus每注册⼀个新的订阅者,这个订阅者⽴刻会收到⼀个回调,即使这个设置的动作发⽣在订阅之前。
问题原因总结
对于这个问题,总结⼀下发⽣的核⼼原因。对于LiveData,其初始的version是-1,当我们调⽤了其setValue或者postValue,其vesion会
+1;对于每⼀个观察者的封装ObserverWrapper,其初始version也为-1,也就是说,每⼀个新注册的观察者,其version为-1;当LiveData设置这个ObserverWrapper的时候,如果LiveData的version⼤于ObserverWrapper的version,LiveData就会强制把当前value推送给Observer。
如何解决这个问题
明⽩了问题产⽣的原因之后,我们来看看怎么才能解决这个问题。很显然,根据之前的分析,只需要在注册⼀个新的订阅者的时候把Wrapper的version设置成跟LiveData的version⼀致即可。
那么怎么实现呢,看看LiveData的observe⽅法,他会在步骤1创建⼀个LifecycleBoundObserver,Life
cycleBoundObserver是ObserverWrapper的派⽣类。然后会在步骤2把这个LifecycleBoundObserver放⼊⼀个私有Map容器mObservers中。⽆论ObserverWrapper 还是LifecycleBoundObserver都是私有的或者包可见的,所以⽆法通过继承的⽅式更改LifecycleBoundObserver的version。
那么能不能从Map容器mObservers中取到LifecycleBoundObserver,然后再更改version呢?答案是肯定的,通过查看SafeIterableMap的源码我们发现有⼀个protected的get⽅法。因此,在调⽤observe的时候,我们可以通过反射拿到LifecycleBoundObserver,再把LifecycleBoundObserver的version设置成和LiveData⼀致即可。
对于⾮⽣命周期感知的observeForever⽅法来说,实现的思路是⼀致的,但是具体的实现略有不同。observeForever的时候,⽣成的wrapper不是LifecycleBoundObserver,⽽是AlwaysActiveObserver(步骤1),⽽且我们也没有机会在observeForever调⽤完成之后再去更改AlwaysActiveObserver的version,因为在observeForever⽅法体内,步骤3的语句,回调就发⽣了。
那么对于observeForever,如何解决这个问题呢?既然是在调⽤内回调的,那么我们可以写⼀个ObserverWrapper,把真正的回调给包装起来。把ObserverWrapper传给observeForever,那么在回调的时候我们去检查调⽤栈,如果回调是observeForever⽅法引起的,那么就不回调真正的订阅者。
LiveDataBus最终实现
public final class LiveDataBus {
private final Map<String, BusMutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DEFAULT_BUS;
}
public <T> MutableLiveData<T> with(String key, Class<T> type) {
if (!ainsKey(key)) {
bus.put(key, new BusMutableLiveData<>());
}
return (MutableLiveData<T>) (key);
}
public MutableLiveData<Object> with(String key) {
return with(key, Object.class);
}
private static class ObserverWrapper<T> implements Observer<T> {
private Observer<T> observer;
public ObserverWrapper(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onChanged(@Nullable T t) {
if (observer != null) {
if (isCallOnObserve()) {
return;
}
}
}
private boolean isCallOnObserve() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (stackTrace != null && stackTrace.length > 0) {
for (StackTraceElement element : stackTrace) {
if ("android.arch.lifecycle.LiveData".ClassName()) &&
"observeForever".MethodName())) {
return true;
}
}
}
return false;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论