EventBus源代码深度剖析

本分析基于的代码可以在这里clone到,强烈建议边看代码边浏览本文,本文章主要分析eventbus的核心思想,因为版本差异,有一些细节可能不太一样,但我建议各位不要陷入细节无法自拔,站在较高角度去吃透它的思想才是我们的目的,因此,一些基本概念已经有很多文章已经讲过,下文不再赘述

分析之前的思考

  • eventbus的用法大家都很清楚,请看下面一种最简单的情况(一定要对照着上述地址的代码看,一些用法和当前版本的EventBus有名称上的差异),我们很清楚,在PostActivity.java中当调用了Bus.getDefault().post("Hello EventBus");这句代码后,RegisterActivity类中的onThreadEvent会被调用,而且是在子线程中.
// 在RegisterActivity.java类中的逻辑
// onCreate函数中
Bus.getDefault().register(this);
// onDestroy函数中
Bus.getDefault().unRegister(this);

//一个接收函数
    @BusReceiver(mode = EventMode.Thread)
    public void onThreadEvent(final String event) {
        Log.v(TAG, "onThreadEvent=" + Thread.currentThread().getName());
        appendLog("onThreadEvent event=" + event
                + " thread=" + Thread.currentThread().getName());
    }
 // 在PostActivity.java类中的逻辑
 // onCreate函数中
 Bus.getDefault().post("Hello EventBus");
  • 我们不妨思考一下,如果是我们自己实现,我们需要考虑哪些问题,我在下面列出了一部分我们需要考虑的核心问题
    • 我们需要保存注册类的哪些信息,该如何保存?
    • 我们需要保存参数的哪些信息,该如何保存?
    • 我们该如何确定哪些函数会被调用?
    • 我们该如何控制他们在特定的线程中运行?
    • 我们需不需要考虑缓存的功能?如何提升框架的性能?
  • 我们接下来就一个一个解决上述的问题,再强调一遍,此时你一定要有一份完整的源代码,我的分析只能作为一个引子,讲解核心的思想,之后还需要你深入代码中把它融会贯通

4个重要的数据结构

  • 标识注解结构非常基础,直接看代码,Bus.EventMode是个枚举类型,有三种值,Sender,Main,Thread,分别标识着不同的线程
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BusReceiver {
    // 默认回调的方法在主线程中执行
    Bus.EventMode mode() default Bus.EventMode.Main;
}
  • 先解决一个重要的问题,我们在存储什么?该如何存储,我们存储的是有特殊注解的方法(Method对象),这个方法中的参数,注册的对象以及相应的注解,所以我们拼装出来了第一个数据结构MethodInfo
public class MethodInfo {
    // 方法对象
    public final Method method;
    // 注册对象的Class对象
    public final Class<?> targetType;
    // 参数的Class对象
    public final Class<?> eventType;
    // 没有什么关键的作用
    public final String name;
    // 注解的mode值
    public final Bus.EventMode mode;

    public MethodInfo(final Method method, final Class<?> targetClass, final Bus.EventMode mode) {
        this.method = method;
        this.targetType = targetClass;
        this.eventType = method.getParameterTypes()[0];
        this.mode = mode;
        this.name = targetType.getName() + "." + method.getName()
                + "(" + eventType.getName() + ")";
    }

    // else codes
}
  • 再封装一层,做到最大化解耦,让MehodInfo只负责存储相应数据结构的职责,让Subscriber去做具体的执行功能,请注意invoke函数
class Subscriber {
    public final MethodInfo method;
    public final Object target;
    // 我认为下面4个成员变量是多余的,因为可以从MethodInfo中拿到
    public final Class<?> targetType;
    public final Class<?> eventType;
    public final Bus.EventMode mode;
    public final String name;

    public Subscriber(final MethodInfo method, final Object target) {
        this.method = method;
        this.target = target;
        this.eventType = method.eventType;
        this.targetType = method.targetType;
        this.mode = method.mode;
        this.name = method.name;
    }
// 使用反射调用注册类的相应函数,传递进来的event是post的内容
    public Object invoke(Object event)
            throws InvocationTargetException, IllegalAccessException {
        return this.method.method.invoke(this.target, event);
    }
    // else codes
}
  • 请注意,Subscriber只是负责了调用函数,但是没有去负责该在哪个线程中去执行,所以说,需要再封装一层,EventEmitter,它是一个Runnable对象,所以我们就可以使用Handler,Executor,控制它到底在哪个线程中执行!
public class EventEmitter implements Runnable {
    private static final String TAG = Bus.TAG;

    public final Bus bus;
    public final Object event;
    public final Subscriber subscriber;
    public final Bus.EventMode mode;
    public final boolean debug;

    public EventEmitter(final Bus bus, final Object event,
                        final Subscriber subscriber, final boolean debug) {
        this.bus = bus;
        this.event = event;
        this.subscriber = subscriber;
        this.mode = subscriber.mode;
        this.debug = debug;

    }

    @Override
    public void run() {
        try {
            if (debug) {
                Log.v(TAG, "sending event:[" + event
                        + "] to subscriber:[" + subscriber
                        + "] at thread:" + Thread.currentThread().getName());
            }
            subscriber.invoke(event);
        } catch (Exception e) {
            if (debug) {
                Log.e(TAG, "sending event:[" + event + "] to subscriber:["
                        + subscriber + "] failed, reason: " + e, e);
            }
        }
    }

    @Override
    public String toString() {
        return "{" +
                "event:[" + event +
                "] to subscriber:[" + subscriber +
                "]}";
    }

看完四个数据结构后,感叹它巧妙的设计,利用层层包装解耦,符合单一职责的原则,使结构清晰易于维护

注册时的行为

Bus.getDefault().register(this)

  • 注册时我们主要做了两件事情
  • 第一件事,以注册类的完整类名为key,它其中的所有满足条件的方法为Value,存储到一个集合中
    final static Map<String, Set<MethodInfo>> sMethodCache = new ConcurrentHashMap<String, Set<MethodInfo>>();
  • 第二件事以参数类型为key,符合的方法集合为Value存储为一个ConcurrentHashMap(以方法参数为标准分割函数)
    private final Map<Class<?>, Set<Subscriber>> mSubscriberMap;

  • 我们来看一下这个过程中的重要的方法

    //获得注册对象中所有符合条件的函数,包括父类中的
   private Set<MethodInfo> getMethods(Class<?> targetClass) {
        // 完整的类名作为缓存的key
        String cacheKey = targetClass.getName();
        Set<MethodInfo> methods;
        // 先看缓存是否有,如果没有去类中找寻符合要求的方法,然后在存储到缓存中
        synchronized (Cache.sMethodCache) {
            methods = Cache.sMethodCache.get(cacheKey);
        }
        if (methods == null) {
            /**
             * 找对象以及对象父类的符合要求的方法
             */
            methods = mMethodFinder.find(this, targetClass);
            synchronized (Cache.sMethodCache) {
                Cache.sMethodCache.put(cacheKey, methods);
            }
        }
        return methods;
    }
  • 跟入,看mMethodFinder.find(this, targetClass);的具体实现,代码很简单,请去MethodHelp.java类中查看,做一个简单的总结,此类的作用就是筛选出符合要求的Method拼装为MethodInfo集合,注意挑选的规则有以下几条(在isValidMethod函数中)
    • 首先它必须拥有BusReceiver注解,这个是大前提,之后需满足以下四个小条件
    • !Modifier.isPublic(method.getModifiers())函数必须是public的
    • Modifier.isStatic(method.getModifiers())函数不是静态的
    • method.getParameterTypes().length != 1函数只能有一个参数
    • !Modifier.isVolatile(method.getModifiers())这个条件很重要,是修复getDeclaredMethods存在的一个bug,请参考这里
    public static Set<MethodInfo> findSubscriberMethodsByAnnotation(
            final Class<?> targetClass) {
        final MethodConverter converter = new MethodConverter() {
            @Override
            public MethodInfo convert(final Method method) {
                // check annotation
                final BusReceiver annotation = method.getAnnotation(BusReceiver.class);
                if (annotation == null) {
                    return null;
                }
                if (!isValidMethod(method)) {
                    return null;
                }
                return new MethodInfo(method, targetClass, annotation.mode());
            }
        };
        return findSubscriberMethods(targetClass, converter);
    }
        /**
     * 根据传入的相应对象的class对象,去检索"那些要被回调的方法",存储起来,并且父类方法都要存储,其实就是加了一个while循环,
     * 内部再加入一个for循环
     */
    public static Set<MethodInfo> findSubscriberMethods(
            final Class<?> targetClass, MethodConverter converter) {
        Class<?> clazz = targetClass;
        final Set<MethodInfo> methods = new HashSet<MethodInfo>();
        while (!shouldSkipClass(clazz)) {
            final Method[] clsMethods = clazz.getDeclaredMethods();
            for (final Method method : clsMethods) {
                final MethodInfo methodInfo = converter.convert(method);
                if (methodInfo != null) {
                    methods.add(methodInfo);
                }
            }
            // search more methods in super class
            clazz = clazz.getSuperclass();
        }
        return methods;
    }

post时的行为

  • 存储以参数类型全称为key(如java.lang.String),此参数类型的父类型或者接口类型为value(父类型和接口类型也加入是因为考虑到了兼容性,这样不需要严格的匹配参数就可以调起函数)
    以参数类型为key的原因是因为eventbus确定调用哪个方法就是由参数类型而不是函数名确定的,所以说只要你post时的参数与你声明的回调函数的参数兼容,就可以调用!
 final static Map<String, Set<Class<?>>> sEventTypeCache = new ConcurrentHashMap<String, Set<Class<?>>>();
  • 获得了参数类型的所有兼容的类型后,一次传递需要传递的值和相应的兼容的类型
        for (Class<?> eventType : eventTypes) {
            postEventByType(event, eventType);
        }
  • 跟入postEventByType函数
    /**
     * 发送某个事件给某个特定类型的订阅者
     *
     * @param event     事件对象
     * @param eventType 匹配的事件类型
     * @param <E>       事件类型
     */
    private synchronized <E> void postEventByType(final E event, final Class<?> eventType) {
        final Set<Subscriber> subscribers = mSubscriberMap.get(eventType);
        if (subscribers == null || subscribers.isEmpty()) {
            return;
        }
        for (Subscriber subscriber : subscribers) {
            sendEvent(new EventEmitter(this, event, subscriber, mDebug));
        }
    }
  • 前面说过EventEmitter的类型和作用,继续跟入sendEvent方法,此方法的作用是根据注解的模式选择相应的类去执行emitter的run方法,此方法逻辑比较简单,请去代码中查看Schedulers类,它会用工厂模式分别创建三个子类,SenderScheduler(直接调用emitter的run方法,以同步方式执行)负责在当前线程中执行,HandlerScheduler(依赖Handler)负责在主线程中执行,ExecutorScheduler(依赖Executor)负责在异步线程中执行,到最后就都会执行到Emitter的run方法中
    public void sendEvent(EventEmitter emitter) {
        if (mDebug) {
            Log.v(TAG, "send event:" + emitter);
        }
        if (EventMode.Sender.equals(emitter.mode)) {
            mSenderScheduler.post(emitter);
        } else if (EventMode.Main.equals(emitter.mode)) {
            if (Helper.isMainThread()) {
                mSenderScheduler.post(emitter);
            } else {
                mMainScheduler.post(emitter);
            }
        } else if (EventMode.Thread.equals(emitter.mode)) {
            mThreadScheduler.post(emitter);
        }
    }
  • 看一下emitter的run方法,非常简单,到最后都会执行过来,只是过来的途径不同(上面分析的三个类负责),由下面可以看到是最终其实执行的是Subscriber的invoke方法,我们上面分析过,这个方法是用反射去调相应的method,到此就分析完毕了,最终就会成功的调用注册类中相应的方法了
    @Override
    public void run() {
        try {
            subscriber.invoke(event);
        } catch (Exception e) {}
    }

解注册时的行为

  • 不知道大家有没有想过这个问题,post时,调用哪个方法是基于参数去判断的,那么,当我们解注册的时候,如何去确定解注册的那个类中的所有方法,我们需要把这些方法从mSubscriberMap中移除,要保证精确性而不能移除错误,因此,又多出来一个数据结构去做这件事情,它的key就是我们的类对象,它的value就是我们类对象中符合方法的所有的参数对象(想想为什么把参数对象作为value,而不是把方法对象作为value,因为我们有mSubscriberMap,可以根据参数对象取出方法对象,再通过方法对象Subscriber的target属性去和我们当前要解注册的对象对比,如果一致,那么把此方法对象从mSubscriberMap中删除)
    private final Map<Object, Set<Class<?>>> mEventMap;
    public <T> void unregister(final T target) {
        if (mDebug) {
            Log.v(TAG, "unregister() target:" + target);
            mStopWatch.start("unregister()");
        }
        //mEventMap的添加元素逻辑在register方法中
        final Set<Class<?>> eventTypes = mEventMap.remove(target);
        if (eventTypes == null || eventTypes.isEmpty()) {
            Log.v(TAG, "unregister() no subscriber for target:" + target);
            return;
        }
        for (Class<?> eventType : eventTypes) {
            Set<Subscriber> subscribers = mSubscriberMap.get(eventType);
            if (subscribers == null || subscribers.isEmpty()) {
                continue;
            }
            synchronized (mSubscriberMap) {
                Iterator<Subscriber> it = subscribers.iterator();
                while (it.hasNext()) {
                    final Subscriber subscriber = it.next();
                    if (subscriber.target == target) {
                        it.remove();
                        }
                    }
                }
            }
        }
    }

缓存处理

  • events的性能瓶颈在两方面有体现.
    • 第一,就是我们去查找某个类中符合特定要求的方法并存储,因为我们不止要查找当前类,还需要查找父类,所有这是一笔不小的开销
    • 第二,我们要去把所有兼容的参数类型查找并存储,这也是一笔不小的开销
  • 根据以上的描述,我们就用了两个数据结构把他们在第一次search时缓存了起来,一个是完整的类名和对应的所有符合要求的方法,另一个是完整的参数名和所有兼容的类型
    static class Cache {

        // key=注册类的完整类名 target.getClass().getName()
        // value=注册类包含的合法的@BusReceiver方法对象集合
        // targetTypeName->method set
        final static Map<String, Set<MethodInfo>> sMethodCache =
                new ConcurrentHashMap<String, Set<MethodInfo>>();
        // key=事件类型的完整类名
        // value=事件类型的所有父类和接口
        // eventTypeName-> event type set
        final static Map<String, Set<Class<?>>> sEventTypeCache =
                new ConcurrentHashMap<String, Set<Class<?>>>();
    }
  • 它俩使用和填充位置分别在register()和unRegister()函数中,请在AndroidStudio中自己使用command + F和command + G自行查看.

可以优化的地方

  • 当我们去搜寻一个类中符合要求的方法时,其实没有必要搜寻系统级别的类的方法,比如Object类中的方法,那么我们就可以做一些小小的优化,请看下面代码,代码很简单,不做赘述
    public static boolean shouldSkipClass(final Class<?> clazz) {
        if (clazz == null || Object.class.equals(clazz)) {
            return true;
        }
        final String clsName = clazz.getName();
        return clsName.startsWith("java.")
                || clsName.startsWith("javax.")
                || clsName.startsWith("android.")
                || clsName.startsWith("com.android.");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值