您的位置:首页 > 移动开发 > Android开发

欢迎使用CSDN-markdown编辑器

2016-06-01 15:07 531 查看

EventBus3.0详解

android EventBus

EventBus30详解
写在前面
前言

什么是EventBus

相似产品比较

使用EventBus30三部曲
定义事件

准备订阅者

发送事件

深入了解
ThreadMode线程通信
ThreadModePOSTING

ThreadModeMAIN

ThreadModeBACKGROUND

ThreadModeASYNC

配置EventBusBuilder

StickyEvent

priority事件优先级

中止事件传递

index索引加速

NoSubscriberEvent

混淆

利弊

使用建议
1EventBus管理

2以事件为对象

参考文献

说了这么多废话下面进入正题

写在前面

1.前言

曾经,一层又一层的业务逻辑让我不知所措,一个又一个的回调让你头晕眼花,一个又一个的参数让你混乱不堪。EventBus,,一个耦合度低到令你害怕的框架。

2.什么是EventBus

EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件、线程通信,可以轻易切换线程、开辟线程。EventBus3.0跟先前版本的区别在于加入了annotation @Subscribe,取代了以前约定命名的方式。

3.相似产品比较

产品名开发者备注
EventBusgreenrobot用户最多,简洁,方便,小巧,文档简洁明了
Guavagoogle一个庞大的工具类库,EventBus只是一个小功能
ottosquarefork guava ,用的人不少
AndroidEventBus何红辉模仿EventBus开发的

使用EventBus3.0三部曲

1.定义事件

public class MessageEvent {
public final String message;

public MessageEvent(String message) {
this.message = message;
}
}


2.准备订阅者

// This method will be called when a MessageEvent is posted
@Subscribe
public void onMessageEvent(MessageEvent event){
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event){
doSomethingWith(event);
}


@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}

@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}


3.发送事件

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));


深入了解

1.ThreadMode线程通信

EventBus可以很简单的实现线程间的切换,包括后台线程、UI线程、异步线程

ThreadMode.POSTING

//默认调用方式,在调用post方法的线程执行,避免了线程切换,性能开销最少
// Called in the same thread (default)
@Subscribe(threadMode = ThreadMode.POSTING) // ThreadMode is optional here
public void onMessage(MessageEvent event) {
log(event.message);
}


ThreadMode.MAIN

// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}


ThreadMode.BACKGROUND

// 如果调用post方法的线程不是主线程,则直接在该线程执行
// 如果是主线程,则切换到后台单例线程,多个方法公用同个后台线程,按顺序执行,避免耗时操作
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
saveToDisk(event.message);
}


ThreadMode.ASYNC

//开辟新独立线程,用来执行耗时操作,例如网络访问
//EventBus内部使用了线程池,但是要尽量避免大量长时间运行的异步线程,限制并发线程数量
//可以通过EventBusBuilder修改,默认使用Executors.newCachedThreadPool()
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
backend.send(event.message);
}


2.配置EventBusBuilder

EventBus提供了很多配置,一般的情况下我们可以不用配置.但是,如果你有一些其他要求,比如控制日志在开发的时候输出,发布的时候不输出,在开发的时候错误崩溃,而发布的时候不崩溃…等情况。

EventBus提供了一个默认的实现,但不是单例。

EventBus eventBus = new EventBus();
//下面这一条的效果是完全一样的
EventBus eventBus = EventBus.builder().build();
//修改默认实现的配置,记住,必须在第一次EventBus.getDefault()之前配置,且只能设置一次。建议在application.onCreate()调用
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus();


3.StickyEvent

StickyEvent在内存中保存最新的消息,取消原有消息,执行最新消息,只有在注册后才会执行,如果没有注册,消息会一直保留来内存中

//在注册之前发送消息
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));


//限制,新界面启动了
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
//在onStart调用register后,执行消息
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {
// UI updates must run on MainThread
textField.setText(event.message);
}

@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}


你也可以手动管理StickyEvent

MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(stickyEvent);
//or
EventBus.getDefault().removeAllStickyEvents();
// Now do something with it
}


在这里,或许你会有个疑问,

StickyEvent=true的订阅者能否接收post的事件?

StickyEvent=false的订阅者能否接收postSticky的事件?

查看源码发现

/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}


private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
...省略部分代码
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}


boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}


发现,post方法没有过滤StickyEvent,而postSticky是调用post方法的,所以,无论post还是postSticky,StickyEvent是true或false,都会执行

4.priority事件优先级

//priority越大,级别越高
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
…
}


//优先级实现方式,遍历当前列表,把当前
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}


5.中止事件传递

// 中止事件传递,后续事件不在调用,注意,只能在传递事件的时候调用
@Subscribe
public void onEvent(MessageEvent event){
…
EventBus.getDefault().cancelEventDelivery(event) ;
}


6.index索引加速

EventBus使用了annotation,默认在编译时生成代码,生成索引,

添加index后会在编译时运行,自动生成相应代码。

ps:由于apt的限制,匿名内部类中的annotation不会被识别,会自动降级在运行时反射,此时,效率会降低

buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
eventBusIndex "com.example.myapp.MyEventBusIndex"
}
}


EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();


EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();


7.NoSubscriberEvent

如果没找到订阅者事件,可以通过EventBusBuilder设置是否默认发送NoSubscriberEvent,默认是打开的

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
....
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}


8.混淆

-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}


9.利弊

好处

简单,方便,小巧,文档清晰,性能消耗少,可定制行强,耦合度低

坏处

耦合度太低

这绝对不是个笑话,,EventBus的耦合太低了,如果不加以控制管理,你会不知道,你发的消息到跑哪里去了。也不知道你的这条消息,会在哪里发出。如果你没有很好的方法解决这个问题,建议不好用太多。

使用建议

1、EventBus管理

EventBus运行创建多个,那么,明确事件的生命周期,根据不同生命周期使用不同的EventBus?

/**
* 方法1
* 用annotation配合使用工厂
* EventBusFactory.getBus(EventBusFactory.START);
* EventBusFactory.getBus();
*/
public class EventBusFactory {
private static SparseArray<EventBus> mBusSparseArray = new SparseArray<>(2);

@IntDef({CREATE, START})
@Retention(RetentionPolicy.SOURCE)
public @interface BusType {
}

public static final int CREATE = 0;
public static final int START = 1;

static {
mBusSparseArray.put(CREATE, EventBus.builder().build());
mBusSparseArray.put(START, EventBus.getDefault());
}

public static EventBus getBus() {
return getBus(START);
}

public static EventBus getBus(@BusType int type) {
return mBusSparseArray.get(type);
}

}


/**
* 方法2
* 用枚举工厂
* EventBusFactory.START.getBus();
*/
public enum EventBusFactory {
CREATE(0),
START(1);

private int mType;

EventBusFactory(int type) {
mType = type;
}

public EventBus getBus() {
return mBusSparseArray.get(mType);
}

private static SparseArray<EventBus> mBusSparseArray = new SparseArray<>(2);

static {
mBusSparseArray.put(CREATE.mType, EventBus.builder().build());
mBusSparseArray.put(START.mType, EventBus.getDefault());
}
}


2、以事件为对象

将数据封装到一个事件类。所有事件放到一个包下。如果事件太多,同个模块的事件可以考虑使用静态内部类,或者再分包。

/**
* This Event is posted by EventBus when no subscriber is found for a posted event.
*
* @author Markus
*/
public final class NoSubscriberEvent {
/** The {@link EventBus} instance to with the original event was posted to. */
public final EventBus eventBus;

/** The original event that could not be delivered to any subscriber. */
public final Object originalEvent;

public NoSubscriberEvent(EventBus eventBus, Object originalEvent) {
this.eventBus = eventBus;
this.originalEvent = originalEvent;
}

}


public class Event  {
public static class UserListEvent {
public List<User> users ;
}
public static class ItemListEvent {
public List<Item> items;
}
}


注意,不是相同类型就一定要作为一个事件封装,具体需要考虑业务情景跟代码情况,比如事件行为不同、事件生命周期不同,如果有必要,写封装成两个Event可能是更好的选择。

public class Event  {
public static class UserListUpdateEventOnCreate {
public List<User> users;
}
public static class UserListUpdateEventOnStart {
public List<User> users ;
}
public static class UserListRemoveEventOnStart {
public List<User> users;
}
}


参考文献

EventBus官网地址

EventBus github地址

EventBus 3.0的用法详解

说了这么多废话,,下面进入正题

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android EventBus