美文网首页
事件传递EventBus

事件传递EventBus

作者: 单向时间轴 | 来源:发表于2018-10-31 21:44 被阅读21次

    目录

    概念

    EventBus:是一个Android事件发布/订阅框架,通过解耦发布者与订阅者的方式,大大的简化了Activity与Activity,Activity与Fragment,Fragment与Fragment等之间的数据传递,从而使得代码更简洁明了。

    使用

    1,配置

    • 打开App的build.gradle,在dependencies中添加最新的EventBus依赖:
      3.0以下版本的AndroidStudio : compile 'org.greenrobot:eventbus:3.1.1'
      3.0及以上版本的AndroidStudio : implementation 'org.greenrobot:eventbus:3.1.1'>
    • 索引加速模式(在AndroidStudio3.1.3上配置成功,在2.2.2版本上使用网上的两种方案均无MyEventBusIndex文件生成) 。成功配置的步骤如下(针对AndroidStudio3.1.3)
    1,在app的build.gradle文件的dependencies中添加:
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
    2,在app的build.gradle文件的defaultConfig中添加:(com.learn.study:当前的包名,可以自己配置)
    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [ eventBusIndex : 'com.learn.study.MyEventBusIndex' ]
        }
    }
    3,rebuiid项目后在app的build/generated/source/apt/debug中找到:MyEventBusIndex文件
    

    2,注册与注销

    //注册(一般在Activity或Fragment的onCreate中进行)
    if (!EventBus.getDefault().isRegistered(this)) {
        EventBus.getDefault().register(this);
    }
    //注销(一般是在Activity或Fragment的onDestory中进行)
    if (EventBus.getDefault().isRegistered(this)) {
        EventBus.getDefault().unregister(this);
    }
    
    //补充(如果使用了索引加速):在Application中配置:
    EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
    

    3,接收事件

    //3.0以后方法名可以任意,但必须加上@Subscribe的标识,同时设置参数为:
    threadMode:当前接收事件的执行线程
    sticky:是否接收粘性事件;
    priority:设置优先级
    方法的参数与发送的数据类型一致,必须为对象类型,如:必须使用Integer而不能使用int
    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 0)
    public void getMainData(Integer number) {
        Log.d(TAG, "ThreadMode : getMainData: " + number + " ;; " + Thread.currentThread().getName());
    }
    

    四种线程模型(ThreadMode:定义线程类型的枚举类)说明:

    • POSTING : 在当前发送的线程中执行(不用切换线程,最轻量级)
    • MAIN:在主线程中执行
    • MAIN_ORDERED:在主线程中执行,但事件始终排队等候确保调用为非阻塞(不常用)
    • BACKGROUND:后台线程,当在UI线程时中发布时才会新建一个后台线程执行
    • ASYNC:交给线程池来管理,直接通过asyncPoster调度。

    4,发送事件

    //发送普通事件
    EventBus.getDefault().post(10);
    //发送粘性事件
    EventBus.getDefault().postSticky(20);
    

    5,注意事项

    1,有注册必须有注销,保证一一对应,同时调用的生命周期方法也对应
    2,注册页面必须有接收方法,主要是需要识别到有Subscribe的注解,发送界面不需要注册。
    3,同一个事件可以定义多个接收,参数必须是对象类型。如:发送的是int但接收参数必须为Interger
    4,使用普通的post发送事件时,必须先注册才能收到。而postSticky可以在发送后注册也能收到

    //1,验证普通事件与粘性事件的发送与注册顺序
    private void testEventBus() {
        EventBus.getDefault().postSticky(20);
        if (!EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().register(this);
        }
    }
    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 0)
    public void getMainData(Integer number) {
        Log.d(TAG, "ThreadMode : getMainData: " + number + " ;; " + Thread.currentThread().getName());
    }
    //打印结果:(即事件能够收到并处理)
    10-24 21:13:52.574 5913-5913/com.learn.study D/MainActivity: ThreadMode : getMainData: 20 ;; main
    private void testEventBus() {
        EventBus.getDefault().post(10);
        if (!EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().register(this);
        }
    }
    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 0)
    public void getMainData(Integer number) {
        Log.d(TAG, "ThreadMode : getMainData: " + number + " ;; " + Thread.currentThread().getName());
    }
    //无打印,即事件未接收
    
    //2,验证线程模型
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMainData(Integer number) {
        Log.d(TAG, "ThreadMode : getMainData: " + number + " ;; " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void getThreadData(Integer number) {
        Log.d(TAG, "ThreadMode : getThreadData: " + number + " ;; " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void getPostData(Integer number) {
        Log.d(TAG, "ThreadMode : getPostData: " + number + " ;; " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void getAsyncData(Integer number) {
        Log.d(TAG, "ThreadMode : getAsyncData: " + number + " ;; " + Thread.currentThread().getName());
    }
    EventBus.getDefault().post(10);
    Log.d("MainActivity", "ThreadMode : sendData :" + Thread.currentThread().getName());
    //在主线程中发送结果打印:
    10-24 21:28:19.490 6837-6837/com.learn.study D/MainActivity: ThreadMode : getMainData: 10 ;; main
        ThreadMode : getPostData: 10 ;; main
    10-24 21:28:19.491 6837-7309/com.learn.study D/MainActivity: ThreadMode : getThreadData: 10 ;; pool-1-thread-1
    10-24 21:28:19.491 6837-6837/com.learn.study D/MainActivity: ThreadMode : sendData :main
    10-24 21:28:19.491 6837-7310/com.learn.study D/MainActivity: ThreadMode : getAsyncData: 10 ;; pool-1-thread-2
    
    //在子线程中发送结果打印:
    10-24 21:33:22.969 7800-7859/com.learn.study D/MainActivity: ThreadMode : getThreadData: 10 ;; Thread-4
        ThreadMode : getPostData: 10 ;; Thread-4
    10-24 21:33:22.970 7800-7860/com.learn.study D/MainActivity: ThreadMode : getAsyncData: 10 ;; pool-1-thread-1
    10-24 21:33:22.970 7800-7859/com.learn.study D/MainActivity: ThreadMode : sendData :Thread-4
    10-24 21:33:23.028 7800-7800/com.learn.study D/MainActivity: ThreadMode : getMainData: 10 ;; main
    
    //3,验证接收事件的优先级(相同的线程中才存在优先级问题,同时拦截才有效果)
    10-24 22:04:43.762 10532-10596/com.learn.study D/MainActivity: ThreadMode : getThreadData: 10 ;; Thread-4;; 优先级:6
        ThreadMode : getPostData: 10 ;; Thread-4;; 优先级:3
    10-24 22:04:43.763 10532-10596/com.learn.study D/MainActivity: ThreadMode : sendData :Thread-4
    10-24 22:04:43.763 10532-10597/com.learn.study D/MainActivity: ThreadMode : getAsyncData: 10 ;; pool-1-thread-1;; 优先级:0
    10-24 22:04:43.810 10532-10532/com.learn.study D/MainActivity: ThreadMode : getMainData: 10 ;; main;; 优先级:10
        ThreadMode : getMain1Data: 10 ;; main;; 优先级:7
    

    原理(针对3.1.1版本的源码)

    1,注册的流程

    //1,使用双重校验的单例创建对象:EventBus.getDefault()
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    //2,register()调用注册的方法
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //查找当前注册类所有的接收方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
    //具体实现查找接收方法:
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //1,从Map集合METHOD_CACHE(键:注册的类class对象,值:当前注册类里面的接收方法)中查找方法,如果查询到方法则直接返回方法集合List,否则重新获取
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        if (ignoreGeneratedIndex) {
            //2,忽视索引加速的方式,直接使用反射的方式获取的注册类下的所有接受方法:最后还是调用findUsingReflectionInSingleClass()方法
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //3,获取到注册类里面的接收方法
            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) {
        //1,这里存在一个查找状态池:FIND_STATE_POOL,大小为4,减少对象的创建。
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //2,查找注册类的接受方法,如果添加了索引加速,而返回接受方法,否则返回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);
    }
    //使用索引加速获取方法
    private SubscriberInfo getSubscriberInfo(FindState findState) {
            if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
                SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
                if (findState.clazz == superclassInfo.getSubscriberClass()) {
                    return superclassInfo;
                }
            }
            if (subscriberInfoIndexes != null) {
                for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                    SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                    if (info != null) {
                        //使用了索引加速则info返回索引的接收方法,否则返回null
                        return info;
                    }
                }
            }
            return null;
        }
    
    //使用反射获取接收方法
    private void findUsingReflectionInSingleClass(FindState findState) {
            Method[] methods;
            try {
                //1,使用反射先获取到注册类里面的所有方法
                methods = findState.clazz.getDeclaredMethods();
            } catch (Throwable th) {
                // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
                methods = findState.clazz.getMethods();
                findState.skipSuperClasses = true;
            }
            //2,遍历所有方法,找到接收事件的方法
            for (Method method : methods) {
                int modifiers = method.getModifiers();
                if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 1) {
                        //3,通过获取方法上的注解Subscribe来区分接收事件的方法.
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        if (subscribeAnnotation != null) {
                            Class<?> eventType = parameterTypes[0];
                            if (findState.checkAdd(method, eventType)) {
                                ThreadMode threadMode = subscribeAnnotation.threadMode();
                                //4,将接收事件的方法信息添加到查询状态的对象中
                                findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                        subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                            }
                        }
                    } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                        String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                        throw new EventBusException("@Subscribe method " + methodName +
                                "must have exactly 1 parameter but has " + parameterTypes.length);
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException(methodName +
                            " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
                }
            }
        }
    
    //查找完成接收事件的方法后,调用subscribe(),将注册类,接收方法,接收事件的类型等添加到对应的Map集合中进行保存,避免相应信息每次获取
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            Class<?> eventType = subscriberMethod.eventType;
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions == null) {
                subscriptions = new CopyOnWriteArrayList<>();
                subscriptionsByEventType.put(eventType, subscriptions);
            } else {
                if (subscriptions.contains(newSubscription)) {
                    throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                            + eventType);
                }
            }
    
            int size = subscriptions.size();
            //根据接收事件的优先级,将订阅事件存放到subscriptionsByEventType集合中
            for (int i = 0; i <= size; i++) {
                if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                    subscriptions.add(i, newSubscription);
                    break;
                }
            }
    
            List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
            if (subscribedEvents == null) {
                subscribedEvents = new ArrayList<>();
                typesBySubscriber.put(subscriber, subscribedEvents);
            }
            subscribedEvents.add(eventType);
            //这里用来处理粘性事件
            if (subscriberMethod.sticky) {
                if (eventInheritance) {
                    // Existing sticky events of all subclasses of eventType have to be considered.
                    // Note: Iterating over all events may be inefficient with lots of sticky events,
                    // thus data structure should be changed to allow a more efficient lookup
                    // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                    Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                    for (Map.Entry<Class<?>, Object> entry : entries) {
                        Class<?> candidateEventType = entry.getKey();
                        if (eventType.isAssignableFrom(candidateEventType)) {
                            Object stickyEvent = entry.getValue();
                             //后续调用跟普通事件相同的流程.
                            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                        }
                    }
                } else {
                    Object stickyEvent = stickyEvents.get(eventType);
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        }
    

    2,注销流程

    EventBus.getDefault().unregister(this);
    //1,与注册相同,使用单例获取到EventBus对象.
    public synchronized void unregister(Object subscriber) {
            //typesBySubscriber这是一个Map集合,键为:注册的类,值为当前类中处理的事件类型集合
            List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
            if (subscribedTypes != null) {
                for (Class<?> eventType : subscribedTypes) {
                    unsubscribeByEventType(subscriber, eventType);
                }
                //从存放注册类与事件类型的对照集合中移除注册类,这样也可以防止对象引用无法释放.
                typesBySubscriber.remove(subscriber);
            } else {
                logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
            }
        }
    
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
            //subscriptionsByEventType这也是一个Map集合,键:事件类型,值:事件类型对应的接收方法.
            //Subscription:将注册类与接收方法封装为了一个数据结构
            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--;
                    }
                }
            }
        }
    

    3,发布事件

    //发送粘性事件
    public void postSticky(Object event) {
            synchronized (stickyEvents) {
                //将粘性事件存入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) {
            //使用ThreadLocal存放当前线程状态对象:PostingThreadState
            PostingThreadState postingState = currentPostingThreadState.get();
            List<Object> eventQueue = postingState.eventQueue;
            //1,将事件加入到事件集合中
            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 {
                    while (!eventQueue.isEmpty()) {
                       //2,事件处理的方法
                        postSingleEvent(eventQueue.remove(0), postingState);
                    }
                } finally {
                    postingState.isPosting = false;
                    postingState.isMainThread = false;
                }
            }
        }
    
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            //3,获取事件类型的class对象,如:int 10 ,最终获取的是:java.lang.Integer
            Class<?> eventClass = event.getClass();
            boolean subscriptionFound = false;
            if (eventInheritance) {
                List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
                int countTypes = eventTypes.size();
                for (int h = 0; h < countTypes; h++) {
                    Class<?> clazz = eventTypes.get(h);
                    //4,发布事件调用方法: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));
                }
            }
        }
    
    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                subscriptions = subscriptionsByEventType.get(eventClass);
            }
            if (subscriptions != null && !subscriptions.isEmpty()) {
                for (Subscription subscription : subscriptions) {
                    postingState.event = event;
                    postingState.subscription = subscription;
                    boolean aborted = false;
                    try {
                        //5,根据不同的线程模型来处理发布的事件
                        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;
        }
    
        //根据接收处理事件的线程模型不同,分别在对应的线程中处理事件.
        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 MAIN_ORDERED:
                    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:
                    asyncPoster.enqueue(subscription, event);
                    break;
                default:
                    throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
            }
        }
    
    void invokeSubscriber(Subscription subscription, Object event) {
            try {
                //6,最终调用method的invoke方法来处理
                subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
            } catch (InvocationTargetException e) {
                handleSubscriberException(subscription, event, e.getCause());
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Unexpected exception", e);
            }
        }
    

    4,索引加速的原理

    在编译期生成类用来记录所有的接收事件方法,将运行期的反射提前到编译期,避免了运行期使用反射增加的性能开销。编译期生成的类如下:

    public class MyEventBusIndex implements SubscriberInfoIndex {
        //用来存放所有的接收事件方法,避免后面需要通过反射获取,减小运行的性能开销
        private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
    
        static {
            SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
            putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
                new SubscriberMethodInfo("getMainData", Integer.class, ThreadMode.MAIN, 10, false),
                new SubscriberMethodInfo("getMain2Data", Integer.class, ThreadMode.MAIN, 7, false),
                new SubscriberMethodInfo("getThreadData", Integer.class, ThreadMode.BACKGROUND, 6, false),
                new SubscriberMethodInfo("getPostData", Integer.class, ThreadMode.POSTING, 3, false),
                new SubscriberMethodInfo("getAsyncData", Integer.class, ThreadMode.ASYNC),
            }));
    
        }
    
        private static void putIndex(SubscriberInfo info) {
            SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
        }
    
        @Override
        public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
            SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
            if (info != null) {
                return info;
            } else {
                return null;
            }
        }
    }
    

    参考:

    https://www.cnblogs.com/bugly/p/5475034.html

    相关文章

      网友评论

          本文标题:事件传递EventBus

          本文链接:https://www.haomeiwen.com/subject/yfzboftx.html