eventbus出来很久了,最近想用一下eventbus,自己对着一些博客撸了一个demo,发现竟然crash了,然后去看看源码发现3.0的eventbus有了很多改动。技术变化真快,得保持谦虚的态度,踏踏实实的学习。正好今天内部群里发了一个如何新技术的学习图,感觉挺好的
作为一个android入门小白还是要多学点。
eventbus是一个是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。比如请求网络,等网络返回时通过Handler或Broadcast通知UI,两个Fragment之间需要通过Listener通信,这些需求都可以通过EventBus实现,作为一个消息总线主要有三个组成部分:
官方一张图可以很好地说明这三者关系
细细想来eventbus确实比android的回调函数要强很多,回调只能回到特定的类中,而Eventbus只要在某类中监听了某个消息,无论在哪个类中,只要有消息发出,监听类都会得到执行,而且比一些广播啥的更加高效。Eventbus主要功能是替代Intent,Handler,BroadCast(onreceive不能超过10s)在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。不过也看到一些人说Eventbus用多了会使代码显得比较混乱。
添加依赖 compile 'org.greenrobot:eventbus:3.0.0'
- 定义传递的数据结构
这是传递的一个载体,由于传递调用post需要一个object,因此定义的类也可以是一个空类
public class TestEvent {
private String mMsg;
public TestEvent(String msg) {
mMsg = msg;
}
public String getMsg() {
return mMsg;
}
}
EventBus.getDefault().register(this);
在onDestroy中注销
EventBus.getDefault().unregister(this);
@Subscribe(threadMode = ThreadMode.MAIN)
public void doMain(TestEvent event) {
String msg = "onEventMainThread get the msg: \n" + event.getMsg();
tv_msg.setText(msg);
Log.d("thread main", Thread.currentThread().getName());
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
关于这几个参数这里介绍下:
- threadMode:
在EventBus中的观察者通常有四种线程模型,分别是PostThread(默认)、MainThread、BackgroundThread与Async。
PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。
-sticky:
类似粘性广播那样,的粘性事件,简单来说就是发送该事件之后订阅该事件也可以收到订阅前的消息(只能收到最近的一条消息)
-priorty:
默认优先级为0,只有在同一个threadmode中优先级越大越先收到订阅事件。
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_msgpost:
Log.d("thread send", Thread.currentThread().getName());
EventBus.getDefault().post(new TestEvent("testEvent1 msg send byTestAvtivity1"));
break;
}
}
代码下载传送门
这里做了俩个功能,MianActvity中只有俩个button 测试类名(btn_act1)、模拟广播的传递链(btn_act2),
MainActivty的订阅函数
@Subscribe(threadMode = ThreadMode.MAIN)
public void doMain(TestEvent event) {
String msg = "onEventMainThread get the msg: \n" + event.getMsg();
tv_msg.setText(msg);
Log.d("thread main", Thread.currentThread().getName());
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void doBack(TestEvent event) {
Log.d("thread back", Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.ASYNC)
public void doAsync(TestEvent event) {
Log.d("thread async", Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void dopost(TestEvent event) {
Log.d("thread post", Thread.currentThread().getName());
}
在跳转到TestActvity1中点击按钮发布,
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_msgpost:
Log.d("thread send", Thread.currentThread().getName());
EventBus.getDefault().post(new TestEvent("testEvent1 msg send byTestAvtivity1"));
break;
}
}
TestActivy1点击之后从打印中可看到如下日志
这也证明了订阅函数是靠参数区分而不是靠函数名 ,在MainActvity中订阅函数名称不同但是入参一样都是TestEvent。
btn_act2中发布函数中模拟发起网络请求
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_msgpost2:
EventBus.getDefault().post(new TestEvent2(" net request--send by TestAvtivity2"));
break;
}
}
MainActivty的链式订阅函数
@Subscribe
public void doReceive(TestEvent2 event2) {
String msg = " receive the msg: \n" + event2.getMsg();
tv_msg.setText(msg);
EventBus.getDefault().post(new TestEvent3(" Net Datas"));
tv_step2.setText("request Net Data ing…… ");
}
@Subscribe
public void showNetData(TestEvent3 event3) {
String msg = "show data:\n" + event3.getMsg();
tv_step3.setText(msg);
}
EventBus.getDefault().register(this);
/** 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;
}
public static EventBusBuilder builder() {
return new EventBusBuilder();
}
在getdefault中使用双重枷锁,放止并发的产生,在register中去除了之前的4个参数的方法(Object subscriber, String methodName, boolean sticky, int priority)这些都移到了注解中,然后开始register方法
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对象subscriberMethodFinder.findSubscriberMethods(subscriberClass)
获取到注解的订阅函数
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
这里根据是否产生索引来得到订阅方法名
分为是否产生索引??这里没看懂,但是findUsingReflection
和findUsingInfo
里面都是通过一个while循环来找到具体的方法名
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
这里findUsingReflection
和findUsingInfo
都有findState.moveToSuperclass();
这里EventBus对继承的父类也会进行查找注册函数,即子类注册一次即可,父类直接使用 @Subscribe来产生订阅函数即可。
findUsingReflectionInSingleClass(findState);是二者核心函数
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
// 事件处理方法必须为public,这里过滤掉所有非public方法
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
// 事件处理方法必须只有一个参数
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
// 如果某个方法加了@Subscribe注解,并且不是1个参数,则抛出EventBusException异常
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
可以看出,这里该方法主要作用就是找出subscriberClass类以及subscriberClass的父类中所有的事件处理方法(添加了@Subscribe注解,访问修饰符为public并且只有一个参数)。值得注意的是:如果子类与父类中同时存在了相同事件处理函数,则父类中的不会被添加到subscriberMethods。
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
// 标识post的线程是否是主线程
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()) {
// 循环处理eventQueue中的event对象
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
// 重置postingState的一些标识信息
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
其中currentPostingThreadState是一个ThreadLocal类型,里面存储了PostingThreadState;
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
而PostingThreadState()
中则包括post队列的一些信息
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
这里所做的工作只是将注册的事件从Eventbus中移除
水平有限这里只是简单的分析了一下源码,了解大概而已。