Android -- 从源码带你从EventBus2.0飚到EventBus3.0(一)
1,最近看了不少的面试题,不管是百度、网易、阿里的面试题,都会问到EventBus源码和RxJava源码,而自己只是在项目中使用过,却没有去用心的了解它底层是怎么实现的,所以今天就和大家一起来学习学习
2,简介EventBus2.x
- EventBus是用来干什么的?
EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息。并且代码的可读性更好,耦合度更低。当你了解了之后其实和我们的观察者模式很像有没有,如果没了解观察值模式的话可以在我以前写的这篇好好的了解一下。
- 简单方法介绍
基本上就是下面这四个方法,让我们来一起简单的使用一下吧
EventBus的简单实用就是下面的四个方法 eventBus.register(this); //添加绑定 eventBus.post(Event event); //发送消息给绑定者 onEventMainThread(Event event);// 绑定者收到消息的接口回调 eventBus.unregister(this); //解除绑定
- 简单使用
在gradle中添加依赖,很简单,就是一句话
compile 'de.greenrobot:eventbus:2.4.0'
在MainActivity中注册
public class MainActivity extends AppCompatActivity {
private Button btn_skip;
private TextView tv_message; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
btn_skip = (Button) findViewById(R.id.btn_skip); tv_message = (TextView) findViewById(R.id.tv_message); btn_skip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
} public void onEventMainThread(FirstEvent event) {
tv_message.setText("收到的消息是:" + event.getMsg());
Log.i("wangjitao", "onEventMainThread:" + Thread.currentThread().getName());
} @Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
这里要注意以下我们EventBus2.x中接收消息的方法必须要以onEvent开头,并且固定了四个方法PostThread,MainThread,BackgroundThread,Async,这个我们下面会详细的和大家介绍的,这里大家留心一下。
创建SecondActivity,发送事件
package com.qianmo.eventbustest; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast; import com.qianmo.eventbustest.event.FirstEvent;
import de.greenrobot.event.EventBus;
import static android.icu.lang.UCharacter.GraphemeClusterBreak.T;
import static com.qianmo.eventbustest.R.id.tv_message; public class SecondActivity extends AppCompatActivity {
private Button btn_send; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second); btn_send = (Button) findViewById(R.id.btn_send); btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//发送消息
EventBus.getDefault().post(new FirstEvent("傻逼啊你!"));
}
});
}
}
看一下打印的结果
04-11 02:08:46.333 6155-6155/com.qianmo.eventbustest I/wangjitao: onEventPostThread:main
ok,没问题,我们继续往下了解
- EventBus的ThreadMode(线程模式)
在EventBus的事件处理函数中需要指定线程模型,而EventBus中一共四中就是我们上面说的PostThread(默认),MainThread,BackgroundThread,Async,相信一般我们的开发中只是用了MainThread线程模式吧,那么这四种线程模式到底有什么区别呢?
PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。
ok,上面都是一堆的理论总结点,而实际中我们开发者是想要代码展示的效果,ok,我们来试试,没毛病。
我们在MainActivity中添加这对应的四个线程模式的方法,并打印其所在的线程,然后在发送也在SecondActivity中打印一下发送线程,关键代码如下:
package com.qianmo.eventbustest; import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast; import com.qianmo.eventbustest.event.FirstEvent; import de.greenrobot.event.EventBus; public class MainActivity extends AppCompatActivity {
private Button btn_skip;
private TextView tv_message; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); EventBus.getDefault().register(this);
btn_skip = (Button) findViewById(R.id.btn_skip); tv_message = (TextView) findViewById(R.id.tv_message); btn_skip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
} public void onEventMainThread(FirstEvent event) {
tv_message.setText("收到的消息是:" + event.getMsg());
Log.i("wangjitao", "onEventMainThread:" + Thread.currentThread().getName());
} public void onEventBackgroundThread(FirstEvent event) {
Log.i("wangjitao", "onEventBackgroundThread:" + Thread.currentThread().getName());
} public void onEventAsync(FirstEvent event) {
Log.i("wangjitao", "onEventAsync:" + Thread.currentThread().getName());
} public void onEventPostThread(FirstEvent event) {
Log.i("wangjitao", "onEventPostThread:" + Thread.currentThread().getName()); } @Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
SecondActivity添加日志
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//发送消息
Log.i("wangjitao", "发送事件"+Thread.currentThread().getName());
EventBus.getDefault().post(new FirstEvent("傻逼啊你!"));
}
});
ok,我们开始,但是我们点击运行工程之后就直接崩溃了!!!! 崩溃日志如下:
“de.greenrobot.event.EventBusException: Illegal onEvent method, check for typos: public void so.cym.eventbus.MainActivity.onEventPostThread(org.json.JSONObject)”
??? 一脸懵逼 ,然后就各种百度,基本上很少提到这个问题,还有不少的大牛的博客都没提到这个问题,终于找到一个同仁说也遇到了这个问题,说是“同一个activity中不能同时有onEventMainThread和onEventPostThread两个函数里对JSONObject事件进行接收”,ok,我将接收方法修改成只有一个onEventPostThread()方法,没错 ,还是在报错,西坝,对于网上的有些答案还是很无语的。
突然灵机一动,上面不是说所有的方法都是以onEvent方法开头的嘛,而且我们onEventPostThread不是号称是默认的接收方法嘛,ok,那现在我将onEventPostThread方法名修改成onEvent来试试,没问题!!!竟然蒙对了(这里大家留心一下,过一下我回带大家从源码来来说明问什么onEventPostThread方法名修改成onEvent就可以了)!,ok,看一下我们的打印信息吧
04-11 02:08:46.332 6155-6155/com.qianmo.eventbustest I/wangjitao: 发送事件main
04-11 02:08:46.333 6155-6155/com.qianmo.eventbustest I/wangjitao: onEventPostThread:main
04-11 02:08:46.335 6155-7354/com.qianmo.eventbustest I/wangjitao: onEventBackgroundThread:pool-1-thread-2
04-11 02:08:46.335 6155-6155/com.qianmo.eventbustest I/wangjitao: onEventMainThread:main
04-11 02:08:46.335 6155-7353/com.qianmo.eventbustest I/wangjitao: onEventAsync:pool-1-thread-1
从日志打印结果可以看出,如果在UI线程中发布事件,则线程模型为PostThread的事件处理函数也执行在UI线程,与发布事件的线程一致。线程模型为Async的事件处理函数执行在名字叫做pool-1-thread-1的新的线程中。而MainThread的事件处理函数执行在UI线程,BackgroundThread的时间处理函数执行在名字叫做pool-1-thread-2的新的线程中。
继续,我们再看看在子线程中发布一条MessageEvent的消息时,会有什么样的结果。
package com.qianmo.eventbustest; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast; import com.qianmo.eventbustest.event.FirstEvent; import de.greenrobot.event.EventBus; import static android.icu.lang.UCharacter.GraphemeClusterBreak.T;
import static com.qianmo.eventbustest.R.id.tv_message; public class SecondActivity extends AppCompatActivity {
private Button btn_send; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second); btn_send = (Button) findViewById(R.id.btn_send); btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Log.i("wangjitao", "发送事件"+Thread.currentThread().getName());
EventBus.getDefault().post(new FirstEvent("傻逼啊你!"));
}
}).start();
//发送消息 }
});
}
}
打印效果如下:
04-11 02:17:41.113 14991-15049/com.qianmo.eventbustest I/wangjitao: 发送事件Thread-4
04-11 02:17:41.115 14991-15049/com.qianmo.eventbustest I/wangjitao: onEventPostThread:Thread-4
04-11 02:17:41.119 14991-15049/com.qianmo.eventbustest I/wangjitao: onEventBackgroundThread:Thread-4
04-11 02:17:41.119 14991-15050/com.qianmo.eventbustest I/wangjitao: onEventAsync:pool-1-thread-1
04-11 02:17:41.121 14991-14991/com.qianmo.eventbustest I/wangjitao: onEventMainThread:main
从日志打印结果可以看出,如果在子线程中发布事件,则线程模型为PostThread的事件处理函数也执行在子线程,与发布事件的线程一致(都是Thread-4)。BackgroundThread事件模型也与发布事件在同一线程执行。Async则在一个名叫pool-1-thread-1的新线程中执行。MainThread还是在UI线程中执行。
以上测试充分说明了不同线程模型的事件处理方法执行所在的线程。以上就是了解的基本方法了。
3,源码解析
我们上面基本上把所有的功能了解了,但是还遗留了一个问题就是为什么onEventPostThread方法不能用,而onEvent方法可以用,现在我们来从源码中来获取答案试试。源码中会用到大量的反射代码,不了解反射的同学可能看不懂,建议先看一下我以前写过的这篇反射文章,会有更好理解源码的。
- EventBus.getDefault()
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
EventBus.getDefault()其实就是个单例,还加了个线程的同步锁,这个问题也会被面试官问 “懒汉模式会存在线程安全问题吗?”
- EventBus.getDefault().register(this);
public void register(Object subscriber) {
register(subscriber, false, 0);
} public void register(Object subscriber, int priority) {
register(subscriber, false, priority);
}
public void registerSticky(Object subscriber) {
register(subscriber, true, 0);
} public void registerSticky(Object subscriber, int priority) {
register(subscriber, true, priority);
} private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
我们可以看到一共有5个注册相关的方法,这里sticky、priority这两个参数很熟悉有没有,和我们的广播中的有序广播、黏治广播的参数一模一样有没有,这样就很好理解了,有了这两个参数我们可以实现类似于广播的有序和黏治功能,上面四个方法最后都是调用我们三个参数的注册方法,让我们直接来看一下里面的方法吧
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
看一下方法里面findSubscriberMethods的代码吧
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {
subscriberMethods = methodCache.get(key);
}
if (subscriberMethods != null) {
return subscriberMethods;
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
} // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
代码有点长,不过我们直接来看核心代码
Method[] methods = clazz.getDeclaredMethods(); 这里通过反射机制,拿到我们注册类(这里注册类不好理解的话可以把它当成MainActivity.java)中所有方法的集合,如果有关反射的相关知识又不懂的话可以去看看以前我写过的这篇文章
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) ; 判断当前方法是不是以“onEvent”字符串开头
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) 判断方法是否为public并且是非static和abstract方法,所以这里我们可以在返回类型中随便填写void或者什么的
if (parameterTypes.length == 1); 判断方法里的参数是否为一个参数
下面这段代码就是判即使四个事件处理函数,这里我们可以看到当方法名是“onEvent”时我们的ThreadMode就是PostThread,这也就解决了我们上面的疑问了
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType)); subscriberMethods集合将中存放的SubscriberMethod,而SubscriberMethod类中有我们想要的三个重要的信息存放:方法对象、响应事件类型,事件参数类型
import java.lang.reflect.Method; final class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
/** Used for efficient comparison */
String methodString; SubscriberMethod(Method method, ThreadMode threadMode, Class<?> eventType) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
}
}
这样我们的代码findSubscriberMethods就看完了,我们继续看register的方法,从这里开始我开始在代码里面写注释了 ,博客园这点很蛋疼,编辑的时候没法确定代码的行数,都不好描述了
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
} // Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
//判断这个类是否是重复注册,例如,在activity中连续注册两次
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
//subscriptionsByEventType这个这个Map其实就是EventBus存储方法的地方, key值是我们的响应方法中的类型
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
//在这里抛出异常,有心思的同学可以连续注册两次试试,就会抛出这个异常
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
} // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true); int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
//我们知道之前priority这个参数是用来体现优先级的,而这里的作用就是按照优先级添加的。可以看到,优先级越高,会插到在当前List的前面。
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
} List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行。stickyEvent其实就是我们post时的参数。这里注意一下我们的postToSubscription后面还会接触到他的
if (sticky) {
Object stickyEvent;
synchronized (stickyEvents) {
stickyEvent = stickyEvents.get(eventType);
}
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
}
这时候register方法就讲完了,这里我们的大体的思路就是:扫描注册类中的所有方法(但注册类中的方法很多的时候这里是很耗费时间的,所以在3.0使用了注解的方式来找到相应的方法),将符合要求的方法存放在subscriptionsByEventType那个Map中,key值是响应方法的参数event类,value就是Subscription集合,而Subscription类中又包含subscriber(监听者)、priority(优先级)、SubscriberMethod(监听者的一些信息,method:监听者中的监听方法、threadMode 监听方法的线程模型,eventType 被监听的事件类型)
- EventBus.getDefault().post()
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event); if (!postingState.isPosting) {
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;
}
}
} final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
currentPostingThreadState是一个LocalThread类型,存放的是一个PostingThreadState类型,而我们的PostingThreadState类中存放了我们重要的一些信息例如:是否正在调用isPosting、当前线程是否为主线程isMainThread、保存时间的eventQueue等。
6-11行:判断当前线程是否为主线程、并判断当前实现是否正在post状态下
12-20行:遍历eventQuenu中所有的event事件,并调用postSingleEvent方法。
看下面的postSingleEvent方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
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));
}
}
} private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<Class<?>>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
} private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
第4行:判断该event是否有父类,如果有,遍历出所有的父类class添加eventTypes集合中,具体的代码请看25-40行
第6-13行:其实最终还是调用的postSingleEventForEventType这个方法,只不过如果有event有父类的话,连父类都调用postSingleEventForEventType方法,让我们直接看一下postSingleEventForEventType方法
第45行:看到了没,这里我们直接subscriptionsByEventType中拿Subscription的集合,subscriptionsByEventType这么map集合有没有很眼熟,没错,这就是我们register中放置事件响应方法总集合!!!,现在终于有点了头绪了吧,我猜他们后面通过Subscription中的subscriber(监听者)、priority(优先级)、SubscriberMethod(监听者的一些信息,method:监听者中的监听方法、threadMode 监听方法的线程模型,eventType 被监听的事件类型)这些信息再通过反射,来直接调用这些方法。不信我们继续往下看!!!!
第49-51行:将Subscription中重要的里个参数放置到PostingThreadState 对象中。这里出现了一个aborted参数,学过广播的同学有没有很熟悉,这个就是我们有序广播的一个重要的参数就是拦截我们优先级高的广播拦截优先级低的广播参数。
我们继续看PostingThreadState 这个方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
} void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
看到没有,这里就是拿到我们subscription里面subscriberMethod去判断
PostThread:直接调用invokeSubscriber()方法,而invokeSubscriber()方法直接利用了反射 method.invoke,将直接该对应Activity中的onEvent方法,也不用管事件发送来自于线程
MainThread:首先判断事件发送线程是否来自主线程,如果是则和PostThread模型一样,直接反射调用方法,如果不是,把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,当然这里我们还是运行在主线程中。
BackgroundThread:如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列。
Async:将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。
ok,这里我们就将主要的方法讲完了,这里来总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。很简单有没有。
- EventBus.getDefault().unregister(this);
这个方法就很简单的,就是讲subscriptionsByEventType中移除掉,源码如下:
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
} private void unubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
这样我们就将EventBus2.4源码差不多读完了,还有一些不太重要的类和方法我就没有去给大家介绍了。
3,EventBus3.0简介
我们知道现在EventBus到了3.0版本了,也在不断的改善,这里使用方法也有稍稍的变化,所以在这里先给大家简单的介绍一下使用
EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快.
在build.gradle中添加如下配置
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 "自己的包名.MyEventBusIndex"
}
}
并且要在事件响应方法中上添加@Subscribe注解关键字,此时编译一次,自动生成生成索引类。在\build\generated\source\apt\PakageName\下看到通过注解分析生成的索引类,然后可以添加索引到EventBus默认的单例中,这样可以加快注册效率
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
简单的代码如下:
package com.qianmo.eventbustest; import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast; import com.qianmo.eventbustest.event.FirstEvent; import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe; public class MainActivity extends AppCompatActivity {
private Button btn_skip;
private TextView tv_message; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// EventBus.getDefault().register(this);
btn_skip = (Button) findViewById(R.id.btn_skip); tv_message = (TextView) findViewById(R.id.tv_message); btn_skip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
} @Subscribe
public void onEventMainThread(FirstEvent event) {
tv_message.setText("收到的消息是:" + event.getMsg());
Log.i("wangjitao", "onEventMainThread:" + Thread.currentThread().getName());
} // public void onEventBackgroundThread(FirstEvent event) {
// Log.i("wangjitao", "onEventBackgroundThread:" + Thread.currentThread().getName());
// }
//
// public void onEventAsync(FirstEvent event) {
// Log.i("wangjitao", "onEventAsync:" + Thread.currentThread().getName());
// }
//
// public void onEvent(FirstEvent event) {
// Log.i("wangjitao", "onEventPostThread:" + Thread.currentThread().getName());
//
// } @Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
然后还是来看看我们线程的四种接收模式,现在3.0之后变成以注解的方式来标记了,而不是像2.x版本必须是固定的方法名,方法名随便写都行
@Subscribe(threadMode = ThreadMode.PostThread, sticky = true)
public void onMessageEventPostThread(MessageEvent messageEvent) {
Log.e("PostThread", messageEvent.getMessage());
} @Subscribe(threadMode = ThreadMode.MainThread, sticky = true)
public void onMessageEventMainThread(MessageEvent messageEvent) {
Log.e("MainThread", messageEvent.getMessage());
} @Subscribe(threadMode = ThreadMode.BackgroundThread, sticky = true)
public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
Log.e("BackgroundThread", messageEvent.getMessage());
} @Subscribe(threadMode = ThreadMode.Async, sticky = true)
public void onMessageEventAsync(MessageEvent messageEvent) {
Log.e("Async", messageEvent.getMessage());
}
以前2.x版本注册有5个方法,现在3.0版本就只有以下一个注册方式了
EventBus.getDefault().register(this);
是否黏滞,是否有优先级都是注解中的字段了,不想2.x版本中注册时用参数带入了。
EventBus 2.x 是采用反射的方式对整个注册的类的所有方法进行扫描来完成注册,当然会有性能上的影响。EventBus 3.0中EventBus提供了EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析、处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快。
以上就是EventBus的全部了解了,这样一来我们完全知道了它是怎么工作的,就下来也这么写方法、怎么写返回类型、怎么写方法参数都可以自己随心所愿的写了,再也不用担心出bug而一脸懵逼了。ok,今天终于和大家写完了,See You Next Time 。。。。
Android -- 从源码带你从EventBus2.0飚到EventBus3.0(一)的更多相关文章
- Android -- 从源码带你从EventBus2.0飚到EventBus3.0
1,最近看了不少的面试题,不管是百度.网易.阿里的面试题,都会问到EventBus源码和RxJava源码,而自己只是在项目中使用过,却没有去用心的了解它底层是怎么实现的,所以今天就和大家一起来学习学习 ...
- 45个android实例源码
分享45个android实例源码,很好很强大http://www.apkbus.com/android-20978-1-1.html andriod闹钟源代码http://www.apkbus.com ...
- 分享45个android实例源码,很好很强大
分享45个android实例源码,很好很强大 http://www.apkbus.com/android-20978-1-1.html 分享45个android实例源码,很好很强大http://www ...
- 将android Settings 源码 导入到 eclipse工程
1. 新建 android 项目 拷贝源码/packages/apps/Settings到你的其它目录. 在eclipse中,新建项目,但是要从exitting source选择: 2. 导入相关的 ...
- 【转】编译Android系统源码和内核源码
原文网址:http://blog.csdn.net/jiangwei0910410003/article/details/37988637 好长时间没有写blog了,之所以没有写,主要还是工作上的事, ...
- Appium Android Bootstrap源码分析之启动运行
通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...
- Android精品源码与技术博文
Android精品源码android遵循Material Design风格天气源码基于exoplay 自定义播放器 支持直播 1 ExoUserPlayer 基本播放器...几种动画效果Animati ...
- Android -- AsyncTask源码解析
1,前段时间换工作的时候,关于AsyncTask源码这个点基本上大一点的公司都会问,所以今天就和大家一起来总结总结.本来早就想写这篇文章的,当时写<Android -- 从源码解析Handle+ ...
- 分享45个android实例源码,很好很强大.收藏吧!!!
andriod闹钟源代码 http://www.apkbus.com/android-20974-1-1.html android源码分享之指南针程序 http://www.apkbus.com/an ...
随机推荐
- gstunnel---一个网络安全管道
项目简介: gstunnel 是 基于go 语言开发的一个安全网络管道,支持tcp协议. gstunnel分为client和server两部分. gstunnel 基于aes进行数据加密. 流程示意: ...
- 规范 : Sql statusEnum
statusEnum 的诞生是为了在看Sql 表时,可以知道他是一个有特别的string的分类,在扩张或修改时,可以方便追踪到c#, e.g. 如果是“称呼”(column title),在sql没有 ...
- Hibernate双向关联的增删改操作的属性
双向关联关系下的增删改操作的属性 1.cascade属性: eg:<set name = "emps" cascade="s ...
- PetaPoco 快速上手
今天来给大家分享一个好用的轻型的.net框架的ORM——PetaPoco 本着快速上手的原则,我们通过和EF对比,让大家能快速使用PetaPoco PetaPoco大家可能没有听说过,但大家一定听说过 ...
- Web平台安装及检测程序
软件名称:microsoft web platform installer 上图: 可以看做是一个App Store, 你再也不用东奔西跑去找什么开发软件,CMS等等了,直接打开这个,勾选上就安装吧, ...
- 【杂】poj2482 Stars in Your Windows 题面的翻译
原地址:http://poj.org/problem?id=2482 神题,被誉为最浪漫的题目,一位acmer以自己独特的方式写下的殷殷情语 你窗前的星星 纵时光飞逝如梭,也我对你的回忆也永不黯然.从 ...
- KoaHub.JS基于Node.js开发的处理和显示日期代码
moment Parse, validate, manipulate, and display dates A lightweight JavaScript date library for ...
- 1610: [Usaco2008 Feb]Line连线游戏
1610: [Usaco2008 Feb]Line连线游戏 Time Limit: 5 Sec Memory Limit: 64 MB Submit: 1396 Solved: 615 [Subm ...
- swift -- 静态变量static
import UIKit class ViewController: UIViewController { //静态变量 swift中的static静态变量,只能在这里声明,不能在方法中声明 ...
- Redis Sentinel中的机制与原理详解
序言 Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案.实际上这意味着你可以使用Sentinel模式创建一个可以不用人为干预而应对各种故障的Redis部署. 它的主要功能有以 ...