Android框架源码分析——EventBus

1、介绍篇

关于EventBus的介绍和使用,此处只做基本介绍,很多开发者都很熟悉其使用方法,也尝到了EventBus在开发中的便捷之处,关于EventBus的使用和源码笔者也是早有接处,本文主要是针对其源码进行分析,带你一起探索EventBus的代码细节

  • EventBus三要素
  1. Event 事件:可以是任意类型,既然为事件一定伴随着传递和操作,在使用中扮演信息和事件载体
  2. Subscriber 事件订阅者:这里的订阅着和RxJava中作用一样负责监听某种状态,在状态和条件改变时回调处理,在EventBus3.0之前所有的订阅方法必须是onEvent开头,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,这些方法在方法名中就确定了执行的线程;而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且传递参数指定线程模型,默认是POSTING。
  3. Publisher 事件的发布者:我们可以在任意线程里发布事件即发布Event,携带信息的EvValue(ent会找到订阅的方法执行;一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可;
  • EventBus3.0有四种线程模型
  1. POSTING (默认): 表示事件处理函数的线程跟发布事件的线程在同一个线程即谁发谁执行
  2. MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作;
  3. BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作;如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程
  4. ASYNC 表示无论事件发布的线程是哪一个,事件都会被单独的线程池处理
  • 使用介绍
  1. 定义事件Event
  data class UserEvent(val id: Int, var name: String, var age: Int)
  1. 注册解除注册
EventBus.getDefault().register(this)
EventBus.getDefault().unregister(this)
  1. 编写订阅的方法的方法,并添加@Subscribe注解,注解方法必须要是Public、非静态、非抽象的、只能有一个参数,否则会收到EventBusException
@Subscribe
public fun toast(userEvent: UserEvent) {
    Toast.makeText(this, "${userEvent.id}  ${userEvent.name}  ${userEvent.age}", Toast.LENGTH_SHORT).show()
}
  1. 发送事件
EventBus.getDefault().post(UserEvent(1,"AAAA",30)) 
EventBus.getDefault().postSticky(UserEvent(1,"AAAA",30)) 

2、EventBus源码分析

对源码的分析还遵循着之前的以Arouter为例谈谈学习开源框架的最佳姿势中讲解的开源框架的学习姿势进行分析,主要从一下几个方面:

  1. 整体执行流程
  2. 如何注册和解除注册观察者
  3. 如何获取和保存观察者和订阅的事件信息
  4. 如何发送事件和接收事件
  5. 如何处理sticky事件
2.1整体执行流程

EventBus时序图
上面为EventBus源码的时许图,为了体现重要的细节画的有点乱,这里先根据时许图用通俗的话简单介绍下工作过程方便后面的理解,首先Client的注册EventBus时,EventBus会先登记你观察什么事件和执行什么方法,当这个事件发送到EventBus时,EventBus查找登记的方法反射调用执行即可;

2.2如何注册和解除注册观察者
  • 注册过程
  1. EventBus.getDefault():以单利的形式初始化并对外提供EventBus实例
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}
  1. register(Object : subscribe):注册观察者
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();  //获取观察者Class
    //获取观察者类中所有的注解方法,保存在List中
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) { //循环执行subscribe()方法
            subscribe(subscriber, subscriberMethod);
        }
    }
}
  1. 保存观察者、事件类型、订阅方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //封装信息
    // 根据监听的事件类型从缓存中获取保存的CopyOnWriteArrayList<Subscription>
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>(); //缓存中不存在则创建并保存subscriptions
        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;
        }
    }
     //从typesBySubscriber中获取缓存的贯彻的事件类型,如果不存在则创建并保存观察者和观察的事件类型
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);
}

简单的总结注册流程:

  1. 根据观察的Event类型从缓存中获取此Event类型的观察方法集合,如果存在则添加此次的SubscriberMethod,如果不存在则创建集合保存SubscriberMethod,并缓存此Event类型的集合
  2. 遍历集合中的SubscriberMethod,按照优先级降序排列
  3. 根据Subscribe实例从缓存中获取观察的Event类型集合,如果存在则添加,否则创建集合并添加缓存
  4. subscriber 保存对应的 eventType ,eventType 保存对应的订阅方法
  • 取消注册观察者
  1. 取消注解相对简单,主要过程在注释中说明了,总结起来就是两句话,线根据取消的Subscribe获取其注册的所有时间类型,然后根据事件类型移除所有的Sucribe对象;
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //根据事件类型获取所有的订阅方法集合
    if (subscriptions != null) {
        int size = subscriptions.size();
        for (int i = 0; i < size; i++) { //遍历集合
            Subscription subscription = subscriptions.get(i);
            if (subscription.subscriber == subscriber) {  //判断方法的观察者是不是要解除的观察者
                subscription.active = false; //设置不活跃状态
                subscriptions.remove(i); //移除观察信息
                i--;
                size--;
            }
        }
    }
}

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); //从缓存中获取Subscrober所观察的事件类型集合
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) { //循环遍历集合执行unsubscribeByEventType()
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber); //移除观察者
    } 
}
2.3获取和保存观察者和订阅信息
  1. 关于保存在注册中已经介绍了,主要使用subscriptionsByEventType和typesBySubscriber,保存每个Event类型对应的观察信息和每个观察者观察的所有Event类型,subscriptionsByEventType主要用于发送事件时获取观察者并调用观察方法,typesBySubscriber主要用于结出注解时确定需要移除哪些事件类型和哪些方法;
  2. 下面看一下获取注册的观察者方法,方法的获取是在register()中开始的,调用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);
    }
    .......
        METHOD_CACHE.put(subscriberClass, subscriberMethods); //缓存获取到的信息
        return subscriberMethods;
    }
}

这里的操作基本框架中都会见到,首先根据Class从METHOD_CACHE中获取,若不存在则反射遍历Class获取所有符合条件的方法,将结果缓存在METHOD_CACHE中,这里使用METHOD_CACHE缓存是防止每一次注册时都需要反射获取,然后根据ignoreGeneratedIndex的值分别去加载观察方法,这里主要是是否使用索引查找,关于索引会在下一篇单独介绍,这里知道调用反射获取就好了;

  1. findUsingReflectionInSingleClass(FindState findState):反射获取并保存注解方法,方法看上去复杂其实是纸老虎,简单来说就是遍历所有方法然后判断符不符合上面的方法要求,并将符合的封装在SubscriberMethod中,返回所有方法的集合,具体细节看注释细节
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        methods = findState.clazz.getDeclaredMethods(); //反射获取所有方法
    } 
    for (Method method : methods) { //遍历方法
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //判断修饰符Public
            Class<?>[] parameterTypes = method.getParameterTypes(); //获取参数集合
            if (parameterTypes.length == 1) { 判断参数长度是否为1
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); //获取并判断Subscribe注解
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0]; //取出参数类型,即观察的事件类型
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode(); //获取直接的执行线程
                        //封装所有信息在SubscriberMethod中,并保存在List<SubscriberMethod>中
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } 
    }
}
2.4 执行发送事件和接收事件
  1. PostingThreadState:保存事件对列、线程信息及订阅的Subscription实例,PostingThreadState保存在ThreadLocal中,所以每个线程有单独的实例,即每个线程中有单独的事件对列,彼此不受影响
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>(); //当前线程的事件对列
    boolean isPosting; //是否正在发送事件
    boolean isMainThread; //是否为主线程
    Subscription subscription; //观察者信息
    Object event;  //事件
    boolean canceled;
}
  1. post(Object event):发送Event事件,发送事件后首先获取当前线程的PostingThreadState,并将事件Event添加到内部对列中,并根据Looper信息判断是否为主线程,然后根据PostingThreadState的发送事件
public void post(Object event) {
   //获取当前线程的事件对列,保存发送的事件
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) { //判断当前线程是否正在发送
        postingState.isMainThread = isMainThread();  // 赋值是否为主线程
        postingState.isPosting = true;
        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState); //循环便利对列,取出头部事件执行
            }
        } 
    }
}
  1. postSingleEvent(Object event, PostingThreadState postingState)
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass(); //获取发送事件的Class
    boolean subscriptionFound = false;
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); //获取当前类和其父类的集合
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) { //遍历所哟Class集合执行postSingleEventForEventType()
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); 
        }
    } else {  //对于单独的Event直接执行postSingleEventForEventType
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
}

上述代码根据发送的Event是否有父类进行分别处理,对于存在父类或接口的调用lookupAllEventTypes()获取所有Class的集合,然后遍历集合中的Class分别执行发送事件,也就是不仅发送自己类型的Event页发送所有父类的event;对于单一的Event直接发送事件;

  1. postSingleEventForEventType():根据发送的Event类型,从subscriptionsByEventType中获取所有注册观察此Event类型的Subscription实例,从而获取其保存的观察者和观察方法,并调用postToSubscription()执行处理事件
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass); //从缓存中获取Subscription集合
    }
    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;
            } 
        }
        return true;
    }
    return false;
}
  1. postToSubscription():根据发送事件的线程和订阅方法设定的线程分别处理事件,具体细节见注释
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event); //POSTING默认在同一线程执行,所以直接执行事件
            break;
        case MAIN:  // 对于指定Main线程方法,如果发送事件在Main线程直接执行,否则调用mainThreadPoster加入主线程对列
            if (isMainThread) {
                invokeSubscriber(subscription, event); 
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED: //在主线程中排队执行
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                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);
    }
}

整个EventBus的执行流程最复杂的地方就是事件的发送,但总的来说还是很好理解的,在发送事件后从缓存的观察方法中找到事件的监听方法,然后根据二者的线程要求选择合适的执行线程,然后反射执行方法即可,对于整个过程可能会有几个疑问,比如:isMainThread在什么时候设定的?mainThreadPoster是什么?为何可以切换线程?. backgroundPoster、asyncPoster如何实现功能?针对这几个对象都是在EventBus初始化时设置好的,下面简单分析下几个疑问:

  1. isMainThread在什么时候设定的?答案:在post()发送事件时直接判断
postingState.isMainThread = isMainThread();  //Post方法中调用isMainThread()
private boolean isMainThread() {
    return mainThreadSupport != null ? mainThreadSupport.isMainThread() : true;
}
@Override
public boolean isMainThread() {
    return looper == Looper.myLooper(); // 此处的looper在创建传入的Looper.getMainLooper()
}
  1. mainThreadPoster是什么?为何可以切换线程?
    答:mainThreadPoster是在初始化EventBus时创建的HandlerPoster,HandlerPoster是使用主线程对列初始化Handler实例,内部封装了EventBus、主线程对列,在添加Event事件时发送信息到Handler中,在handleMessage()中调用eventBus.invokeSubscriber()执行观察方法
public Poster createPoster(EventBus eventBus) {
    return new HandlerPoster(eventBus, looper, 10);
}
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
    super(looper);
    this.eventBus = eventBus;
    this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
    queue = new PendingPostQueue(); //初始化主线程中的对列
}
queue.enqueue(pendingPost); //加入事件对列
if (!handlerActive) {
    handlerActive = true;
    if (!sendMessage(obtainMessage())) { //发送事件
        throw new EventBusException("Could not send handler message");
    }
}
@Override
public void handleMessage(Message msg) {
       eventBus.invokeSubscriber(pendingPost); //最终还是调用eventBus.invokeSubscriber()
}
  1. backgroundPoster、asyncPoster如何实现功能?
    答:backgroundPoster和asyncPoster原理一样,他们都是Runnable的字类,在内部保存事件对列,添加事件后会调用线程池执行发送事件
class AsyncPoster implements Runnable, Poster {
    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); //调用线程池执行Runnable
    }
    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        eventBus.invokeSubscriber(pendingPost); //取出事件调用 eventBus.invokeSubscriber()发送事件
    }
}
2.5、处理Sticky的Event
  1. postSticky():EventBus内部保存着每个粘性事件,发送粘性事件时首先被保存到缓存中去,然后按照正常事件发送出去,因为粘性事件的本身还是事件
public void postSticky(Object event) {
    synchronized (stickyEvents) {
        stickyEvents.put(event.getClass(), event); //保存发送的Event事件
    }
    post(event); //按正常流程发送事件
}

2.粘性事件的接收, 在注册观察粘性事件时,如果Subscribe注解中标名接受粘性事件,则直接获取缓存中匹配的粘性事件并直接发送,所以在观察粘性事件时只要注册成功立刻会执行方法

if (subscriberMethod.sticky) { 如果注册的方法为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)) { //遍历stickyEvents集合,获取符合的Event发送事件
                Object stickyEvent = entry.getValue();
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    } else {
        Object stickyEvent = stickyEvents.get(eventType);
        checkPostStickyEventToSubscription(newSubscription, stickyEvent); //最终调用postToSubscription()处理事件
    }
}

3、EventBus飙车神技——索引

在上面的分析中我们知道,EventBus使用findUsingReflectionInSingleClass()通过反射获取每个观察者的注解方法,用官方的话说早期是因为效率所以选择反射放弃注解,EventBus 3.0后使用注解生成索引文件,在加载时不需要使用反射更好的提高了EventBus的效率;

3.1、索引的使用
  • 引入EventBus的注解
    因为使用Kotlin开发,所以这里使用Kapt引入,其他的使用方式可去官方查看点击查看
implementation 'org.greenrobot:eventbus:3.1.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1'
  • 在build.gradle中配置索引文件,这里配置的信息会作为最后生成的Index文件的包名和类名
kapt {
    arguments {
        arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')
    }
}
  • 实例化EventBus
EventBus.builder().addIndex(new MyEventBusIndex()).ignoreGeneratedIndex(false).installDefaultEventBus();
  • 创建事件的观察方法
    @Subscribe
	public void action(TypeEvent event) {
	}
	
	@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
	public void actionTwo(TypeEvent event) {
	}
  • 查看生成的MyEventBusIndex
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; // Key = Class Value= SubscriberInfo

    static { . //初始化HashMap保存信息
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); 
        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("action", TypeEvent.class), //封装信息到SimpleSubscriberInfo中并保存
            new SubscriberMethodInfo("actionTwo", TypeEvent.class, ThreadMode.MAIN, 0, true)
        }));
    }
    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);  //保存到HashMap中
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

注解生成类中,首先创建Map集合用于保存所有的注解方法,在static代码块中将注解方法名、事件类型、线程信息、优先级、是否为粘性事件都封装在SubscriberMethodInfo中,然后将一个类中的创建的所有SubscriberMethodInfo保存在集合中,最后集合封装在SimpleSubscriberInfo中,并以class为Key保存在在Map中;

3.2、根据索引查找注解方法

EventBus.builder().addIndex(new MyEventBusIndex())方法中,将MyEventBusIndex实例保存在subscriberInfoIndexes中,然后在创建EventBus时使用subscriberInfoIndexes索引创建SubscriberMethodFinder的实例,通过之前的学习知道SubscriberMethodFinder负责查找所有的注册方法,在SubscriberMethodFinder.findSubscriberMethods()中如果设置ignoreGeneratedIndex为false,则执行findUsingInfo(),下面看看findUsingInfo()是如何获取注解类中保存的方法的

  • findUsingInfo()
while (findState.clazz != null) {
    findState.subscriberInfo = getSubscriberInfo(findState); //获取SubscriberInfo信息
    if (findState.subscriberInfo != null) {
        SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); //获取Method集合
        for (SubscriberMethod subscriberMethod : array) { //遍历Method集合
            if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                findState.subscriberMethods.add(subscriberMethod); //直接保存Method
            }
        }
    } else {
        findUsingReflectionInSingleClass(findState);//没找到的继续使用反射查找
    }
}

从上面的代码中看到首先从getSubscriberInfo获取subscriberInfo,可以推测到这里获取的subscriberInfo就是前面注解类中创建并实例化的SimpleSubscriberInfo,然后获取其中保存的SubscriberMethod集合,遍历集合中的SubscriberMethod保存在findState.subscriberMethods并返回,这样就获取到所有的注解方法集合了,下面看一下getSubscriberInfo如何获取的。

  • getSubscriberInfo(findState)
if (subscriberInfoIndexes != null) {
    for (SubscriberInfoIndex index : subscriberInfoIndexes) {  //遍历传入的subscroberInfoIndexes集合
 SubscriberInfo info = index.getSubscriberInfo(findState.clazz); //从MyEventBudIndex中保存的集合中获取SubscriberInfo实例
        if (info != null) {
            return info;
        }
    }
}

代码逻辑很简单,上面是使用class为Key缓存的这里遍历所有的subscroberInfoIndexes然后以class为键取出SubscriberInfo实例,到此注解生成类和解析方法的过程就分析完毕了,在编译时就创建并保存好所有方法在执行时只需要直接获取即可。

3.3、查看EventBusAnnotationProcessor
@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe”) //声明处理的注解类型
@SupportedOptions(value = {"eventBusIndex", "verbose”}) //配置的索引文件的参数
public class EventBusAnnotationProcessor extends AbstractProcessor {
    public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
    public static final String OPTION_VERBOSE = "verbose";

    private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>(); //保存类中的方法
    private final Set<TypeElement> classesToSkip = new HashSet<>(); //保存跳过的不符合要求的方法
    String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);  // 获取build.gradle中配置的文件信息
    collectSubscribers(annotations, env, messager);  //获取类中的所有注解方法,保存在methodsByClass中
    checkForSubscribersToSkip(messager, indexPackage);  //跳过不符合条件的方法保存在classesToSkip中
    createInfoIndexFile(index); //创建类文件
}

EventBusAnnotationProcessor中使用APT根据注解生成代码,整个类中的方法不多,出去判断和写入的逻辑后上面为主要的流程代码,真个过程分四步,其余细节可自行查看源码:

  1. 获取build.gradle中配置的文件信息并解析包名和类名
int period = index.lastIndexOf('.');
String myPackage = period > 0 ? index.substring(0, period) : null; //解析包名
String clazz = index.substring(period + 1); //解析类名
  1. 获取类中的所有注解方法,保存在methodsByClass中
  2. 找出不符合条件的方法保存在classesToSkip中,主要过滤修饰符和参数
  3. 创建类文件并写入数据

4、EventBus框架学习

4.1、设计模式
  • Builder设计模式:像很多框架一样EventBus采用Builder模式配置和初始化EventBus
  • 单例模式:单例模式对外提供,保证程序中只有一个实例
  • 观察者模式:整个EventBus的设计基础就是观察者模式,通过注册观察方法订阅事件,在事件发生时响应订阅方法
4.2、细节实现
  • 观察方法和观察类的缓存
    在EventBus中使用subscriptionsByEventType以EventType为Key缓存每种事件类型对应的所有观察者和观察方法;再使用typesBySubscriber以Subscriber为Key保存每个观察者观察的事件类型,简单的说就是Subscriber --> EventType --> fun() 这样将所有的方法、观察者、事件类型联系起来,在发送事件时和注销时查找都很便捷

  • ThreadLocal区分每个线程

在处理事件的每个线程上都有各自的ThreadLocal 实例,在ThreadLocal中创建PostingState实例用于保存每个线程的信息,这样在事件发送时线程等信息就会伴随事件,并根据线程确定处理方式,线程彼此互布影响。

  • 执行线程的切换

在EventBus的内部保存着主线程handler、backgroundPoster、asyncPoster实例,并且每个实例中保存着各自的对列,在事件发生时根据发送事件的线程和注解方法的信息,将事件加入到各自的对列中然后各自执行事件。

对EventBus的学习就到此结束了,本篇文章也是对之前笔记的整理,希望对学习EventBus和学习框架的同学有所帮助!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值