- 写在前面:如果你看到这篇文章,希望你能够自己对照着源码走一遍,直接反射或者使用索引都会详细分析使用的!
EventBus优点和特性
- 也许你有个疑问: 市面上有很多事件传递框架为何选择EventBus,而不是Otto , Rxbus等,相比较他们都可以满足日常开发需求,只是后两者更新基本停滞,而EventBus还在正常更新维护,选择接入人数也是最多的,同时功能也是最全的,如果你选择了使用该框架,自然了解源码的运行对于调试绝对是有帮助的!
- 我们学知识不能简简单单只是会用而已,要知其然而知其所以然才能进步,提升自己~
优点
- 简化组件间的通信,解耦事件发送和接受者
- 很好的应用于Activity,Fragment之间,后台线程之间的通信,避免使用多个Intent传递,handler通信等
- 避免复杂的,易于出错的依赖和生命周期问题,比如多个Activity跳转后的回调问题
- 速度响应快,尤其是3.0以后再编译器增加索引
- 轻量大概50K左右的jar包
- 被大量的app所使用,在实际中被验证
- 有很多的高级功能,如线程切换模式,订阅的优先级等
特性
- 简单易用的注解API : 简单的@Subscribe注解放到订阅方法上即可通过编译期的订阅者索引,app就可以不用在运行期通过注解反射拿到订阅了
- 支持事件投递到UI线程,不管事件是从哪个线程发送过来的
- 支持事件和订阅者的继承关系: 如 事件A是事件B的父类,则发送B类型的事件,该事件也会发送给对于事件A感兴趣的订阅者,对于订阅者也存在相似的继承关系,可以通过eventInheritance(false)关闭该功能,只在本类中调用,父类接口不在回调的
- 即可零设定EventBus直接工作,也可以通过构建者模式调整EventBus的行为,满足你的需求EventBusBuilder.builder()
//通过EventBusBuilder.builder()的方法构建EventBusBuilder对象配置里面的信息以后调用build()方法创建
public EventBus build() { //创建一个EventBus对象
return new EventBus(this);
}
复制代码
- 注意:通过此方法可以创建多个EventBus对象,且每个配置信息不同,但是每个EventBus的实例都是独立的,也就是说每个EventBus post事件,只要使用该EventBus注册的订阅者才能接收到,其他EventBus注册的是无法接收的即在EventBus.getEventBus(type).regirst(this)的Activity或者Fragment中的方法都注册到了该type返回的EventBus中啦!
EventBus的构建
- 首先分析该类的各个属性含义
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
//默认线程池,是一个核心0,最大Max,无缓存队列SynchronousQueue;如果大量事件一起发送可能导致OOM,一般需要更改一下线程池配置
//通过build的方法executorService(executorService)传递进来,主要用于异步和后台事件传递
////在订阅方法中抛出异常时,是否打印日志,在类中注解重写onSubscriberExceptionEvent可以监听到
boolean logSubscriberExceptions = true;
//没有找到订阅方法,是否打印日志
boolean logNoSubscriberMessages = true;
//在非订阅SubscriberExceptionEvent事件方法中抛出异常时, 是否发送SubscriberExceptionEvent事件
boolean sendSubscriberExceptionEvent = true;
//没找到事件订阅方法,是否发送NoSubscriberEvent事件
boolean sendNoSubscriberEvent = true;
//在非订阅SubscriberExceptionEvent事件方法中抛出异常时, 是否抛出EventBusException异常, 默认为false
boolean throwSubscriberException;
boolean eventInheritance = true; //发送子事件,是否发送父事件,默认为true,最好改成false,避免不必要的麻烦
//三个参数用于参照订阅方法
boolean ignoreGeneratedIndex; //是否直接用反射查找订阅方法(就是运行期查找,速度慢耗时,3.0以后优化使用索引的),默认是false
boolean strictMethodVerification; //非注解生成索引时,严格方法验证:当方法不符合格式(public,非abstract, 非static,非桥接方法,只有一个参数)时,是否抛出EventBusException异常,默认false
List<SubscriberInfoIndex> subscriberInfoIndexes; //注解生成的索引,在编译器生成,需要通过android-apt三方插件或annotationProcessor生成
List<Class<?>> skipMethodVerificationForClasses; //检查以onEvent开头的方法,基本不用啦,都是使用注解自定义方法了
复制代码
-
注意: 对EventBus增加索引,必须在第一个EventBus.regirst之前,因此一般操作都是在Application的oncreate方法中添加
//提高性能:第一点是关闭父类以及接口查找分发事件(只有本类调取,父类接口不在回调); //第二点 添加索引,索引添加的原理就是提前在编译的时候加载好注册类的相关信息。 EventBus.builder().addIndex(new MyEventBusIndex()).eventInheritance(false).installDefaultEventBus(); //EventBus.getDefault() 获得的即为当前installDefaultEventBus方法生成的,不会在重新创建了,但是如果通过builder.build()重新构建的EventBus就无效了! 复制代码
这里我们主要通过Event3.0的源码,毕竟用新不用旧嘛
注解Subscribe
- 通过注解标记订阅方法:同时可以指定threadMode,代表该订阅方法运行的线程,指定sticky,代表是否是粘性事件,指定priority代表优先级(由高到底依次接收,可以更改设置)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
复制代码
- threadMode: 订阅方法运行的线程
- ThreadMode.POSTING —— POST线程,订阅方法运行在发布者所在的线程(默认)
- ThreadMode.MAIN —— UI主线程,订阅方法运行在主线程,发送如果是UI线程直接运行,如果不是通过Handler回调到UI线程运行
- ThreadMode.BACKGROUND —— 后台线程,发布者是主线程,订阅方法运行在新开子线程;发布者是子线程,订阅方法运行在发布者所在的线程;(通过线程池创建子线程)
- ThreadMode.ASYNC —— 异步线程,订阅方法运行在新开子线程,无论发布者是在哪个线程(同上共用一个线程池)
- sticky: 与Android广播中的sticky概念一致,表示如果当前还未注册,则通过postSticky发送的广播将会保存在内存中,当有注册时就会法将该粘性事件传递给订阅者,即先发送后订阅是可以接收到的!
- priority:订阅优先级,事件发送以后根据优先级传递,相同优先级根据订阅顺序传递(添加由大->小,调取方法直接轮询由0 -> size从而达到事件传递性,且事件对象不可变但属性值可变从而可以更改值的传递)
注册register
- EventBus通过register注册到Activity或Fragment中
public void register(Object subscriber) { //subscriber表示this
Class<?> subscriberClass = subscriber.getClass(); //获取this代表的类
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //查找订阅者类中的所有订阅方法
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
//SubscriberMethod类的属性
public class SubscriberMethod {
final Method method; //订阅方法
final ThreadMode threadMode; //订阅方法在哪个线程执行
final Class<?> eventType; //事件类
final int priority; //优先级
final boolean sticky; //是否是粘性
/** Used for efficient comparison */
String methodString;
复制代码
- 看一下 subscriberMethodFinder.findSubscriberMethods如何查找订阅方法
//首先肯定是subscriberMethodFinder的创建:在EventBus的构造函数
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex); //注意里面传递的参数: 索引,方法验证是否符合,是否使用反射调用
//引入EventBus的第一个缓存map集合: 键表示当前注册类activity或者Fragment,值表示当前类中的订阅方法的集合
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) { //如果注册类已经缓存过了,直接返回
return subscriberMethods;
}
//ignoreGeneratedIndex表示使用反射也就是忽略注解器编译器生成的MyEventBusIndex类,默认是false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass); //通过反射调用,在运行期
} else { //我们都会添加编译器生成的代码逻辑
subscriberMethods = findUsingInfo(subscriberClass);//// 从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
}
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,通过索引获取的稍后解释
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);//从数组长度为4中取值并初始化Findstate
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState); //找到类中的所有事件响应方法
findState.moveToSuperclass(); //继续寻找当前类父类中注册的事件响应方法
}
return getMethodsAndRelease(findState);
}
复制代码
- 查找类中的所有事件响应方法
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 获取类中的所有方法,包括私有方法
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(); //获取方法的属性,private,static等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //如果方法时public,且不是abstract,static,bridge, synthetic编译器生成的
Class<?>[] parameterTypes = method.getParameterTypes();//获取方法参数,且参数只有1个
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); //获取以Subscribe注解的方法
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())); //将一个类中的所有订阅方法都加入到findstate的集合中
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
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");
}
}
}
复制代码
- 我们看一下FindState类的属性
//FindState是SubscriberMethodFinder的静态内部类,类不长,我们都拿过来吧
static class FindState {
final List<SubscriberMethod> subscriberMethods = new ArrayList<>(); //订阅方法信息列表
final Map<Class, Object> anyMethodByEventType = new HashMap<>(); //以事件类型为key,方法信息为value的集合
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>(); //以methodkey为key,订阅者类为value集合 (下方的methodkey : method方法所在的类)
final StringBuilder methodKeyBuilder = new StringBuilder(128); //生成methodkey
//methodkey 是由method名字,事件类名称组成
Class<?> subscriberClass; //订阅者类
Class<?> clazz;
boolean skipSuperClasses; //是否跳过父类
SubscriberInfo subscriberInfo;
void initForSubscriber(Class<?> subscriberClass) { //初始化
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
void recycle() {
subscriberMethods.clear();
anyMethodByEventType.clear();
subscriberClassByMethodKey.clear();
methodKeyBuilder.setLength(0);
subscriberClass = null;
clazz = null;
skipSuperClasses = false;
subscriberInfo = null;
}
//检查订阅者中注册的事件响应是否可以合法的加入到订阅方法信息中,分为两层检查
boolean checkAdd(Method method, Class<?> eventType) { //method是订阅方法,eventType是事件类
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
//第一次检查同一事件类型下是否已有订阅方法信息了
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) { //没有就添加成功
return true;
} else {
//如果当前事件类型在同一个类中对应多个方法,则检查他们方法签名是否一致
if (existing instanceof Method) { //existing是相同的键对应的oldValue
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this); //方法签名不一致(一个类中多个方法中含有同一个事件类)添加FindState作为value
}
return checkAddWithMethodSignature(method, eventType);
}
}
//使用订阅方法签名检测是否可以加入订阅方法信息列表中,method订阅方法,eventType事件类
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
//方法签名methodkey怎么生成的
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass(); //方法所在的类
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) //methodClass是MethodClassOld的子类或者子接口
{
//如果不存在同样方法签名的订阅方法或 之前保存的订阅方法所在的类为当前将要添加的订阅方法所在的类的子类(目前不存在此情况,因为只会从子类向父类查找),则可以合法添加此订阅方法信息
return true;
} else {
// 存在同样方法签名,且methodOld是子类
//subscriberClassByMethodKey只保存父子继承关系的最下层子类,目的是为了在子类注册监听事件时,如果父类中有相同的事件响应方法,应该调用子类的覆写方法
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
}
复制代码
- 以上对于FindState中的步骤为:
- 通过反射获取class的所有方法
- 过滤掉不是public和是abstract,static,bridge,synthetic的方法,且参数只有一个的
- 找出别subScribe注解修饰的方法
- 将method和事件类型添加到findState中
- 将所有的method订阅方法封装成SubscribeMethod添加到findState中集合类中
- 这就是查询订阅方法的步骤,下面接着上文,继续注册步骤!
- 我们目光从新回到注册逻辑上面来
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //上面已经具体分析了,如何去找到该类中的所有订阅方法
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod); //遍历方法调取
}
}
}
复制代码
- 我们看subscribe(subscriber, subscriberMethod) 参数: 当前订阅类, 订阅类中的订阅方法
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType; //订阅方法的事件类
Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //创建一个订阅类的封装,注意参数
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;
}
}
//typesBySubscriber缓存的是key:当前订阅类activity/fragment, value:当前类中的订阅方法的事件集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType); //在当前订阅方法集合中加入当前事件
if (subscriberMethod.sticky) { //如果是粘性
if (eventInheritance) { //是否查找父类方法
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); //查找当前是当前事件是缓存事件的父类则也会调用它的子类
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent); //在注册的时候就开始调用订阅方法了,如果缓存队列中存在
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
//checkPostStickyEventToSubscription
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// 一会儿post的时候还要研究这个方法
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
//new的一个订阅类 Subscription 属性包括
final class Subscription {
final Object subscriber; // 订阅者对象
final SubscriberMethod subscriberMethod; // 订阅方法
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active; // 是否处于激活状态.如果不激活就不运行哦
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true; //新建的肯定是激活状态
}
@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
@Override
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}
复制代码
- 总结订阅方法: 由于上方已经总结了对于一个订阅类查找到它之中的所有订阅方法以后循环调用subscribe继续注册
- 创建一个订阅类,判断是否存在以当前事件为key的List<订阅类>的集合,就是将所有相同的事件的订阅类统一管理,这样发送一个事件,只需要遍历该集合即可知道回调那些类的哪些方法
- 注意加入到List集合时根据优先级排序,由大到小,调用顺序也是如此即可实现优先级发送事件啦
- 对于订阅类通过typesBySubscriber缓存: key当前订阅类,value:当前订阅类中所有事件类的集合
- 如果是粘性事件,判断是否需要调取事件父类或接口对应的订阅方法,如果true且缓存中存在该事件的post,则直接执行订阅方法,否则只执行当前类中的订阅方法(达到了先发送,后订阅,且订阅即运行的目的,可以在Fragment中不通过intent对重新打开的Activity传递信息)
反注册unregister
- 有订阅对应的就有反注册,否则会内存泄漏且多次注册相同信息源码中报错的
- 我们先思考一下,上方注册的时候主要有两个map集合,
- ypesBySubscriber表示key:订阅类,value:订阅类中的所有的事件类型集合;
- subscriptionsByEventType表示key: 事件类型 , value: 所有注册该事件的订阅方法集合
- 举个例子,朋友圈A和B是好友,如果A要取关B,则A中有map(A , List好友们),在A中查找List好友将B删除,同时在B中找到他的好友列表将A删除即可啦
- 同样的,对于上方两个集合来说:我们也是反注册时通过this订阅类在ypesBySubscriber集合中找到该订阅类中的所有事件类型的集合后,遍历在subscriptionsByEventType中找到以当前事件为key的value,订阅方法集合中删除该类对应的订阅方法即可啦
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); //通过订阅类获取他当中的所有订阅事件类,我们加入的时候时加入事件类的class
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) { //遍历订阅事件,开始解绑另外一个集合中的数据:以eventType为key中删除
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber); //从当前订阅类为key的map移除
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //获取以当前事件为key的所有订阅方法集合
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) { //遍历如果事件是在当前类中订阅的,则从订阅方法集合中删除,因为这个类就要over啦,自然也就不用再接受订阅事件啦
subscription.active = false; //标记不激活
subscriptions.remove(i); //从订阅方法集合中删除
i--;
size--;
}
}
}
}
复制代码
post发送事件
- 通过上面的注册和反注册已经将事件,订阅方法都添加到集合中了,我们就可以发送一个事件开始响应方法啦!
- 小知识点: ThreadLocal是线程隔离的只有同一线程才能获取值,通过get访问,其他线程获取不到的,同一个线程中才能获取,且同一个线程获取的是同一个对象
//ThreadLocal 的get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //如果已经存在map则返回map中的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//调用initialValue创建一个对象值,存储到map中,下次直接通过get即可获取
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
//上面简单分析了ThreadLocal的源码,下面开始真正的post操作
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() { //如果调用get方法没有value值通过这个方法新建一个返回的,下次改线程在调用,已经存在直接返回了,达到了对象的线程唯一性
return new PostingThreadState();
}
};
public void post(Object event) {
//代码很短小,第一行就蒙蔽了,不急,就是上面我们说了get函数
PostingThreadState postingState = currentPostingThreadState.get(); //不同线程对应不同的postingState,相同的线程之间遵循happended-before
List<Object> eventQueue = postingState.eventQueue; //postingState中的eventQueue队列
eventQueue.add(event); //将事件添加到队列中:每个post线程的队列都是相互独立的
//isPosting默认false,可以进去
if (!postingState.isPosting) { //相同线程是遵循happended-before规则的,是安全的
//是否是主进程
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;
}
复制代码
- post流程
- 首先通过ThreadLocal获取当前线程中状态PostingState,注意不同的线程拥有不同的状态PostingState
- 将发送的event加入到PostingState类中的队列中
- 循环遍历事件队列,发送单个事件调用
- 发送单个Event的方法postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass(); //获取事件类型
boolean subscriptionFound = false;
if (eventInheritance) { //如果允许事件继承,默认允许
//找到eventClass的所有父类和实现接口
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
// 依次向 eventClass 的父类或接口的订阅方法发送事件
// 只要有一个事件发送成功,返回 true ,那么 subscriptionFound 就为 true
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)); //发送NosubSribeEvent事件,可以重写订阅方法接受到从而做自己的处理逻辑
}
}
}
//postSingleEventForEventType(event, postingState, eventClass)参数: 事件对象里面可能有值, 当前线程的状态postingState, 事件类型class
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass); //获取以当前事件为key的订阅类的集合(属性: 订阅类,订阅方法,是否激活)
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) { //还记得添加由大->小,这里取值是由0->size实现优先级,同时event对象不可变,但是每个共用同一个event,里面的属性值是可以更改的
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//发送事件,参数subscription订阅类信息,event发送事件对象, 是否是主线程
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;
}
//postToSubscription: isMainThread 表示发送方法是否在主线程中
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 { //其他线程向主线程发送添加到队列(链表)后通过Handler一次发送队列中的值给UI线程最终调用的还是invokeSubscriber(subscription, event)方法
//PendingPost添加队列获取是通过PendingPost对象池,同Message相同的
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) { //如果在子线程中发送,则随机开辟一条子线程接收
backgroundPoster.enqueue(subscription, event);
} else { //在子线程中发送,在相同的线程中接收
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //无论是UI还是子线程,都会开辟一个新的线程接收
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
//invokeSubscriber参数: subscription订阅类信息: 订阅类,方法,是否激活信息
void invokeSubscriber(Subscription subscription, Object event) {
try {
//反射调用方法: method.invoke(class, 参数信息)即可,在哪个线程调用就运行在哪个线程
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause()); //如果没有找到就发送错误事件,重写即可监听到
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
复制代码
- 我们看一下线程之间是如何做的吧!
- 主线程的mainThreadPoster.equeue()
//在EventBus中创建的
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
final class HandlerPoster extends Handler {
private final PendingPostQueue queue; //事件队列
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue(); //每个线程都有他自己的时间队列
}
void enqueue(Subscription subscription, Object event) {
//获取很有意思,所有线程共用同一个对象池,当存在可用返回,否则新建,回收的时候添加到池中,了解过Message消息对象的很熟悉啦
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");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost); //UI线程取出来调用invokeSubscriber方法
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}
//Event中的方法
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost); //回收对象到对象池中以便下一个运用
if (subscription.active) { //如果当前激活状态,则反射调用方法即可
invokeSubscriber(subscription, event);
}
}
//类的静态常量,所有线程共用同一个
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
复制代码
- 直到现在我们还没有用到线程池的概念吧,这个时候你是不是都忘记了EventBus构造函数中创建了一个线程池newCacheExecutor() 可能导致OOM,看一下backgroundPoster.enqueue(subscription, event);
//EventBus构造函数中创建
backgroundPoster = new BackgroundPoster(this);
final class BackgroundPoster implements Runnable { //代码量不多,整个拿过来
private final PendingPostQueue queue; //时间队列
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
//通过事件封装对象池获取对象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost); //添加一个到队列中,下面就可以执行了
if (!executorRunning) { //线程池是否正在运行
executorRunning = true;
//调用EventBus中创建的线程池执行该类的run方法
eventBus.getExecutorService().execute(this);
}
}
}
@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;
}
}
}
//线程池的一个线中程执行方法,同上方的Ui线程一致的,反射调用执行方法即可,无非就是运行线程不同,这个是子线程,上面的是UI线程
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
// asyncPoster.enqueue(subscription, event);同样是在EventBus构造方法中创建的
class AsyncPoster implements Runnable { //类比较简单
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll(); //通过子线程直接运行即可
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
复制代码
- 通过上面分析,equeue添加是一个个添加,一个个执行,通过executorRunning变量控制着,如果run执行中耗时过长,添加的特别迅速,线程池将会创建大量线程,最终可能OOM
- Posting: 表示post线程在哪儿发送,接受的就是该线程
- Main: 如果在主线程中发送,则直接执行接受,否则利用Handler回调到主线程中执行
- BackGround子线程: 如果发布事件在主线程,则调用线程池中的一个子线程去执行,否则直接在发送线程中执行
- (ASYNC)异步线程: 无论发布事件是UI还是子线程都利用一个异步线程来执行!
- 注意:以上所有的方法分析都是在Post()方法内部调用逻辑,所以线程切换需要注意是否是线程池创建还是原来的post方法所在的线程之内
配置索引
- EventBus3.0以后为了提高效率避免在运行期通过反射来做以上大量的工作,使用了索引配置生成类,可以在编译器生成注册文件,从而提升效率
- 使用:两种方式:使用android-apt三方插件,或者annotationProcessor
//1.使用android-apt
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 "com.monster.android.wild.MyEventBusIndex"
}
}
//2\. 使用annotationProcessor
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex:'com.monster.android.wild.MyEventBusIndex']
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
复制代码
- com.monster.android.wild.MyEventBusIndex就是我们想要生成的Subscriber Index类
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; //缓存mao:key当前订阅类 , 值是封装的SimpleSubscriberInfo类(订阅类,应该检查父类,该订阅类中对应的订阅方法的数组)
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
// 从以上createInfoIndexFile()的实现来看,除了类名MyEventBusIndex,只有这一段代码是非hardcode。编译器自动生成的代码
// 以订阅者类为单位,将该订阅者类中的所有订阅函数以及相关参数,封装到SimpleSubscriberInfo类对象中,
// 以供EventBus在注册过程中使用。注意SimpleSubscriberInfo类对象是在编译时期就生成的,
// 因而在运行时期可以直接使用,省去了运行时期通过反射做相似操作的时间和资源消耗,从而提高效率,这里就是全文的精髓所在。
putIndex(new SimpleSubscriberInfo(com.monster.android.wild.myeventbusdemo.MainActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent",
com.monster.android.wild.myeventbusdemo.MainActivity.EventBusEvent.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
// 将会在EventBus在注册过程中使用,等会大家会看到
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
复制代码
- 上面register中有两个分支,通过ignoreGeneratedIndex是否直接使用反射调用区分,我们当时走的是第一个findUsingReflection,下面看添加索引后的第二个方法findUsingInfo
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass); //findstate数组中准备并初始化FindState对象
while (findState.clazz != null) {
//重点,重点,重点: 通过索引获取编译器生成的订阅者信息
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) { //注意这里的subscriberInfo实际上是系统自动生成的类SimpleSubscriberInfo对象 //如果找到了就遍历订阅类中的订阅方法数组加入进去
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); //调用SimpleSubscriberInfo.getSubscriberMethods方法获取订阅方法集合封装成SubscriberMethod[]
for (SubscriberMethod subscriberMethod : array) { //轮询,下面就跟反射是一致的操作了
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else { //否则还是老实的回到反射注册上面去吧
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState); //回收findState对象,以便下次使用
}
//getSubscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { //新建的findState为null
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//subscriberInfoIndexes是在合适创建的
if (subscriberInfoIndexes != null) { //subscriberInfoIndexes可能添加多个文件信息,这里我们只是一个
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
//拿到唯一的一个index类即index为: MyEventBusIndex类对象
SubscriberInfo info = index.getSubscriberInfo(findState.clazz); //调用它的getSubscriberInfo(订阅类)即可获得已经put进缓存SUBSCRIBER_INDEX中的方法封装类SimpleSubscriberInfo
if (info != null) {
return info;
}
}
}
return null;
}
//通过EventBus构造函数中创建通过build传递进来的,如果我们使用索引并且重写build方法传递进来数据了
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//将索引值穿进去
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
复制代码
-
至此:无论是直接反射注册还是通过添加索引在编译器生成文件运行期直接读取注册都分析完成了,不知道你明白了没有呢?最后的话也整理了一份《Android相关源码精编解析》,方便大家笔记学习,有需要的朋友可以点赞+评论支持下,然后评论留言或私信我获取!