EventBus 源码分析与总结

基于V3.1.1
EventBus 官方地址
EventBus GitHub地址

EventBus 是什么

概念:EventBus是一个Android事件发布/订阅框架
同类:Otto、RxBus
出品方:greenrobot

EventBus 优缺点

优点

  • 通过消息总线形式,解耦发布者和订阅者,简化Android事件传递,从而代替Android传统的Intent、Handler、Broadcast方式或接口回调
  • 支持注解配置消息

不足

  • 消息事件满天飞,维护难
  • 无延迟发送
  • 不支持跨进程/App
  • Android生命周期无感知

EventBus 工作原理

消息发布和订阅模型

生产者:发布者发布消息
消费者:订阅者注册消息
调度中心: 执行消息的分发到位

图片来源EventBus

线程切换

主要就是两个切换,主线程切子线程,子线程切主线程
一个进程内通过Looper.getMainLooper可以顺利切换到主线程
子线程通过是通过主线程执行某个线程,自动切换,也可以借助ThreadLocal缓存对象来切换

参考:Android线程消息机制

EventBus 类图

  • EventBus:核心类,代表了一个事件总线。实现发布-订阅模式。
  • SubscriberMethodFinder:寻找订阅者的订阅方法
  • FindState:寻找订阅方法过程中的缓存Buffer,目的内存复用
  • SubscriberMethod:订阅者的方法封装类
  • Subscription:订阅者对象的封装
  • ThreadMode:线程模型,用来标识不同线程执行消息
  • PostingThreadState:消息发布状态封装
  • Poster:不同线程消息发布器

EventBus 订阅-发布时序图

EventBus 订阅-发布流程图

订阅流程源码分析

EventBus.getDefault
获取EventBus单例,

// 提供默认单例 EventBus
public static EventBus getDefault() {
   EventBus instance = defaultInstance;
   if (instance == null) {
       synchronized (EventBus.class) {
           instance = EventBus.defaultInstance;
           if (instance == null) {
               instance = EventBus.defaultInstance = new EventBus();
           }
       }
   }
   return instance;
}

EventBus(EventBusBuilder builder) {
   logger = builder.getLogger();
   // <订阅事件类型,订阅对象集合>
   subscriptionsByEventType = new HashMap<>();
   // <订阅者,订阅事件类型集合>
   typesBySubscriber = new HashMap<>();
   // 粘性事件 map
   stickyEvents = new ConcurrentHashMap<>();
   // 主线程支持类
   mainThreadSupport = builder.getMainThreadSupport();
   // 主线程分发者
   mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
   // 后天线程分发者
   backgroundPoster = new BackgroundPoster(this);
   // 异步现场分发者
   asyncPoster = new AsyncPoster(this);
   indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
   // 订阅方法寻找器
   subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
           builder.strictMethodVerification, builder.ignoreGeneratedIndex);
   logSubscriberExceptions = builder.logSubscriberExceptions;
   logNoSubscriberMessages = builder.logNoSubscriberMessages;
   sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
   sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
   throwSubscriberException = builder.throwSubscriberException;
   // 是否执行分发父类事件类型
   eventInheritance = builder.eventInheritance;
   // 线程执行器 
   executorService = builder.executorService;
}

EventBus.register

  1. 获取订阅者Class
  2. 通过反射,获取Class的所有订阅方法集合
  3. 遍历订阅方法集合,存储两个Map,Map<订阅事件类型,订阅对象集合>, Map<订阅者,订阅事件集合>
  4. 如果订阅的是Stick事件类型,则直接寻找对应事件,然后分发到订阅方法
public void register(Object subscriber) {
    // 获取 订阅对象的 Class
   Class<?> subscriberClass = subscriber.getClass();
   //  通过注解反射拿到 订阅Class的订阅方法
   List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
   synchronized (this) {
       for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // 注册 订阅
           subscribe(subscriber, subscriberMethod);
       }
   }
}

SubscriberMethodFinder.findSubscriberMethods

  1. 先从内存缓存 Map<订阅对象Class,订阅对象Class的订阅方法> 找
  2. 然后通过反射方法获取订阅者Class的所有订阅方法
  3. Map 存储
  4. 返回订阅者的订阅方法集合
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // 内存缓存 Map<订阅对象Class,订阅对象Class的订阅方法>
   List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
   if (subscriberMethods != null) {
       return subscriberMethods;
   }
    
   if (ignoreGeneratedIndex) {  // 默认false
       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 {
        // map存储
       METHOD_CACHE.put(subscriberClass, subscriberMethods);
       return subscriberMethods;
}


SubscriberMethodFinder.findUsingInfo

  1. 通过FIND_STATE_POOL构建FindState,用做找寻订阅方法时的缓存对象
  2. 执行反射寻找订阅者的订阅方法,存储到FindState中
  3. 循环遍历找订阅者的父类的订阅方法
  4. 返回订阅者的订阅方法集合,并释放FindState
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // FindState 在寻找订阅方法过程中 用于存储相关信息,为了内存复用,通过FIND_STATE_POOL维护了4个FindState来循环使用
   FindState findState = prepareFindState();
   findState.initForSubscriber(subscriberClass);
   while (findState.clazz != null) {
        // 获取SubscriberInfo 包含订阅者Class、Methods等
       findState.subscriberInfo = getSubscriberInfo(findState);
       
       if (findState.subscriberInfo != null) {
            ... // 如果有 ,直接遍历 设置methods数据
       } else {
            // 执行 反射获取订阅methods
           findUsingReflectionInSingleClass(findState);
       }
       // 父类Class,然后继续遍历找所有订阅方法
       findState.moveToSuperclass();
   }
   // 返回订阅者的订阅方法集合,并释放FindState
   return getMethodsAndRelease(findState);
}

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
   List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
   findState.recycle();
   synchronized (FIND_STATE_POOL) {
       for (int i = 0; i < POOL_SIZE; i++) {
           if (FIND_STATE_POOL[i] == null) {
               FIND_STATE_POOL[i] = findState;
               break;
           }
       }
   }
   return subscriberMethods;
}

SubscriberMethodFinder.findUsingReflectionInSingleClass
遍历类中所有方法,寻找目标订阅方法,封装成SubscriberMethod存储到findState的subscriberMethods集合中

private void findUsingReflectionInSingleClass(FindState findState) {
   Method[] methods;
   try {
       // 获取所有声明的方法
       methods = findState.clazz.getDeclaredMethods();
   }
   ...
   // 遍历方法
   for (Method method : methods) {
       int modifiers = method.getModifiers();
       // 找 public 方法
       if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            // 找参数为1的方法
           Class<?>[] parameterTypes = method.getParameterTypes();
           if (parameterTypes.length == 1) {
                // 找 含有 Subscribe注解的方法
               Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
               if (subscribeAnnotation != null) {
                   Class<?> eventType = parameterTypes[0];
                   if (findState.checkAdd(method, eventType)) {
                        // 添加 ThreadMode
                       ThreadMode threadMode = subscribeAnnotation.threadMode();
                       // findState 添加订阅的方法
                       findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                               subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                   }
               }
      }
   }
}

EventBus.subscribe

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // 事件类型 (订阅的事件)
   Class<?> eventType = subscriberMethod.eventType;
   // 构造 Subscription (封装订阅对象及对应方法)
   Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
   // 针对eventType取对应的 CopyOnWriteArrayList【主要用来实现事件优先级接受】
   CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
   if (subscriptions == null) { // 没有,则创建 订阅集合
       subscriptions = new CopyOnWriteArrayList<>();
       // 存储 <订阅事件类型,订阅对象集合>
       subscriptionsByEventType.put(eventType, subscriptions);
   } 

   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;
       }
   }
   
   // 缓存 Map<订阅者,订阅事件集合>

   List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
   if (subscribedEvents == null) {
        // 存储
       subscribedEvents = new ArrayList<>();
       typesBySubscriber.put(subscriber, subscribedEvents);
   }
   subscribedEvents.add(eventType); // 添加订阅事件

    // 如果订阅方法是 支持sticky事件,则直接执行分发sticky事件
   if (subscriberMethod.sticky) {
        //表示是否分发订阅了响应事件类父类事件的方法
       if (eventInheritance) {
           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);
       }
   }
}

发布流程源码分析

EventBus.post

参考:深入理解ThreadLocal

public void post(Object event) {
    //通过ThreadLocal<PostingThreadState> 获取当前现场的 PostingThreadState 
   PostingThreadState postingState = currentPostingThreadState.get();
   // 通过List 存储 事件
   List<Object> eventQueue = postingState.eventQueue;
   eventQueue.add(event);
    // 是否正在分发中,没有则开始分发
   if (!postingState.isPosting) {
       postingState.isMainThread = isMainThread(); // 是否主线程
       postingState.isPosting = true; // 标记为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;
       }
   }
}

EventBus.postSingleEvent
根据eventInheritance确认是否需要分发父类Class,最终执行postSingleEventForEventType 进行事件分发

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
   Class<?> eventClass = event.getClass();
   boolean subscriptionFound = false;
   if (eventInheritance) {
        // 查询事件对象的父类对象Class
       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);
   }
}

EventBus.postSingleEventForEventType

  1. 获取订阅对象集合
  2. 遍历订阅对象,执行事件分发
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;
}

EventBus.postToSubscription
根据不同的 ThreadMode 进行处理

  1. 执行线程与目标线程匹配,则直接反射执行订阅方法
  2. 不匹配,则先将事件添加到PendingPostQueue,然后通过ExecutorService执行Runnable
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    // 根据不同的 ThreadMode 进行处理 
   switch (subscription.subscriberMethod.threadMode) {
       case POSTING:
           invokeSubscriber(subscription, event);
           break;
       case MAIN:
           if (isMainThread) {
               invokeSubscriber(subscription, event);
           } else {
               mainThreadPoster.enqueue(subscription, event);
           }
           break;
       case MAIN_ORDERED:
           if (mainThreadPoster != null) {
               mainThreadPoster.enqueue(subscription, event);
           } else {
               // temporary: technically not correct as poster not decoupled from subscriber
               invokeSubscriber(subscription, event);
           }
           break;
       case BACKGROUND:
           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);
   }
}

BackgroundPoster
以BackgroundPoster为例,进行代码分析

  1. 构建PendingPost,然后加入PendingPostQueue队列,接着调用线程执行器执行线程
  2. 线程执行,循环获取PendingPost,调用EventBus.invokeSubscriber反射执行订阅者的订阅方法

final class BackgroundPoster implements Runnable, Poster {

    ...

    // 入队
    public void enqueue(Subscription subscription, Object event) {
        // 将 订阅者和事件封装为一个 PendingPost
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost); // 入队
            if (!executorRunning) {
                executorRunning = true;
                // 调用现场执行器,执行
                eventBus.getExecutorService().execute(this); 
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    轮训取 post,1s没有则停止执行
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    // 最后执行eventBus的invokeSubscriber方法
                    eventBus.invokeSubscriber(pendingPost);
                }
            }
            ...
    }

}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343