手写EventBus框架——源码分析1

哎呀呀

最近感觉框架上非常弱鸡,于是找到了EventBus,想通过解析源码,学习架构设计最后并手写实现EventBus框架。
 那么,gogogo 先从源码查看出发,一步一步实现自己的EventBus吧;

路漫漫其修远兮

01. 手写EventBus框架——源码分析1
02. 手写EventBus框架——源码分析2
03. 手写EventBus框架——动手_整体架构设计
04. 手写EventBus框架——动手_终结

1. 订阅、取消订阅

EventBus3.0 使用方式如下:

public class SampleComponent extends Fragment  
{  
  
    @Override  
    public void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        EventBus.getDefault().register(this);  
    }  
  
    @Subscribe
    public void gogogo(param)  
    {  
    }
      
    @Override  
    public void onDestroy()  
    {  
        super.onDestroy();  
        EventBus.getDefault().unregister(this);  
    }  
      
}  

1.1 注册

1.1.1 注册源码分析

 private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
 private final Map<Object, List<Class<?>>> typesBySubscriber;
 private final Map<Class<?>, Object> stickyEvents;
 public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //获取该类所有订阅的方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //订阅方法
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
 // 缓存订阅方法
   private void subscribe(Object subscriber, SubscriberMethod subscriberMethod){
   //缓存数据 至  subscriptionsByEventType;
   //缓存数据 至  typesBySubscriber;
   //缓存数据 至  stickyEvents;
   } 

以上就是 register 所做的事情;

  • subscriberMethodFinder.findSubscriberMethods(subscriberClass) 找到所有 EventBus 匹配的方法;
  • 循环逐个调用缓存订阅方法 subscribe,最终缓存进入 subscriptionsByEventTypetypesBySubscriber

看一下如何找到匹配方法列表的;

***  SubscriberMethodFinder.class ***
    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 {
        // 使用Info取出方法  默认该方法
            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> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

以上可以看出 本质都是调用 findUsingReflection

***  SubscriberMethodFinder.class ***

   private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        //这里的  clazz  ==  subscriberClass;
        while (findState.clazz != null) {
            //找到只有一个参数的 方法集合;
            findUsingReflectionInSingleClass(findState);
            //转移到 父 Clazz
            findState.moveToSuperclass();
        }
        //这里 就是返回 findState.subscriberMethods;
        return getMethodsAndRelease(findState);
    }
   private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // 找到所有方法
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // 找到 public方法,包含继承和接口的方法
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            //判断修饰符, Public  不包含 static, abstaact
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                //判断方法数为 1
                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)) {
                    //抛异常
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                //抛异常
            }
        }
    }

这一段可以看出它的实现机制:核心原理是反射。

亮点:其中判断标识符的方式 与Android源码非常类似;

  int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
  //判断修饰符, Public  不包含 static, abstaact
  if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0)
    {
      //...
    }

1.1.2 小结——注册事件分析

注册的源码已经分析过了,来个小小的总结

流程:

  • 先找到所有 方法数量为1个且带有@Subscribe注解的方法
  • 将方法寄存进我们的缓存当中(subscriptionsByEventTypetypesBySubscriberstickyEvents

其它收获

  • CopyOnWriteArrayList 线程读写安全的列表
  • ConcurrentHashMap支持检索的完全并发性的哈希表
  • 存储的一个设计,非常巧妙。

1.2 取消注册

比较简单。缓存清除

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

2. 发布

我们平常调用时这样的

EventBus.getDefault().post(param);
//或者 
EventBus.getDefault().postSticky(param);

o.O

    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }
    
    public void post(Object event) {
       //...
    }

本质都是调用 post 方法;

2.2 post (Object event)

让我们来瞧瞧 它是什么鬼

    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };
    
    public void post(Object event) {
        //获取当前线程 的 PostingThreadState
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        //添加到消息列表
        eventQueue.add(event);

        if (!postingState.isPosting) {
            //判断是否是主线程
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            //状态改为true
            postingState.isPosting = true;
           //...
            try {
                while (!eventQueue.isEmpty()) {
                    //发送
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                //... 还原状态
            }
        }
    }

这边可以看到 先会从 currentPostingThreadState 取出状态,各种判断 然后调用postSingleEvent
postSingleEvent 这可是块硬骨头;
再难也要啃下它,哼哼...

2.3 postSingleEvent()

    private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
        // 
           //...
                // 进入 
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        //...
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            //...
            //如果没有执行,第一次会进入以下方法
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                    // 包装成 NoSubscriberEvent 方法
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
    
    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //获取  subscriptions
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
               //...
                try {
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                   //...
                }
            }
            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);
        }
    }

2.3.1 小结——Post事件分析

** 流程**

  1. 根据EventType 得到subscriptions = subscriptionsByEventType 中的CopyOnWriteArrayList<Subscription>;
  2. 循环 subscriptions,并调用postToSubscription发送 ;
  3. postToSubscription 内根据线程类型来执行方法;
  4. 发布了以后 缓存方法直接执行。

使用注意点:
从这边的分析可以看出来,post调用以后,会直接执行订阅方法,那么订阅方(Activity)不在栈顶的时候,如果做些动画展示,那是很耗性能的,那就尴尬了。。

这边梳理了整个事件的流程与原理,顺带也收获到了一些干货
细节方面没有那么深入。

下一篇: 02. 手写EventBus框架——源码分析2


希望我的文章不会误导在观看的你,如果有异议的地方欢迎讨论和指正。
如果能给观看的你带来收获,那就是最好不过了。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,275评论 25 707
  • 我每周会写一篇源代码分析的文章,以后也可能会有其他主题.如果你喜欢我写的文章的话,欢迎关注我的新浪微博@达达达达s...
    SkyKai阅读 24,892评论 23 184
  • 路漫漫其修远兮 01. 手写EventBus框架——源码分析102. 手写EventBus框架——源码分析203....
    wenld_阅读 679评论 0 0
  • 近日,《中国诗词大会》宛如飓风一般袭来,“满屏竞传飞花令,一众争说武亦姝”。 来自各行各业、年长年少的选手以诗为剑...
    靖萱z阅读 707评论 2 5
  • 第一次听到这篇短文是在一个雨后的清晨,或许记错了,但我更愿意相信那是雨后的清晨,我和孩子正在做游戏,欧阳夏丹婉转柔...
    时汝佳阅读 3,086评论 0 0