Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目录

EventBus

一句话描述:调用【register】方法后,EventBus会把当前类中【onEvent开头的方法】,存入一个【单例】内部维持着一个【map】中,KEY就是【方法中的参数】,Value就是此【onEvent开头的方法】;而后在post的时候,EventBus就会根据我们传入的参数的类型去map中查找匹配的方法(可能有很多个),然后逐个【反射】调用这些方法。

收不到消息的原因

  • 方法名不对(必须以onEvent开头)
  • 参数不对(参数只能有一个,且不能是基本类型)
  • 可能没有注册。发送不需要注册,接收需要注册。父类如果注册的话,子类调用super方法后不能再注册,否则异常退出
  • 注册的位置可能不对(试一下改为在onCreate方法中注册,在onDestroy中1取消)
  • 可能需要加注解(在发送和接收的方法上加上@Subscribe注解)
  • 可能你的onEvent方法运行时有错误(比如空指针),此时EventBus不会提示任何异常,这会让你误以为是没有回调到
  • 如果在BActivity中发了一条消息,AActivity中收到消息后时不能显示吐司的

注册 register

简单说,EventBus.getDefault().register(this)所做的事情就是:在当前类中遍历所有的方法,找到onEvent开头的然后进行存储。

EventBus.getDefault()其实就是个单例,register公布给我们使用的有4个,本质上调用的是同一个:

public void register(Object subscriber) { register(subscriber, DEFAULT_METHOD_NAME, false, 0); }
public void register(Object subscriber, int priority) { register(subscriber, DEFAULT_METHOD_NAME, false, priority); }
public void registerSticky(Object subscriber) { register(subscriber, DEFAULT_METHOD_NAME, true, 0); }
public void registerSticky(Object subscriber, int priority) { register(subscriber, DEFAULT_METHOD_NAME, true, priority); }
private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(), methodName);
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}

四个参数

  • subscriber:需要扫描的类,也就是我们代码中常见的this
  • methodName:用于确定扫描什么开头的方法,这个是写死的"onEvent",可见我们的类中都是以这个开头
  • sticky:是否是粘性事件,最后面再单独说
  • priority:优先级,优先级越高,在调用的时候会越先调用

findSubscriberMethods

首先会调用findSubscriberMethods方法,实际是去遍历该类内部所有方法,然后根据methodName去匹配,匹配成功的就封装成SubscriberMethod对象,最后存储到一个List并返回。

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName) {
String key = subscriberClass.getName() + '.' + eventMethodName;
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.getMethods();//去得到所有的方法
for (Method method : methods) {//开始遍历每一个方法,去匹配封装
String methodName = method.getName();
if (methodName.startsWith(eventMethodName)) {//分别判断了是否以onEvent开头,是否是public且非static和abstract方法,是否是一个参数。如果都符合,才进入封装的部分。
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(eventMethodName.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {//根据方法的后缀,来确定threadMode,threadMode是个枚举类型
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));//将method, threadMode, eventType传入构造SubscriberMethod对象,添加到List
}
}
} 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 " + eventMethodName);//最常见的这个异常是在这里抛出来的
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}

suscribe 方法

然后for循环扫描到的方法,然后调用suscribe方法

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
subscribed = true;
Class<?> eventType = subscriberMethod.eventType; //eventType是我们方法参数的Class,是Map的key
//根据subscriberMethod.eventType,去subscriptionsByEventType查找一个CopyOnWriteArrayList<Subscription> ,如果没有则创建。
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // Map的value,这里的subscriptionsByEventType是个Map,这个Map其实就是EventBus存储方法的地方
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);//Map的value中保存的是Subscription
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
for (Subscription subscription : subscriptions) {
if (subscription.equals(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++) { //按照优先级添加newSubscription,优先级越高,会插到在当前List的前面。
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); //根据subscriber存储它所有的eventType
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (sticky) { //判断sticky;如果为true,从stickyEvents中根据eventType去查找有没有stickyEvent,如果有则立即发布去执行
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());
}
}
}

你只要记得一件事:扫描了所有的方法,把匹配的方法最终保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList<Subscription> )中;

eventType是我们方法参数的Class,Subscription中则保存着subscriber, subscriberMethod, priority;包含了执行改方法所需的一切。

发布 post

调用很简单,一句话,你也可以叫发布,只要把这个param发布出去,EventBus会在它内部存储的方法中,进行扫描,找到参数匹配的,就使用反射进行调用

/** 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都会去调用整个队列
return;
} else {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();//判断当前是否是UI线程(我去,竟然是通过这种方式判断的!)
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {//遍历队列中的所有的event,调用postSingleEvent方法。
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//得到event当前对象的Class,以及父类和接口的Class类型;主要用于匹配,比如你传入Dog extends Animal,他会把Animal也装到该List中。
Class<? extends Object> eventClass = event.getClass();
List<Class<?>> eventTypes = findEventTypes(eventClass);
boolean subscriptionFound = false;
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) { //遍历所有的Class,到subscriptionsByEventType去查找subscriptions,register时我们就是把方法存在了这个Map里
Class<?> clazz = eventTypes.get(h);
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(clazz);
}
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;
}
}
subscriptionFound = true;
}
}
if (!subscriptionFound) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
if (eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) { //根据threadMode去判断应该在哪个线程去执行该方法
case PostThread:
invokeSubscriber(subscription, event); //直接【反射】调用;也就是说在当前的线程直接调用该方法
break;
case MainThread:
if (isMainThread) {//如果是UI线程,则直接调用
invokeSubscriber(subscription, event);
} else {//否则把当前的方法加入到队列,然后直接通过【handler】去发送一个消息,并在handler的handleMessage中去执行我们的方法
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
if (isMainThread) {//如果是UI线程,则将任务加入到后台的一个【队列】,最终由Eventbus中的一个【线程池】去调用executorService = Executors.newCachedThreadPool();
backgroundPoster.enqueue(subscription, event);
} else {//如果当前非UI线程,则直接调用
invokeSubscriber(subscription, event);
}
break;
case Async: //将任务加入到后台的一个【队列】,最终由Eventbus中的一个【线程池】去调用;线程池与BackgroundThread用的是【同一个】
asyncPoster.enqueue(subscription, event); //BackgroundThread中的任务,【一个接着一个去调用】,中间使用了一个布尔型变量进行的控制。Async则会【动态控制并发】
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

粘性事件 sticky

介绍了register和post;大家获取还能想到一个词sticky,在register中,如果sticky为true,会去stickyEvents去查找事件,然后立即去post;

那么这个stickyEvents何时进行保存事件呢?

其实evevntbus中,除了post发布事件,还有一个方法也可以:

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);
}

和post功能类似,但是会把方法存储到stickyEvents中去;

大家再去看看EventBus中所有的public方法,无非都是一些状态判断,获取事件,移除事件的方法;没什么好介绍的,基本见名知意。

2016-9-7

最新文章

  1. ASP.NET MVC Razor
  2. iOS 收起键盘的几种方式
  3. Runtime 类
  4. Apache Spark源码走读之1 -- Spark论文阅读笔记
  5. 条款38:通过聚合设计has-a或者is-implemented-in-terms-of
  6. 转】MyEclipse使用总结——设置MyEclipse开发项目时使用的JDK
  7. xcode 编译错误的 之 头文件 包含成.m了
  8. android应用的不同版本间兼容性处理
  9. opencv3.2.0图像处理之高斯滤波GaussianBlur API函数
  10. Prometheus部署监控容器
  11. maven中央仓库地址(支持db2,informix等)
  12. java安全管理器SecurityManager介绍
  13. pytorch中,不同的kernel对不同的feature map进行卷积之后输出某一个channel对应的多个feature map如何得到一个channel的feature map
  14. 337. House Robber III二叉树上的抢劫题
  15. iOS 11开发教程(十九)iOS11应用视图美化按钮之设置按钮的外观
  16. Linux文件管理常用命令用法总结
  17. Python compile() 函数
  18. Python——eventlet.hubs
  19. Java编程语言下 Selenium 驱动各个浏览器代码
  20. 学习笔记-db

热门文章

  1. macOS 自动修改mac地址脚本
  2. Java JPA 查询实体部分字段
  3. SVN简明使用方法 .
  4. 查看Redis信息和状态
  5. [转]Android DPAD not enabled in AVD
  6. bzoj2400
  7. CF 85D Sum of Medians (五颗线段树)
  8. 未在本地计算机上注册&quot;MSDAORA.1&quot;提供程序
  9. 我的学习笔记之node----node.js+socket.io实时聊天(1) (谨此纪念博客开篇)
  10. GPS