zoukankan      html  css  js  c++  java
  • 源码解析-EventBus

    • 示例使用
    • 时序图
    • 源码解读

    EventBus 使用

    • 官网定义:EventBus 是一个使用 Java 写的观察者模式,解耦的 Android 开源库。EventBus 只需要几行代码即可解耦简化代码,加快开发速度。

    使用:

    1. 定义 event
      Event 就是个标准 POJO
        public class MessageEvent {
    
            public final String message;
    
            public MessageEvent(String message) {
                this.message = message;
            }
        }
    
    1. 准备订阅者。
      在需要接收消息的 Activity 或者 Fragemnt 生命周期方法中注册,同时在对应的生命周期方法中进行反注册。不解绑的话会造成内存泄漏。
        @Override
        public void onStart() {
            super.onStart();
            // 注册
            EventBus.getDefault().register(this);
        }
            
        @Override
        public void onStop() {
            // 反注册
            EventBus.getDefault().unregister(this);
            super.onStop();
        }
    

    写一个方法使用注解 Subscribe 修饰,同时设置 threadMode = ThreadMode.MAIN 表示在 UI 线程接收该事件。也可以使用其他的 ThreadMode 来修饰,比如 POSTING , ASYNC 等,不过在 Android 中一般都是用 MAIN 的。

            // 收到消息,弹出 Toast 
            @Subscribe(threadMode = ThreadMode.MAIN)
            public void onMessageEvent(MessageEvent event) {
                Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
            }
    
    1. 发送 evnet
      在需要发送事件的地方通过调用 post(event) 方法发送自定义的 event
        EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
    

    官网
    Github

    时序图

    源码解析

    • 思路:通过 EventBus.gerDefault().register(this),把当前的 Activity 或者 Fragment 和通过反射拿到在其中用 @Subscribe 修饰的方法,通过 Mapevent 和 订阅者绑定,通过 post() 方法携带 Event ,从容器中取出相关的 subscribe 和方法,通过反射调用该方法,把消息分发到目标 subscribe 。在 ActivityFragment 生命周期销毁的时候,通过 unRegister()subscribe 从容器中移除,去掉订阅者模式,防止内存泄漏。

    • 相关类

      • Poster:接口,唯一方法 enqueue(), 加入队列的。
      • HandlerPoster: 切换到 UI 线程的 Poster 实现类,内部通过一个链表存储消息,通过 Handler 把消息切换到 UI 线程,从而实现更新 UI 线程
      • BackgroundPoster: 后台执行 EventPoster 实现类
      • AsyncPoster:与 BackgroudPoster 类似
      • PendingPostQueue: 存储 PendingPost 的链表
      • Subscription: 订阅对象,有 subscribersubscribeMethod 构成
      • SubscriberMethod: subscriber 中的接收方法,有 方法名称 ,接收信息线程, 优先级, 是否是粘性, event类型
      • PostingThreadState:保存在 ThreadLocal 中,主要存储 event 的相关信息, 是否在主线程,订阅者信息,是否取消,是否已发送。
      • MainThreadSupport: 切换到 UI 线程用的,配合 HandlerPoster 一起使用。
      • SubscriberInfo:Base class for generated index classes created by annotation processing
    • 从使用的入口开始

    注册:EventBus.getDefault().register(this)

    发送消息:EventBus.getDefault().post(event)

    • 发送消息一般调用 post() 方法,然后把定义的 Event 发送出去,看下 post() 方法源码
        public void post(Object event) {
            // ThreadLocal 中获取变量 postingState.
            PostingThreadState postingState = currentPostingThreadState.get();
            // 从 postingState 中拿到 ArrayList
            List<Object> eventQueue = postingState.eventQueue;
            // 把当前 event 加入到 ArrayList 中
            eventQueue.add(event);
            // 是否已发送,在主线程
            if (!postingState.isPosting) {
                postingState.isMainThread = isMainThread();
                postingState.isPosting = true;
                if (postingState.canceled) {
                    throw new EventBusException("Internal error. Abort state was not reset");
                }
                try {
                    // ArrayList 不为 Null , 循环取出来数据,通过 postSingleEvent() 发送。
                    while (!eventQueue.isEmpty()) {
                        postSingleEvent(eventQueue.remove(0), postingState);
                    }
                } finally {
                    // Arraylist 中所有的消息都发送完成,设置 isPosting 和 isMainThread 为 false。
                    postingState.isPosting = false;
                    postingState.isMainThread = false;
                }
            }
        }
    

    ThreadLocal 中拿到 PostingThreadState ,然后把消息放入的到变量 ArrayList 中,然后判断 postingState 的状态,是否处于发送状态,不在发送状态的话,进入条件判断然后遍历 ArrayList 调用 postSingleEvent() 方法把 event 发送。看下 postSingleEvent() 源码

        private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            Class<?> eventClass = event.getClass();
            // 订阅者信息是否能找到
            boolean subscriptionFound = false;
            // 通过 EventBusBilder 传入的, 默认为 true
            if (eventInheritance) {
                List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
                int countTypes = eventTypes.size();
                for (int h = 0; h < countTypes; h++) {
                    Class<?> clazz = eventTypes.get(h);
                    // 进入到了 postSingleEventForEventType() 方法
                    subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
                }
            } else {
                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));
                }
            }
        }
    

    在方法 postSingleEvent() 中,通过成员变量 eventInheritance (表示是否要向上查找事件的父类) 来判断,默认为 true ,所以进入 if 中,lookupAllEventTypes() 方法会通过递归的方式进行查找所有父类事件并存到 List 中。然后通过遍历调用 postSingleEventForEventType() 方法进行处理
    看下 lookupAllEventTypes() 源码,其中 addInterfaces() 通过递归拿到全部的父类事件。

        private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
            synchronized (eventTypesCache) {
                List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
                if (eventTypes == null) {
                    eventTypes = new ArrayList<>();
                    Class<?> clazz = eventClass;
                    while (clazz != null) {
                        eventTypes.add(clazz);
                        addInterfaces(eventTypes, clazz.getInterfaces());
                        clazz = clazz.getSuperclass();
                    }
                    eventTypesCache.put(eventClass, eventTypes);
                }
                return eventTypes;
            }
        }
    

    addInterfaces() 源码,通过递归调用方法,查找所有的父类事件并加入到 List

        static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
            for (Class<?> interfaceClass : interfaces) {
                if (!eventTypes.contains(interfaceClass)) {
                    eventTypes.add(interfaceClass);
                    addInterfaces(eventTypes, interfaceClass.getInterfaces());
                }
            }
        }
    

    看下 postSingleEventForEventType() 源码。成员变量 subscriptionsByEventType 是一个 Map<Class<?>, CopyOnWriteArrayList<Subscription>>,在注册的时候,eventType 作为 key ,subscriptions 作为 value 存入了 map 中。 通过成员变量拿到 subscriptions ,然后遍历该列表,调用 postToSubscription() 方法

        private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
            // 线程安全的ArrayList 保存订阅者信息
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                // 根据 clz 获取该类的订阅者信息列表, subscriptionsByEventType 这个 Map 在注册的时候,把 eventType 和 subscriptions 绑定了。
                subscriptions = subscriptionsByEventType.get(eventClass);
            }
            if (subscriptions != null && !subscriptions.isEmpty()) {
                //遍历订阅者信息列表
                for (Subscription subscription : subscriptions) {
                    //设置 postingState 的状态
                    postingState.event = event;
                    postingState.subscription = subscription;
                    // 是否可以取消,默认不可取消
                    boolean aborted = false;
                    try {
                        // 传入订阅者信息,event, 是否在主线程
                        postToSubscription(subscription, event, postingState.isMainThread);
                        // postingState.canceled 默认是 false 。表示不可取消
                        aborted = postingState.canceled;
                    } finally {
                        //postingState 恢复成默认状态。
                        postingState.event = null;
                        postingState.subscription = null;
                        postingState.canceled = false;
                    }
                    if (aborted) {
                        break;
                    }
                }
                return true;
            }
            return false;
        }
    

    进入 postToSubscription() 源码,根据订阅者在注册的时候存入的 threadModeevent 分发到不同的线程中。根据 threadMode 不同,可以分为 POSTING, MAIN, MAIN_ORDERED, BACKGROUND, ASYNC。 变量 mainThreadPoster 其实是 HandlerPoster ,把消息发送到 UI 线程的。

        private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
            switch (subscription.subscriberMethod.threadMode) {
                case POSTING:// 订阅者与 event 在相同线程
                    invokeSubscriber(subscription, event);
                    break;
                case MAIN:// UI 线程
                    // event 是在 UI 线程,则直接通过反射调用。
                    if (isMainThread) {
                        invokeSubscriber(subscription, event);
                    } else {
                        // event 不在 UI 线程,则加入队列,通过 Handler 切换到 UI 线程
                        mainThreadPoster.enqueue(subscription, event);
                    }
                    break;
                case MAIN_ORDERED://UI线程,按照顺序发送消息
                    if (mainThreadPoster != null) {
                        mainThreadPoster.enqueue(subscription, event);
                    } else {
                        // temporary: technically not correct as poster not decoupled from subscriber
                        invokeSubscriber(subscription, event);
                    }
                    break;
                case BACKGROUND://
                    if (isMainThread) {
                        backgroundPoster.enqueue(subscription, event);
                    } else {
                        invokeSubscriber(subscription, event);
                    }
                    break;
                case ASYNC://订阅者单独一个线程,不同于发送线程和UI线程。
                    asyncPoster.enqueue(subscription, event);
                    break;
                default:
                    throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
            }
        }
    

    invokeSubscriber() 方法,通过反射执行订阅者的订阅方法

        void invokeSubscriber(Subscription subscription, Object event) {
            try {
                subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
            } catch (InvocationTargetException e) {
                handleSubscriberException(subscription, event, e.getCause());
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Unexpected exception", e);
            }
        }
    

    反注册:EventBus.getDefault().unregister(this);

  • 相关阅读:
    /etc/fstab 文件解释
    CRLF和LF
    Git远程操作详解
    jsp错误处理
    jsp隐式对象
    关于循环队列要注意的
    JSP动作元素
    JSP指令
    jsp语法简介
    jsp声明周期
  • 原文地址:https://www.cnblogs.com/liyiran/p/9172989.html
Copyright © 2011-2022 走看看