EventBus v 3.0 使用和源码分析


写在前面:EventBus是在项目中用到的一个很棒的开源事件总线的框架.使用起来方便简单,封装完善,不过笔者大部分(或者说是基本上)属于拿来主义,直接实用型,很少关注底层的一些封装思想等.写这篇文章也是为了总结EventBus 3.0的一些基本使用,方便以后查看,相当于学习笔记.

在这篇文章中将有一下内容:

  • EventBus 的基本使用
  • 源码分析

cmd-markdown-logo

官网: http://greenrobot.org/eventbus/
GitHub: http://greenrobot.org/eventbus/


一. 什么是 EventBus

EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.

大体意思就是说EventBus是一个基于发布者/订阅者用于解耦的开源类库,加快开发速度,使用简单.

大概的作用就是:

  • 简化组建之间的通信
  • 降低发送者和接受者之间的耦合
  • 很好的转换activity Fragment 异步线程中的通信 (个人认为这个是最重要的)
  • 使用简单

1. 添加依赖

 dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
 }

2. 定义一个 Events

public interface Events{
    class Evnet1{}

    class Event2{}

    //....
    // 这里用接口是为了方便把所有的事件放到一起管理起来,个人习惯问题.当然也可以创建一个包专门用来存放事件类
}

3. 在Activity or Fragment 中注册EventBus

   public EventAcitvity extends Activity{

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(getLayoutResId());
            EventBus.getDefault().register(this);
        }


        @Override
        public void onStop() {
            super.onStop();
            EventBus.getDefault().unregister(this);
        }
   }

4. 在接受类(通常是Activity or Fragment)中定义一个方法

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(Events.Evnet1 event) {
    /*

        类似与handler.handlerMessage 
        通常都是在子线程中发送一个消息到handler中

        这里EventBus也是这样
        @Subscribe(threadMode = ThreadMode.MAIN)
        这个注解代表的意思就是在主线程中执行下面这段代码方法名是随便定义的 参数就是接受你要发送的那个事件类

    */
};

5. 发送事件

EventBus.getDefault().post(new Events.Evnet1());
//这段代码通常是写在异步线程中.如请求网络后需要把新数据更新到界面
//当然你也可以把注册写到Service中.通过EventBus来解决Activity和Service之间的通信问题

这样看起来使用还是很简单的.通常情况下以上的写法基本上能解决大部分的使用场景了.异步线程请求网络,发送事件,通知主线程更新UI,替换Handler的一些使用场景等.当然还有其他的一些.
也就是在接受事件的方法中,使用过EventBus之前版本的童鞋们应该会有体会.
老版本中接受方法的方法名后面是固定的.在v3.0中是可以随意的自定义,只需要在方法上加入

@Subscribe

对设计模式有过了解的童鞋们可能看到这个就想到了观察者模式,在这里笔者想说的是,就个人理解来看,这东西就是观察者模式的的一种运用方式.对发送出来的的事件进行订阅,简单来看一下他的源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {

    // 线程模式.默认是发送事件的线程
    ThreadMode threadMode() default ThreadMode.POSTING;

    //粘性
    boolean sticky() default false;

    //优先级,优先级越高,在调用的时候会越先调用。
    int priority() default 0;
}

看起来很简洁.不过核心的是 ThreadMode 这个类中定义的4个常量.它是一个美枚举类,如下

public enum ThreadMode {

    //订阅方法和发送者在同一个线程中执行,直白的来说就是,如果你调用
    第五步发送事件的方法是在子线程中执行的,那么定义的接受方法也是在子线程中执行的.
    //这样的话就不能用来直接更新UI了,否则会出现异常
    POSTING,
    // 切换到UI线程,这个很明显
    MAIN,
    /** 
     * 如果在非UI线程发布的事件,则直接执行,和发布在同一个线程中。如果在UI线程发布的 事件,则加入后台任务队列,使用线程池一个接一个调用。
     */
    BACKGROUND,
    //加入后台任务队列,使用线程池调用,注意没有BackgroundThread中的一个接一个
    ASYNC
}

注释中写的已经很明白了.这里不在赘述,具体的使用场景还是需要根据业务来判断使用那种模式.

二. 源码分析

1.首先从最开始的

EventBus.getDefault();

 /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

和平时写的懒加载单利模式一样.EventBus也是一个单利,使用双重判断和同步锁,没什么可说的

2. register

EventBus.getDefault().register(this);

public class EventBus {

//这里截取核心代码

  ....

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder
                                            .findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

注册方法中传入的this代表的是那个类,方法的参数类型是一个Object,可以看出来,任何类型都可通过这个方法注册,而且方法中第一样也表明了.需要的是这个Class对象,与旧版EventBus不同的是,这里只有这一个register方法,取消了优先级,粘性方法的重载,感兴趣的童鞋们可以看看旧版的源码就知道了.

EventBus 本身是使用了建造者模式,也就是通常的Builder.build();形式
EventBus 本身有了一套默认的参数设置,如下

    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

     EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        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;
    }
public class EventBusBuilder {
        private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

        boolean logSubscriberExceptions = true;
        boolean logNoSubscriberMessages = true;
        boolean sendSubscriberExceptionEvent = true;
        boolean sendNoSubscriberEvent = true;
        boolean throwSubscriberException;
        boolean eventInheritance = true;
        boolean ignoreGeneratedIndex;
        boolean strictMethodVerification;
        ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
        List<Class<?>> skipMethodVerificationForClasses;
        List<SubscriberInfoIndex> subscriberInfoIndexes;
        EventBusBuilder() {
        }
    }

主要是一些默认的参数设置,不过多描述.需要注意的是这里的这个默认的线程池DEFAULT_EXECUTOR_SERVICE,它是一个只有非核心线程的线程池,最大线程数为Integer.MAX_VALUE,因为这是一个非常大的一个数量,可以把他默认为无限大,线程超时的时间为60s,超过了会回收限制线程,感兴趣的童鞋可以深入了解一下.

这里需要注意的是SubscriberMethodFinder这个类,如下

class SubscriberMethodFinder {

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

        if (ignoreGeneratedIndex) {
            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 {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }
}

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        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) {
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && 
                                        (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method
                                        .getAnnotation(Subscribe.class);
                    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()));
                        }
                    }
                } 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");
            }
        }
    }

这里就是EventBus 的真正核心了 METHOD_CACHE这个一定要记住,非常重要!!!
它本身是一个map的,主要用途简单来说就是把注册的类中所有的方法都遍历一边,通过反射,寻找到具有 @Subscribe注解的方法,获取相关的参数获取后存转换成SubscriberMethod放到List中,在以这个类本身当成key存有相关方法的List作为value存放到METHOD_CACHE中,相当于一个内存缓存. 上面代码片段总 6 ~ 8 行中也充分的说明了这个,首先通过传递过来的参数获取这个类对应的说有被 @Subscribe注释的方法集合,有的话直接返回,没有的话在进行进一步的处理.
由于默认的参数都是为 true [EventBusBuilder]中的设置,忘记了回去看一下,说以直接调用findUsingReflection 进行方法的处理,获取到返回结果后,放入到METHOD_CACHE 直接返回处理过后的方法集合
findUsingReflectionInSingleClass这个方法中是对被注解类的方法进行反射遍历处理等.

3. unRegister(this)

EventBus.getDefault().unregister(this);
    /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: "
                        + subscriber.getClass());
        }
    }

取消注册,基本上就是属于遍历,移除之类的.没什么好解释的.

4 post 发送事件

EventBus.getDefault().post(new Events.Evnet1());
/** 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;
            }
        }
    }

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


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

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

    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 {
                    mainThreadPoster.enqueue(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);
        }
    }

    /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
    private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
        synchronized (eventTypesCache) {
            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
            if (eventTypes == null) {
                eventTypes = new ArrayList<>();
                Class<?> clazz = eventClass;
                while (clazz != null) {
                    eventTypes.add(clazz);
                    addInterfaces(eventTypes, clazz.getInterfaces());
                    clazz = clazz.getSuperclass();
                }
                eventTypesCache.put(eventClass, eventTypes);
            }
            return eventTypes;
        }
    }

大概就是register 时候是把方法存放好,等待调用.那么Post基本上就是重中取到相对应的方法,然后调用
PostingThreadState 是保存在ThreadLoacl中的参数.主要是为了区别是哪一个线程中调用或者保存的,不严谨的说ThreadLoacl就是分别为多个线程保存那个线程中存在的状态或者数据的.比如我有3个线程

        ThreadLoacl<Boolean> local = new ThreadLoacl();

        new Thread(new Runnable() {
            @Override
            public void run() {
                local.put("key","true")
            }
        },"Thrad1").start();
         new Thread(new Runnable() {
            @Override
            public void run() {
                local.put("key","false")
            }
        },"Thrad2").start();
         new Thread(new Runnable() {
            @Override
            public void run() {
                local.put("key","true")
            }
        },"Thrad3").start();

然后分别打印结果,分别是true false true 他们之间是互相不影响的,具体不详细描述了,感兴趣的话可以去搜索看看.
然后在判断当前的线程,这个事件是否在发送等等. 剩下的就交给了postSingleEvent方法
这个方法中大概就是属于根据你传入的事件类去进行匹配. 去register中的那个METHOD_CACHE中匹配,然后还是通过反射执行对应的方法
postToSubscription方法中是进行线程的判断 ThreadMode中的那4个类型

case MAIN:
首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。
case BACKGROUND:
如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用
executorService = Executors.newCachedThreadPool();。
case ASYNC:
将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。


最后

到这里.基本上就解析的差不多了,当然还有很多细节问题没有详细说明,但这篇文章相当于分析的总结之做,写的时候又重新过了一边.加深了一下印象,希望忘记的不会那么快,同时也是想到那个做了一次笔记.重要的是还需要 RTFS 多么xxx 的一句话啊..

参考资料

Android EventBus源码解析带你深入理解EventBus 需要说明一下的是这个版本比较老.实现和使用的细节可能有差别,但是具体流程,思想核心基本上不变.对我的学习分析有很大帮助,在此感谢作者带来这么详细的分析.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值