背景
在Android开发中,我们经常会遇到对于一个相同ID的对象,现在在不同activity页面,当我们在其中一个activity修改该对象的属性时,我们怎么来同步这些现实呢?在我之前的的项目中,自己一直是发广播或者是定一个静态方法,在修改的activity中,调用这个静态方法,这个是可以解决问题,但是不够方便。android开源项目中,做事件通信的很多,EventBus,Rxandroid..等等,从使用的简单程度和项目的成熟度,在项目中使用eventbus都是一个不错的选择 。
本博使用eventbus版本:2.4.4
开发工具:Android studio 1.5
简单使用
第一步,注册接受者
EventBus.getDefault().register(this);
第二步,定义事件,其实就是一个Java实体类
public class MyEventObject {};
第三步,在接受者中声明接受事件的方法onEventMainThread
或者是其它()注意:这些方法可以有不同的重载;
public void onEventMainThread(MyEventObject object) {
}
public void onEventMainThread(MyEventObject2 object) {
}
/**
注意:这两个重载方法,哪个能接受到,发布的这个事件尼?
答案是:事件发布时,指定的哪个EventObject,哪个重载方法就能收到,订阅事件
**/
(初学者可以先跳过这四个方法的详解)延伸其实也可以根据,不同需求使用如下方法:
onEvent:
/**如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,
onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。
使用这个方法时,在onEvent方法中不能执行耗时操作,
如果执行耗时操作容易导致事件分发延迟。**/
onEventMainThread:
/**
如果使用onEventMainThread作为订阅函数,
那么不论事件是在哪个线程中发布出来的,
onEventMainThread都会在UI线程中执行,
接收事件就会在UI线程中运行,这个在Android中是非常有用的,
因为在Android中只能在UI线程中更新UI,
所以在onEvnetMainThread方法中是不能执行耗时操作的。
*/
onEventBackground:
/**
如果使用onEventBackgrond作为订阅函数,
那么如果事件是在UI线程中发布出来的,
那么onEventBackground就会在子线程中运行,
如果事件本来就是子线程中发布出来的,
那么onEventBackground函数直接在该子线程中执行。
*/
onEventAsync:
/**
使用这个函数作为订阅函数,
那么无论事件在哪个线程发布,
都会创建新的子线程在执行onEventAsync.
也就是说在这个方法中可以执行一些耗时操作
*/
第四步,发布事件
EventBus.getDefault().post(new FirstEvent("呵呵,渺小的地球人"));
这样当,发布事件后,MainActivity就可以收到SecdondActivtiy发布的事件,然后更新先关的字段显示
Evenbus疑问及原理
疑惑
Eventbus里的几个概念对象到开发中:
1.事件发布者:activity.fragement,service,thread
2.事件订阅者:activity.fragement,service,thread
3.事件:Android组件中实体类
学习完EventBus简单实用,肯定会有很多疑问:
**1当有事件发布的时候,是谁在调用onEvent…这些方法?
2.如何根据事件发布在哪些线程,而让这些方法运行在规则约定的线程?
3.我们以前实用Intent在页面之间传递数据,现在实用这种类似于回调的几种方法,这些方法,会不会造成类存泄露啊?**
带着这些疑问,我们来看看源码里是怎么实现
源码分析
**开始前,明确我们想搞清楚哪几个问题:
a.如何调用onEventXXXX方法
b.如何根据onEventXXXX指定线程的调度
c.同一个onEventXXXX的重载方法是如何发布事件的
d.最后,通过阅读源码,使用eventBus,在声明注册、反注册、onEventXXXX方法声明、事件发布,时需注意哪些事情。**
1.注册从EventBus.getDefault().register(this);
入手
/**
在这个方法的源码注释里,已经说的很清楚
1.当补关注事件时,一定要解除事件的绑定,在组件的生命周期中,解除订阅
2.根据需要,指定onEventXXXX方法,是否需要在ui线程调用onEventXXXX等等,如何选择方法,参看上面四个方法的注释
*/
public void register(Object subscriber) {
register(subscriber, false, 0);
}
我们在切到这个方法的实现方法:
private synchronized void register(Object subscriber, boolean sticky, int priority) {
/***这里通过反射,找到四个(有几个就是几个)方法,将它们封装成
SubscriberMethod对象,添加到subscriberMethods 这个集合里
**/
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//订阅事件
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
是不是很好奇,这个SubscriberMethod里面到底封装了什么?
SubscriberMethod(Method method, ThreadMode threadMode, Class<?> eventType) {
this.method = method;//同过反射,拿到组件里的哪个onEventXXXX方法
this.threadMode = threadMode;//注意这里,就是我们要知道的,onEventXXXX到底在哪个线程运行
this.eventType = eventType;//事件,就是我们定义的那个FirstEvent实体类
}
如何知道,onEventXXXX到底在哪个线程运行?
好,来看看EventBus类里的这个成员这个类:
private final SubscriberMethodFinder subscriberMethodFinder;
其实是这个类的findSubscriberMethods(),使用了反射来,将所有的onEvnetXXXX方法和参数,运行在哪个线程,来一一分装的,这个方法很长,但是逻辑很清晰:
/**
1.通过我们,穿进来的订阅者对象(在本例中是activity),通过反射的, Method[] methods = clazz.getDeclaredMethods();找到所有的方法,循环,找到methodName.startsWith(ON_EVENT_METHOD_NAME),找到onEvent开头的方法,并且过滤掉系统的方法,最后根据名字中是否包含
"MainThread"、"BackgroundThread"、"Async",等区分他们运行在哪些线程
*****/
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;
}
//找onEnent方法
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;
}
}
好了,我们再回到事件订阅这个方法:
subscribe(subscriber, subscriberMethod, sticky, priority);
所以订阅的事件都被放在里:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
好了,现在就就等着你发布事件
2.事件发布,从EventBus.getDefault().post(new FirstEvent("你好"));
/**
PostingThreadState ,封装了调用post方法的运行线程信息
发布一条事件,就将其从list中删除,发布事件在postSingleEvent()中实现;
***/
/** 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) {
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;
}
}
}
切到postSingleEvent()中来:
/**
注意,这里就是我们说过的,同一个eventClass的不同订阅onEventxxx都可以响应
**/
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));
}
}
}
再进到postToSubscription()里来:
/**
根据不同的订阅,订阅时确定的threadMode,invokeSubscriber发布订阅
***/
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);
}
}
切到invokeSubscriber()中,看看实现:
/**
好,看到反射了没有?再次反射调用订阅的onEventXXX方法将,事件发布到订阅者
*/
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);
}
}
好了,以上我们就看到了,整个事件:订阅—>发布—->到订阅者的流程
然而,还有一个问题,很纠结,我们知道了,onEventXXXX要运行在哪里线程里,eventBus是如何让他实现的:
看看这个方法:
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);
}
}
再切到enqueue()中来:
public void enqueue(Subscription subscription, Object event) {
//PendingPost 链表,缓存了所有的订阅事件
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
看看这个实现了 Runnable借口,在run方法里使用反射来调用onEventXXXX
class AsyncPoster implements Runnable {}
这样就实现了,onEventAsync每次都开启子线程运行,其他两种类似
嗯哼,这里算是把eventBus的整个运行流程,原理搞清楚了,但是那个ExecutorService executorService是什么?线程池,感兴趣的可以继续切进去。
注意:
我们在指定这几onEventXXXX一定要是public 的否则或报错