eventBus3简单示例
首先添加依赖, compile ‘org.greenrobot:eventbus:3.0.0’
然后要先定义一个event。
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
第二步,在要订阅事件的类中,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent messageEvent) {
((TextView)findViewById(R.id.textView)).setText(messageEvent.getMessage());
}
因为eventbus 3中采用了注解实现,所以我们接受event的方法onMessage方法命名可以任意命名,什么getMessage,mainThreadMessage都可以,eventbus只是根据注解和参数MessageEvent来调用。
第三步,在发送消息的地方直接
EventBus.getDefault().post(new MessageEvent("I am poster"));
完整demo代码
public class SubscriberAcitivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((Button)findViewById(R.id.jump)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SubscriberAcitivity.this, PosterActivity.class));
}
});
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void getMessage(MessageEvent messageEvent) {
((TextView)findViewById(R.id.textView)).setText(messageEvent.getMessage());
}
}
public class PosterActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_poster);
((Button)findViewById(R.id.post)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent("I am poster"));
}
});
}
}
效果就是在posterActivity中发的消息,被subscriberActivity接收到并依次更改了自己的UI。从这里可以一眼看出EventBus最大的好处,一是解耦,消息接受者和发送者没有任何关系,二是相对于传统的activity之间的通信机制:handler接收消息异步更改UI,eventBus为我们进行了封装简化了编写过程,当然在eventBus的内部也是通过handler实现的。
java的注解
先来看一下我们在subscriber中添加的注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
首先注解的定义方法十分简单,类似接口public @interface Subscribe{},就定义了@Subscribe这个注解。然后在上面添加上元注解,元注解就是我们定义的这个注解的注解,描述了我们自定义注解的基本信息。可以看到上面有3个元注解
@Documented是用javadoc生成文档相关的
@Retention定义了该注解被保留的时间长短,Runtime的意思当然就是程序运行时也有效。
@Target则是注解针对什么使用,@Target({ElementType.METHOD})表示这个注解是使用与方法的。
然后就是注解中的成员变量,ThreadMode 是一个枚举,在为注解设置成员时,基本语法就是
类型 成员名称() default 默认值
在使用时,直接@Subscribe(threadMode = ,sticky = ,priority = )即可,不给即为默认值。
java中的反射
定义完注解,那么该如何使用呢,这个时候java的反射就派上用场了。
反射学术化的定义很多,但在这里我们的应用却很简单。
首先,在jvm类加载器加载类时会为每个类生成一个Class对象并放在方法区中,也就是说对于一个类,我们可能没有通过new 实例化出它的对象,但是我们可以拿到它的Class对象。简单来讲,程序一但运行,我们可以拿到每个类的全部信息,就是Class。
既然说了是全部信息,那么在Class中就会包含该类的全部方法,然后对于每一个方法又可以拿到该方法的全部注解或者指定注解。见下面的代码:
Class<?> clazz = SubscriberAcitivity.class;
for (Method method : clazz.getDeclaredMethods()){
//拿到指定注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//通过注解,获取当时我们设定的几个值
subscribeAnnotation.threadMode();
subscribeAnnotation.sticky()
subscribeAnnotation.priority();
}
};
也就是说,通过注解我们可以利用反射找到某个方法,然后注解中可以存放一些我们后面可能用到的属性。
最后利用反射调用method.invoke(clazz, new MessageEvent(“I am poster”)),表明我们的SubscriberActivity调用了@Subscribe注解标识的方法,给入的参数是MessageEvent。
可以看到,通过java反射,我们定义了方法,定义了类,但是对于类的实例化,甚至对于方法的调用,都不需要我们直接去做。也就是避免了new Object().method()这样的写法,将方法定义者和方法调用者松耦合。
event bus 源码分析
介绍完前面的基础,下面来分析一下eventBus的源码。
首先eventBus是一个典型的观察者模式,所以我们也就分观察者和被观察者两部分分析:
观察者 register
首先从入口进入,EventBus.getDefault().register(this);,把当前消息的订阅者注册到eventbus
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
典型的DCL单例模式实现,获取EventBus实例后register。构造方法中调用真正构造方法,这里使用到里Builder模式,在builder中可以设置是否打印异常日志、发送异常事件、订阅者的父类是否可以接收event,是否使用线程池以,及诸如Subscriber Index的新特性的设置。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
利用反射获取订阅者的Class对象。然后findSubscriberMethods中找遍历订阅者的所有方法,并找其中仅有一个参数,并且有注解@Subscribe的方法。通过注解获取ThreadMode等信息后封装进SubscriberMethod中,然后将订阅方法添加到并保存下来,最后存在一个哈希表 METHOD_CACHE中,表的键就是订阅者的Class对象,值就是接收事件的方法。然后将上面找到的方法遍历,并调用subscribe方法。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//依据event类型,获取针对一种event的全部订阅subscription
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//不存在初始化,存在则检查新的订阅是否存在
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
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;
}
}
//检查该 subscriber 已有的event事件类型并存储。
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
...
//关于订阅方法设置sticky的一些额外处理。
}
}
可以看到,通过subscribe方法,将订阅者和接收不同事件的方法封装到了一起,然后根据event类型分类存放,可猜想这样可以根据event来通过Map将event发送给相关subscriber处理,同时还将subscriber所订阅的全部event存储了起来。
subscriber发布订阅已经完成,作为观察者模式中的观察者,现在即等待被观察者发出消息,接收并执行逻辑。
被观察者 post
被观察者发送event,观察者看到event后执行逻辑。
eventBus中通过post方法发布一个事件,也就是我们的自定义实体类XXXEvent。
public void post(Object event) {
//通过ThreadLocal为每个线程维护一个变量副本,记录post线程的线程状态
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
//判断是否是UI线程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
post方法获取当前发起post线程的队列,并将event加入到队里中,然后将队列中所有事件拿出执行postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
...
//如果事件可以被父类继承执行相关逻辑。
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
...
//没有找到订阅subscription的一些补救措施
}
}
在postSingleEventForEventType方法中,首先依据event,去我们前面说到的map中找订阅subscription,找到相关的subscription后在方法postToSubscription执行的是真正的调用。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
逻辑很清晰,首先拿出该订阅subscription的ThreadMode,来确定订阅者接收event后逻辑应该执行在哪个线程。如果是POSTING,则直接在post事件的线程中执行。执行这个postToSubscription的当然也就是post线程了,所以直接调用订阅者。调用的方式也十分简单,直接通过java反射机制,Method.invoke方法给入调用者和event参数即可。
如果不是当前线程,则加到UI线程、后台线程、异步线程对应的Poster的队列中去。下面分情况分析:
MAIN
先判断当前线程是否为UI线程,如果是就直接invoke。否则给到HandlerPoster的队列中,HandlerPoster实际是一个handler,并且在构造方法中将UI线程的Looper绑定到了这个handler中,这个HandlerPoster也就相当于执行在UI线程中。分析其enqueue方法
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
PendingPost中封装的是订阅信息和当前发送出来的event,订阅信息中包含subscriber、以及处理相关event的method,handlerActive表示当前handler是否繁忙,不忙就让它处理消息,也就是自己给自己发送一个空消息,这样可以通过handler的机制,保证逻辑是执行在UI线程的。handler的handleMessage就是从这个队列中取出全部pendingPost然后通过反射调用了,当然这里还加上了一个时间判断,如果方法执行时间过长,重新给自己发送一个消息,然后结束自己。这样做也就是给UI线程喘气的时间,让它可以绘制界面,通过UI线程自己的消息机制,在不是那么忙的时候继续拿pendingPost,反射调用。当然,在调用subscriber的时候,还要判断其是否处于active状态,也就是说如果unrigster了的subscriber不接收事件。
BACKGROUND
逻辑类似MAIN,主要确保处理event的逻辑执行在后台线程,也就是 不是UI线程。
BackgroundPoster是一个Runnable,其中封装了队列,Runnable的执行交给EventBus中定义的全局线程池。执行enqueue时先是同样将信息封装进PendingPost,然后在线程池不繁忙时,执行该Runnable。
线程做的事情就是从队列中拿出任务执行,执行完了结束其线程本身,没执行完在该线程中一直执行,通过反射进行类似的相关调用。
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
ASYNC
异步实现,立即通过线程池执行处理当前PendingPost,与background不同的是,background中的一个线程,作为后台线程会处理很多Post事件,而Async仅处理一个PendingPost。Background是和Main相对的概念,而ASYNC则是执行单独任务的轻量级的线程。BACKGROUND线程全局只有一个,而ASYNC可以有很多。
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
总结
EventBus就是一个总线,订阅者注册到这里,并把自己的方法存储进去。事件发送者发送event,去eventBus中寻找订阅者的信息及方法,然后通过eventBus选择执行的方式,进行方法调用。
至此,源码部分整体逻辑分析完成,当然还要很多其他特性其他设置,需要进一步研究。