通过EventBus的源码理解注解的使用(一)

注解

注解的基础定义,很多文章都有了,在这里就不总结了。

推荐一个文章大致了解:
https://blog.csdn.net/niubitianping/article/details/60145128

之前的疑惑

之前看过一些文章,说注解IOC框架一些思路在Android上会引起性能的损耗,以前疑惑为什么会这样?

再查相关的资料的时候,是说因为用到了反射,进而引起了性能损耗。那为什么反射会引起性能的损耗呢? 是通过 EventBus 来理解了这个原因。实际场景分析,如果有不对的希望有大神看到可以指点一二。

早期版本的EventBus原理

主要是通过一段段摘出源码进行分析理解。通过删除cpoy来进行理解,并非全是源码

一、首先就是EventBus的简单使用示例

EventBus.getDefault().register(this);

EventBus.getDefault().unregister(this);

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(Object object) {

}

先跟入在需要接收Event的地方的注册流程 register 。

  public void register(Object subscriber) {
      //通过getClass拿到注册类的class文件
      Class<?> subscriberClass = subscriber.getClass();
      List<SubscriberMethod> mSubscriberMethodFinder = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
      synchronized (this) {
          for (SubscriberMethod subscriberMethod : subscriberMethods) {
              subscribe(subscriber, subscriberMethod);
          }
      }
  }
  
 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
   List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
   if (subscriberMethods != null) {
       return subscriberMethods;
   }

   subscriberMethods = findUsingReflection(subscriberClass);

   if (subscriberMethods.isEmpty()) {
     //throw new EventBusException("Subscriber " + subscriberClass
     //      + " and its super classes have no public methods with the @Subscribe annotation");
       return null;
   } else {
       METHOD_CACHE.put(subscriberClass, subscriberMethods);
       return subscriberMethods;
   }
 

在 register 中通过 getClass 拿到 class 文件。在 findSubscriberMethods 方法中找到对应的 SubscriberMethod 的集合。SubscriberMethod 是用来保存 Subscriber 反射拿到的 Method 、 ThreadMode(在什么线程Post Event)、EventTpye的class、以及优先级。

之前刚开始的时候对理解源码中 SubscriberMethod 中的命名有点问题。源码中属性 final Class<?> eventType;
起初以为是通过反射拿到 Method中的参数(实际上也是拿到参数),看到后边就明白了,本地的队列、缓存其实是通过 eventType 来进行区分,这里也就是事件类型。

好接着往下看,看到方法名 findUsingReflection这里就是通过反射拿到 subscriberClass 相关注解方法的地方。简化过后的代码

    private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {

        Method[] methods = subscriberClass.getDeclaredMethods();

        List<SubscriberMethod> subscribeMethods = new ArrayList<>();
        for (Method method : methods) {
            //获取标记有自定义注解 Subscribe 的方法
            Subscribe annotation = method.getAnnotation(Subscribe.class);
            if(null != annotation){
                //获取方法参数
                Class<?>[] parameterTypes = method.getParameterTypes();
                if(parameterTypes.length == 1){
                    //只有一个参数的时候获取一个参数
                    Class<?> parameterType = parameterTypes[0];
                    //获取自定义注解Subscribe上的value,即时在哪个线程进行
                    ThreadMode threadMode = annotation.threadMode();
                    subscribeMethods.add(new SubscriberMethod(method,threadMode,parameterType));
                }
            }

        }


        return subscribeMethods;
    }

在这里看到一个损耗性能的地方,代码是通过 for循环遍历整个 subscriber 的Method,并且判断是否有 Subscribe的注解标记。(但是感觉这里不是最终的损耗点,希望有大神指点出来具体的原因)

最后在通过一些列的代码操作,进行本地保存

    /**
     * 简单的注册流程
     * @param subscriber
     * @param subscriberMethod
     */
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //通过反射获取得到的方法的参数类型来保存subscriber?? 也就是事件类型
        Class<?> parameterType = subscriberMethod.mEvnetType;
        CopyOnWriteArrayList<Subscription> subscriptions = mSubscriberByEventType.get(parameterType);
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

        if(subscriptions == null){
            subscriptions = new CopyOnWriteArrayList<>();
            mSubscriberByEventType.put(parameterType,subscriptions);
        }else{
            if(subscriptions.contains(newSubscription)){
                //存在则不用再次注册
                return;
            }
        }

        subscriptions.add(newSubscription);

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(parameterType);

    }

mSubscriberByEventType 是一个Map集合。就是用 eventType 作为Key,CopyOnWriteArrayList 作为Value 进行存储。

最后通过 post 发送Event。


    public void post(Object event){
        //将event添加到队列中
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if(!postingState.isPosting){
            //postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            try {
                while (!eventQueue.isEmpty()) {
                    //从队列中获取event,并且根据 threadMode 在那个线程发送 Event
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        //获取 event class 文件
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;

            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);

//        if (!subscriptionFound) {
//            if (logNoSubscriberMessages) {
//                logger.log(Level.FINE, "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 = mSubscriberByEventType.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;
    }

    //这里是截取的源码的一部分,主要的post逻辑在这里进行执行
    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.mThreadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.mThreadMode);
        }
    }

    //到这里就可以看出来,通过反射拿到Method之后,在 invoke 执行该方法,将 Event传送出去
    void invokeSubscriber(Subscription subscription, Object event) {
        try {
            //最终在这里通过反射拿到 方法和ThreadMode之后 invoke 执行方法,最终将方Event传到对应的 Subscribe的标记方法中
            subscription.subscriberMethod.mMethod.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            //handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

最后思考

单从源码这里看到,通过注解、反射拿到 Subscribe 中的标记方法,就存在在遍历整个 Subscribe 过程,因此感觉是这块拖慢了 APP 的性能。

猜你喜欢

转载自blog.csdn.net/u012102149/article/details/88233602