手写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, CopyOnWriteArrayList> subscriptionsByEventType;
 private final Map>> typesBySubscriber;
 private final Map, Object> stickyEvents;
 public void register(Object subscriber) {
        Class subscriberClass = subscriber.getClass();
        //获取该类所有订阅的方法
        List 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, List> METHOD_CACHE = new ConcurrentHashMap<>();
    
    List findSubscriberMethods(Class subscriberClass) {
        List 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 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 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> 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 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 currentPostingThreadState = new ThreadLocal() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };
    
    public void post(Object event) {
        //获取当前线程 的 PostingThreadState
        PostingThreadState postingState = currentPostingThreadState.get();
        List 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, List>> 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 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;
  2. 循环 subscriptions,并调用postToSubscription发送 ;
  3. postToSubscription 内根据线程类型来执行方法;
  4. 发布了以后 缓存方法直接执行。

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

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

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


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

人生得意须尽欢, 桃花坞里桃花庵
点个关注呗,对,不信你点试试?

你可能感兴趣的:(手写EventBus框架——源码分析1)