zoukankan      html  css  js  c++  java
  • EventBus 线程切换原理

    主要问题其实只有两个,其一:如何判断当前发送事件的线程是否是主线程;其二:如何在接收事件时指定线程并执行;
    一个一个来看。

    1.如何判断是否在主线程发送

    EventBus在初始化的时候会初始化一个MainThreadSupport对象,它会去获取主线程的Looper对象并存起来。(当前最新版本如果不是Android环境MainThreadSupport会为空,非Android环境也就无需关注是否是UI主线程的问题了)
    在发送消息的时候,EventBus会取出当前线程的Looper对象对象与主线程Looper对象做比较,如果相同,说明是在主线程发送消息,如果不同,说明是在子线程发送消息。以下是MainThreadSupport的代码:

    public interface MainThreadSupport {
    
        boolean isMainThread();
    
        Poster createPoster(EventBus eventBus);
    
        class AndroidHandlerMainThreadSupport implements MainThreadSupport {
    
            private final Looper looper;
    
            public AndroidHandlerMainThreadSupport(Looper looper) {
                this.looper = looper;
            }
    
            @Override
            public boolean isMainThread() {
                return looper == Looper.myLooper();
            }
    
            @Override
            public Poster createPoster(EventBus eventBus) {
                return new HandlerPoster(eventBus, looper, 10);
            }
        }
    }
    
    2.怎么在指定的线程执行订阅者的方法

    在找到订阅者之后,判断不同线程情况下执行订阅方法的逻辑基本都在postToSubscription()方法里面:

    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);
            }
        }
    

    以上代码中,如何判断是否是主线程上面已经说过了。
    invokeSubscriber()这个方法其实就是拿到订阅者的信息,直接执行订阅方法了(通过反射获取)。
    subscription对象中有一个SubscriberMethod对象,而SubscriberMethod这个对象基本上包含了订阅者的执行线程、订阅方法、是否粘性事件、优先级等等信息。如下:

    public class SubscriberMethod {
        final Method method;
        final ThreadMode threadMode;
        final Class<?> eventType;
        final int priority;
        final boolean sticky;
        /** Used for efficient comparison */
        String methodString;
        //省略若干代码...
    }
    

    所以,从postToSubscription()方法可以看出,当threadMode是POSTING时,直接在当前线程执行,不做判断,也就是从哪个线程发送,就从哪个线程执行订阅方法;
    我们这里主要来看threadMode为MAIN和BACKGROUND的情况:

    在主线程执行

    当threadMode为MAIN时,如果在主线程发送,直接在当前线程执行,没有问题。

    如果不在主线程发送,会有一个mainThreadPoster将包含订阅者信息的对象加入队列。这个mainThreadPoster其实是Handler的子类,它利用Handler的消息机制,发送消息并在主线程接收消息,获取到订阅者的信息后在主线程处理事件,从而实现在子线程发送消息,在主线程处理事件。
    这里直接利用Hadler的现成机制,可谓简明高效。

    在子线程执行

    当threadMode为BACKGROUND时,如果不在主线程发送,直接执行,没有问题。

    如果在主线程发送,这里有一个backgroundPoster将包含订阅者信息的对象加入队列。BackgroundPoster其实是Runnable的子类,在自己的run方法中不断从队列中取出订阅者对象,执行订阅方法。EventBus维护了一个线程池,BackgroundPoster会将自己丢到线程池中,执行自己的run方法,从而实现在在主线程发送事件,在子线程中执行订阅方法。

    以上,就是EventBus切换执行线程的主要流程。
    其实并不难。
    末尾附上一篇讲解EventBus原理的简练博文:戳这里


    附:EventBus线程切换

  • 相关阅读:
    HBaseTDG ClientAPI The Basics
    Hadoop TDG 3 – MR Features
    NoSQL Data Modeling Techniques
    配置SharePoint 2010的User Profile Synchronization连接到第三方的LDAP服务
    Failed to load data access DLL, 0x80004005
    如何开放HyperV服务的远程连接权限给别人?
    SharePoint 2010 Search 架构 已完工
    如何对SharePoint里OOB的JavaScript文件进行Debug 之一
    UPA Sync Service启动之后立即自动停止
    SharePoint 2010服务器场的防火墙需要开放哪些端口?
  • 原文地址:https://www.cnblogs.com/wytiger/p/10436188.html
Copyright © 2011-2022 走看看