Android EventBus框架(二)之源码简单解析

上一篇,我们基本知道了EventBus的使用步骤,接下来先简单研究一下其中的源码。在分析源码之前,我们先回顾一下Java反射的知识点:

JAVA反射机制

基本定义:

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Sun为提供的关于反射机制中的类:

java.lang.Class;

java.lang.reflect.Constructor;

java.lang.reflect.Field;

java.lang.reflect.Method;

java.lang.reflect.Modifier;

我们可以通过反射机制访问java对象的属性,方法,构造方法等。

(1)getDeclaredMethods()

获取所有的方法

(2)getDeclaredMethod(“方法名”,参数类型.class,……)

获得特定的方法

(3)getFields()则是返回类型中的所有公有属性

(4)getDeclaredField( )

获得特定的属性

JAVA反射的常规使用步骤

(1)得到要调用类的class

(2)得到要调用的类中的方法(Method)

(3)方法调用(invoke)

如下:

//实体类
public class Student {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }  

    public static void toString(int age,String name){
        System.out.println("大家好,我叫"+name+",今年"+age+"岁");
    }  

//通过反射调用toString()方法
Class cls = Class.forName("chb.test.reflect.Student");
Method m = cls.getDeclaredMethod("toString",new Class[]{int.class,String.class});
m.invoke(cls.newInstance(),20,"chb")

反射的基本知识我们知晓了,再来看看EventBus的一些基本知识:

首先对EventBus的有一个大局的认识,参考官方的介绍图如下:

EventBus中的观察者通常有四种订阅函数

(就是某件事情发生被调用的方法)

(1)、onEvent

(2)、onEventMainThread

(3)、onEventBackground

(4)、onEventAsync

这四种订阅函数都是使用onEvent开头的,它们的功能稍有不同,在介绍不同之前先介绍两个概念:

(1)告知观察者事件发生时通过EventBus.post函数实现,这个过程叫做事件的发布;

(2)观察者被告知事件发生叫做事件的接收,是通过下面的四种订阅函数实现的。

1 onEvent:如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
2 onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
3 onEvnetBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
4 onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.

源码分析

我们分析源码先从程序的入口开始,EventBus的入口是注册时开始, 即

EventBus.getDefault().register(this);

EventBus.getDefault()返回的是一个EventBus的实例,是一个单例模式:

static volatile EventBus defaultInstance;

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

这里实现单例采用了双重校验模式,提高的效率,也能防止并发可能产生的问题,同时defaultInstance采用了volatile 来修饰,避免了编译期相关值的修改。

然后看看注册方法:register

 public void register(Object subscriber) {
 //这里subscriber一般是this,在上篇文章中就指MainActivity.this
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);//findSubscriberMethods()顾名思义就是通过类名去找类中的方法
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

我们再来看看findSubscriberMethods(subscriberClass)方法:

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        //其中METHOD_CACHE是一个缓存的ConcurrentHashMap,Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();该Map以订阅的类名为key,订阅函数列表为value;

        if (subscriberMethods != null) {
            return subscriberMethods;
            //首先判断缓存中是否已经包含该订阅类(即是否已经订阅过);
        }
//第一次进来subscriberMethods肯定是Null
        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;
        }
    }

执行findUsingReflection(subscriberClass)后,会去执行findUsingReflectionInSingleClass方法,我们再看看####findUsingReflectionInSingleClass方法:

private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            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();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                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()));
                        }
                    }
                } 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订阅者的注册;

我们回到register方法:

for (SubscriberMethod subscriberMethod : subscriberMethods) {
           subscribe(subscriber, subscriberMethod, sticky, priority);
       } 

对每一个订阅方法,对其调用subscribe方法,进入该方法看看到底干了什么:

subscribe方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
        subscribed = true;
        //从订阅方法中拿到订阅事件的类型
        Class<?> eventType = subscriberMethod.eventType;
        //通过订阅事件类型,找到所有的订阅(Subscription),订阅中包含了订阅者,订阅方法
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //创建一个新的订阅
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
        //将新建的订阅加入到这个事件类型对应的所有订阅列表
        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);
                }
            }
        }  

        //根据优先级插入订阅
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }
        //将这个订阅事件加入到订阅者的订阅事件列表中
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<Class<?>>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
        //这个是对粘性事件的,暂时不讨论
        if (sticky) {
            Object stickyEvent;
            synchronized (stickyEvents) {
                stickyEvent = stickyEvents.get(eventType);
            }
            if (stickyEvent != null) {
                postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
            }
        }
    } 

好了,到这里差不多register方法分析完了,大致流程就是这样的,我们总结一下:

1、找到被注册者中所有的订阅方法。

2、依次遍历订阅方法,找到EventBus中eventType对应的订阅列表,然后根据当前订阅者和订阅方法创建一个新的订阅加入到订阅列表

3、找到EvnetBus中subscriber订阅的事件列表,将eventType加入到这个事件列表。

所以对于任何一个订阅者,我们可以找到它的 订阅事件类型列表,通过这个订阅事件类型,可以找到在订阅者中的订阅函数。

register分析完了就分析一下post吧,这个分析完了,EventBus的原理差不多也完了…

post(Object event)

public void post(Object event) {
        //这个EventBus中只有一个,差不多是个单例吧,具体不用细究
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        //将事件放入队列
        eventQueue.add(event);  

        if (postingState.isPosting) {
            return;
        } else {
            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;
            }
        }
    }  

ost里面没有什么具体逻辑,它的功能主要是调用postSingleEvent完成的,进入到这个函数看看吧:

postSingleEvent(Object event, PostingThreadState postingState)

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
       Class<? extends Object> eventClass = event.getClass();
    //找到eventClass对应的事件,包含父类对应的事件和接口对应的事件
       List<Class<?>> eventTypes = findEventTypes(eventClass);
       boolean subscriptionFound = false;
       int countTypes = eventTypes.size();
       for (int h = 0; h < countTypes; h++) {
           Class<?> clazz = eventTypes.get(h);
           CopyOnWriteArrayList<Subscription> subscriptions;
           synchronized (this) {
            //找到订阅事件对应的订阅,这个是通过register加入的(还记得吗....)
               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;
           }
       }
    //如果没有订阅发现,那么会Post一个NoSubscriberEvent事件
       if (!subscriptionFound) {
           Log.d(TAG, "No subscribers registered for event " + eventClass);
           if (eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
               post(new NoSubscriberEvent(this, event));
           }
       }
   }  

这个方法有个核心方法 postToSubscription方法,进入看看吧

postToSubscription

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //第一个参数就是传入的订阅,第二个参数就是对于的分发事件,第三个参数非常关键:是否在主线程
        switch (subscription.subscriberMethod.threadMode) {
        //这个threadMode是怎么传入的,仔细想想?是不是根据onEvent,onEventMainThread,onEventBackground,onEventAsync决定的?
        case PostThread:
            //直接在本线程中调用订阅函数
            invokeSubscriber(subscription, event);
            break;
        case MainThread:
            if (isMainThread) {
                //如果直接在主线程,那么直接在本现场中调用订阅函数
                invokeSubscriber(subscription, event);
            } else {
                //如果不在主线程,那么通过handler实现在主线程中执行,具体我就不跟踪了
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case BackgroundThread:
            if (isMainThread) {
                //如果主线程,创建一个runnable丢入线程池中
                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);
        }
    } 

小结

直接反射调用;也就是说在当前的线程直接调用该方法;

(1)case MainThread:

首先去判断当前如果是UI线程,则直接调用;否则: mainThreadPoster.enqueue(subscription, event);把当前的方法加入到队列,然后直接通过handler去发送一个消息,在handler的handleMessage中,去执行我们的方法。说白了就是通过Handler去发送消息,然后执行的。

(2)case BackgroundThread:

如果当前非UI线程,则直接调用;如果是UI线程,则将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用

executorService = Executors.newCachedThreadPool();。

(3) case Async:将任务加入到后台的一个队列,最终由Eventbus中的一个线程池去调用;线程池与BackgroundThread用的是同一个。

这么说BackgroundThread和Async有什么区别呢?

BackgroundThread中的任务,一个接着一个去调用,中间使用了一个布尔型变量handlerActive进行的控制。

Async则会动态控制并发。

因此,从完整的源码分析来看:register就是把当前类中匹配的方法,存入一个map,而post会根据实参(订阅事件的类型(由类名决定))去map查找进行反射调用。其实如果不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家提起EventBus,也可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。

参考致谢:

1 http://blog.csdn.net/yuanzeyao/article/details/38174537

2 http://blog.csdn.net/lmj623565791/article/details/40920453

时间: 2024-12-23 13:43:17

Android EventBus框架(二)之源码简单解析的相关文章

ngx lua模块源码简单解析

ngx lua模块源码简单解析分类: nginx 2014-07-11 11:45 2097人阅读 评论(0) 收藏 举报nginxlua数据结构架构目录(?)[+]对nginx lua模块的整个流程,原理简单解析.由于nginx lua模块相关配置,指令,API非常多,所以本文档只以content_by_lua指令举例说明. 读本文档最好配合读源码. 不适合对nginx和lua一点都不了解的人看.1.相关配置详细配置见 https://github.com/openresty/lua-ngin

springmvc(2)Controller源码简单解析

前面简单的分析了一下DispatcherServlet,接下来分析一下Controller,在web的MVC中,Controller就是其中的C,启动的一些页面逻辑处理,页面映射的功能: 首先看看超类: public interface Controller {//处理请求,最后返回一个ModelAndView对象,这里的ModelAndView就是我们前面分析过:在DispatchServlet中的doDispath()这个方法里面//会通过render方法得到ModelAndView对象,如

spring bean源码简单解析

最近在看spring的源码,发现看这个还是有点早,看的很吃力,有多东西还不是很明白,像代理等, 我感觉spring用abstract模板来写主要功能,用接口来拓展功能,用的出神入化,但也让很多简单 的东西变得不那么好懂了,就是写的啰嗦了,个人感觉.下面就是下spring bean源码的学习: private static final Resource RETURNS_NULL_CONTEXT = qualifiedResource(CLASS, "returnsNull.xml");

Android属性动画AnimatorSet源码简单分析

跟上之前的两篇文章 Android属性动画ValueAnimator源码简单分析 Android属性动画ObjectAnimator源码简单分析 继续看AnimatorSet源码的大概过程. AnimatorSet 提供了一种把多个动画放到一起,按照某种特定的顺序来播放,比如一个接一个的播放或者多个动画一起播放. AnimatorSet简单使用随便举一个最简单的例子 //AnimatorSet AnimatorSet animSet = new AnimatorSet(); ObjectAnim

Android属性动画ValueAnimator源码简单分析

Android开发的过程中经常要用到属性动画,经常都是网上扒下来看下怎么用,但是经常不知道为什么要这么用,手一哆嗦一不小心就点到源码里面去了.我们就来看看Android属性动画ValueAnimator类源码的简单实现,从而对ValueAnimator类有个大概的了解. 在Android开发过程中做动画效果的时候用到ValueAnimator的时候最简单的方法我们是这么干的 // ValueAnimator ValueAnimator valueAnimator = ValueAnimator.

Android FM模块学习之四源码解析(二)

上一章我们了解了FM主activity:FMRadio.java,若没查看的,请打开链接Android FM模块学习之四源码解析(一) 查看fmradio.java源码注释.接下来我们来看看FM重要的一个类:FMRadioService.java 由上一章我们已经知道,打开FM时,在OnStart函数中会bindToService来开启服务, public boolean bindToService(Context context, ServiceConnection callback) { L

《Android源码设计模式解析》读书笔记——Android中你应该知道的设计模式

断断续续的,<Android源码设计模式解析>也看了一遍,书中提到了很多的设计模式,但是有部分在开发中见到的几率很小,所以掌握不了也没有太大影响. 我觉得这本书的最大价值有两点,一个是从设计模式的角度去理解Android源码,结合着日常开发中的常用类,对设计模式的理解会更加的深刻:另外一个好处就是了解常用模式,再看其他人写的代码的时候,更容易理解代码思路.下面是我的读书笔记和一些思考,设计模式只整理我认为重要的部分. 建造者模式 建造者模式最明显的标志就是Build类,而在Android中最常

Android应用Preference相关及源码浅析(Preference组件家族篇)

1 前言 前一篇(点我阅读前一篇<Android应用Preference相关及源码浅析(SharePreferences篇)>)我们讨论分析使用了Android的SharePreferences,相信看过的朋友都有了自己的感悟与理解,这一篇我们继续乘热打铁来说说SharePreferences的衍生品--Preference组件. 其实Preference组件大家一定不陌生,因为Android系统的Setting应用及我们市面上一些符合Android设计思想的应用的设置界面一般都会用它来实现,

spark版本定制五:基于案例一节课贯通Spark Streaming流计算框架的运行源码

本期内容: 1.在线动态计算分类最热门商品案例回顾与演示 2.基于案例贯通Spark Streaming的运行源码 一.在线动态计算分类最热门商品案例回顾与演示 案例回顾: package com.dt.spark.sparkstreaming import com.robinspark.utils.ConnectionPool import org.apache.spark.SparkConf import org.apache.spark.sql.Row import org.apache.