EventBus基本使用与源码解析

一 解析EventBus

        EventBus是一款针对Android优化的发布-订阅事件总线。它简化了应用程序内各组件间、组件与后台线程间的通信。其优点是开销小,代码更优雅,以及将发送者和接收者解耦。当一个Android应用功能越来越多的时候,保证应用的各个部分之间高效的通信将变得越来越困难。所以为了解决这个问题,EventBus应运而生!

1.1 EventBus概述

        在讲到 EventBus 的基本用法之前,我们需要了解 EventBus 的三要素以及它的 4 种ThreadMode。

        EventBus的三要素如下。

        • Event:事件。可以是任意类型的对象。

        • Subscriber:事件订阅者。在 EventBus 3.0 之前消息处理的方法只能限定于 onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,它们分别代表4种线程模型。而在EventBus 3.0之后,事件处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)。 

        • Publisher:事件发布者。可以在任意线程任意位置发送事件,直接调用 EventBus 的post(Object)方法。可以自己实例化EventBus对象,但一般使用EventBus.getDefault()就可以。根据post函数参数的类型,会自动调用订阅相应类型事件的函数。

        EventBus的4种ThreadMode(线程模型)如下。

        • POSTING(默认):如果使用事件处理函数指定了线程模型为POSTING,那么该事件是在哪个线程发布出来的,事件处理函数就会在哪个线程中运行,也就是说发布事件和接收事件在同一个线程中。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。

        • MAIN:事件的处理会在UI线程中执行。事件处理的时间不能太长,长了会导致ANR。

        • BACKGROUND:如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行;如果事件本来就是在子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。

        • ASYNC:无论事件在哪个线程中发布,该事件处理函数都会在新建的子线程中执行;同样,此事件处理函数中禁止进行UI更新操作。

1.2 EventBus基本用法

        EventBus基本使用分为以下5个步骤。

      (1)自定义一个事件类

public class MessageEvent {
    ......
}

    (2)在需要订阅事件的地方注册事件

 EventBus.getDefault().register(this);

     (3)发送事件

EventBus.getDefault().post(messageEvent);

     (4)处理事件

@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent){
    ... 
}

       前面说过,在EventBus3.0以后消息处理的方法可以随便取名,但是需要加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)。

     (5)取消事件订阅

EventBus.getDefault().unregister(this);

1.3 EventBus实战

       前面讲到了EventBus的基本用法,但是这过于简单,这里举一个例子来应用EventBus。

     (1)添加依赖

implementation'org.greenrobot:eventbus:3.0.0'

     (2)添加混淆规则

       在模块的 proguard-rules.pro 混淆规则文件中添加如下规则:

-keepattributes *Annotation*
-keepclassmembers class * {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
 
# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

      (3)定义消息事件类

public class MessageEvent {
    public String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

     (4)注册和取消订阅事件

       在MainActivity中注册和取消订阅事件,在 MainActivity 中定义了两个 Button:一个用来注册事件,另一个用来跳转到SecondActivity。代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView tv_message;
    private Button btn_enter_second_activity, btn_register_event;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_message = findViewById(R.id.tv_message);
        btn_enter_second_activity = findViewById(R.id.btn_enter_second_activity);
        btn_register_event = findViewById(R.id.btn_register_event);

        btn_enter_second_activity.setOnClickListener(this);
        btn_register_event.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.btn_enter_second_activity) {
            // 跳转到SecondActivity
            Intent intent = new Intent(this, SecondActivity.class);
            startActivity(intent);
        } else if (id == R.id.btn_register_event) {
            // 注册事件
            EventBus.getDefault().register(this);
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消注册事件
        EventBus.getDefault().unregister(this);
    }


}

     (5)事件订阅者处理事件

       在MainActivity中自定义方法来处理事件,在这里ThreadMode设置为MAIN,事件的处理会在UI线程中执行,用TextView来展示收到的事件消息。

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(MessageEvent event) {
        if (event != null && event.getMessage() != null) {
            tv_message.setText(event.getMessage());
        }
    }

      (6)事件发布者发布事件

        创建了SecondActivity来发布消息,在SecondActivity中,我们定义“发送事件 按钮来发送事件并将SecondActivity finish掉。代码如下所示:

public class SecondActivity extends AppCompatActivity {
    private Button btn_send_event;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        btn_send_event = findViewById(R.id.btn_send_event);
        btn_send_event.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MessageEvent event = new MessageEvent();
                event.setMessage("欢迎学习EventBus");
                EventBus.getDefault().post(event);
                finish();
            }
        });
    }
}

        好了,运行程序,如图1-3-1所示。接下来我们点击MainActivity 中的“注册事件 按钮来注册事件,然后点击“跳转到SecondActivity按钮,这时跳转到SecondActivity,如图1-3-2所示。接下来点击“发送事件 按钮,这个时候 SecondActivity 被 finish 掉,因此界面展示的是MainActivity,如图1-3-3所示。可以看到MainActivity的TextView显示“欢迎学习EventBus",MainActivity成功地收到了SecondActivity发送的事件。

图1-3-1 初始样式​​​​
图1-3-2 跳转到SecondActivity​​
图1-3-3 MainActivity接收到事件

1.4 EventBus的黏性事件

        除了上面讲的普通事件外, EventBus 还支持发送黏性事件,就是在发送事件之后再订阅该事件也能收
到该事件,这跟黏性广播类似。为了验证黏性事件,我们修改以前的代码,如下所示。
     ( 1 )订阅者处理黏性事件
       在 MainActivity 中新写一个方法用来处理黏性事件:
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onStickyEvent(MessageEvent event) {
        if (event != null && event.getMessage() != null) {
            tv_message.setText(event.getMessage());
        }
    }
     ( 2 )发送黏性事件
 
       在 SecondActivity 中定义一个Button 来发送黏性事件:
 btn_send_event.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MessageEvent event = new MessageEvent();
                event.setMessage("EventBus黏性事件");
                EventBus.getDefault().postSticky(event);
                finish();
            }
        });
        现在运行代码再来看看效果。首先,我们在 MainActivity 中并没有点击“注册事件”按钮,而是直接跳到 SecondActivity中点击发送“发送事件”按钮。这时界面回到MainActivity,我们看到TextView仍旧显示着 MainActivity的字段,这是因为我们现在还没有订阅事件。接下来我们点击“注册事件”按钮,TextView 内容 发生改变,显示“EventBus黏性事件”,说明黏性事件被成功接收到了。
 

二 源码解析EventBus

2.1 EventBus构造方法

       当我们要使用 EventBus 时,首先会调用 EventBus.getDefault()来获取 EventBus 实例。现在查看 getDefault方法做了什么,如下所示:
    /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
       很明显这是一个单例模式,采用了双重检查模式 (DCL )。接下来查看 EventBus 的构造方法做了什 么
 
    /**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }
这里 DEFAULT_BUILDER 是默认的 EventBusBuilder ,用来构造 EventBus
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

this调用了EventBus的另一个构造方法,如下所示:

    EventBus(EventBusBuilder builder) {
        // 以事件类型作为Key,Subscription的List集合作为Value的Map集合
        subscriptionsByEventType = new HashMap<>();
        // 订阅者作为Key,订阅事件的List集合作为Value的Map集合
        typesBySubscriber = new HashMap<>();
        // 黏性事件的Map集合
        stickyEvents = new ConcurrentHashMap<>();
        // Handler对象,用于线程间切换
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        // Runnable对象
        backgroundPoster = new BackgroundPoster(this);
        // Runnable对象
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }
       我们可以通过构造一个EventBusBuilder来对 EventBus 进行配置,这里采用了建造者模式。
       在这里注意到对于EventBus可以采用单实例模式获取,但是EventBus的构造方法为公共的。很显然也就是说明了在应用中可以存在多个EventBus,对于存在多个的EventBus情况下,它们之间相互独立,会发布和订阅各自的事件进行接收执行。

2.2 订阅者注册

       获取 EventBus 后,便可以将订阅者注册到 EventBus 中。下面来看一下register方法:
 
    /**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); // 1
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod); // 2
            }
        }
    }
       对于register中的参数,就是我们的订阅者,也就是我们经常传入的this对象。
     ( 1 )查找订阅者的订阅方法
      上面代码注释 1 处的 findSubscriberMethods方法找出一个SubscriberMethod(对于SubscriberMethod类中,主要就是用保存订阅方法的Method对象,线程模式,事件类型,优先级,是否粘性事件等属性。 )的集合,也就是传进来的订阅 者的所有订阅方法,接下来遍历订阅者的订阅方法来完成订阅者的注册操作。可以看出register 方法做了两 件事:一件事是查找订阅者的订阅方法,另一件事是订阅者的注册。在SubscriberMethod 类中,主要用来保 存订阅方法的Method 对象、线程模式、事件类型、优先级、是否是黏性事件等属性。下面就来查看 findSubscriberMethods方法,如下所示:
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        // 从缓存中获取SubscriberMethod集合
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); // 1
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        //ignoreGeneratedIndex是否忽略注解器生成的MyEventBusIndex,默认为false
        if (ignoreGeneratedIndex) {
            //通过反射获取subscriberMethods
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //通过注解器生成的MyEventBusIndex信息获取subscriberMethods,
            //如果没有配置MyEventBusIndex,依然通过通过反射获取subscriberMethods
            subscriberMethods = findUsingInfo(subscriberClass); // 3
        }
        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); // 2
            return subscriberMethods;
        }
    }
       上面代码注释 1 处从缓存中查找是否有订阅方法的集合,如果找到了就立马返回。如果缓存中没有,则 根据 ignoreGeneratedIndex 属性的值来选择采用何种方法来查找订阅方法的集合。 ignoreGeneratedIndex 属性 表示是否忽略注解器生成的 MyEventBusIndex(在项目重新rebuild以后,会自动生成在build文件夹下,类名也可以自己定义) 。如何生成 MyEventBusIndex 类以及它的使用,可以参考官方 文档 http //greenrobot.org/eventbus/documentation/subscriber-index/,这里就不再讲解了。 ignoreGeneratedIndex 的默认值是 false ,可以通过 EventBusBuilder 来设置它的值。在注释 2 处找到订阅方法的 集合后,放入缓存,以免下次继续查找。我们在项目中经常通过EventBus 单例模式来获取默认的 EventBus 对 象,也就是ignoreGeneratedIndex false 的情况,这种情况调用了注释 3 处的 findUsingInfo 方法:
 
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        //创建和初始化FindState对象
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            //获取订阅者信息,没有配置MyEventBusIndex返回null
            findState.subscriberInfo = getSubscriberInfo(findState); // 1
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); // 2
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //通过反射来查找订阅方法
                findUsingReflectionInSingleClass(findState); // 3
            }
            //进入父类查找订阅方法
            findState.moveToSuperclass();
        }
        //回收处理findState,并返回订阅方法的List集合
        return getMethodsAndRelease(findState);
    }
      在FindState里面,它保存了一些订阅者的方法以及对订阅方法的校验。通过initForSubscriber初始化了FindState的clazz属性。  上面代码注释 1 处通过 getSubscriberInfo 方法来获取订阅者信息。在我们开始查找订阅方法的时候并没有忽略注解器为我们生成的索引 MyEventBusIndex 。如果我们通过 EventBusBuilder 配置了 MyEventBusIndex,便会获取 subscriberInfo 。注释 2 处调用 subscriberInfo getSubscriberMethods 方法便可以得 到订阅方法相关的信息。如果没有配置MyEventBusIndex ,便会执行注释 3 处的 findUsingReflectionInSingleClass方法,将订阅方法保存到 findState 中。最后再通过 getMethodsAndRelease 方法findState 做回收处理并返回订阅方法的 List 集合。默认情况下是没有配置 MyEventBusIndex 的,因此现在 查看一下findUsingReflectionInSingleClass方法的执行过程,如下所示:
 
 
 private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods(); // 1
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        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) {
                    //查找包含Subscribe的注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        //保存到findState对象当中
                        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)) {
                    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");
            }
        }
    }
       上面代码注释 1 处通过反射来获取订阅者中所有的方法,并根据方法的类型、参数和注解来找到订阅方
法。找到订阅方法后将订阅方法的相关信息保存到 findState 中。
 
     ( 2 )订阅者的注册过程
       在查找完订阅者的订阅方法以后便开始对所有的订阅方法进行注册。我们再回到 register方法中,在那里的注释 2 处调用了 subscribe 方法来对订阅方法进行注册,如下所示:
 
 // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 获取订阅方法中的订阅事件
        Class<?> eventType = subscriberMethod.eventType;
        // 创建一个SubScription来保存订阅者和订阅方法
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // 1
        //获取当前订阅事件中Subscription的List集合
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 2
        if (subscriptions == null) {
            //该事件对应的Subscription的List集合不存在,则重新创建并保存在subscriptionsByEventType中
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            // 判断订阅者是否已经被注册
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

       //将newSubscription按照订阅方法的优先级插入到subscriptions中
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription); // 3
                break;
            }
        }

        //通过订阅者获取该订阅者所订阅事件的集合
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); // 4
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //将当前的订阅事件添加到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);
            }
        }
    }
       首先,上面代码注释 1 处会根据 subscriber (订阅者)和 subscriberMethod (订阅方法)创建一个 Subscription(订阅对象)。注释 2 处根据 eventType (事件类型)获取 Subscriptions (订阅对象集合)。如果 Subscriptions为 null 则重新创建,并将 Subscriptions 根据 eventType 保存在 subscriptionsByEventType Map 集 合)。注释3 处按照订阅方法的优先级插入到订阅对象集合中,完成订阅方法的注册。注释 4 处通过 subscriber获取 subscribedEvents (事件类型集合)。如果 subscribedEvents null 则重新创建,并将 eventType 添加到subscribedEvents 中,并根据 subscriber subscribedEvents 存储在 typesBySubscriber Map 集合)。如果 是黏性事件,则从stickyEvents 事件保存队列中取出该事件类型的事件发送给当前订阅者。总结一下, subscribe方法主要就是做了两件事:一件事是将Subscriptions 根据 eventType 封装到 subscriptionsByEventType 中,将subscribedEvents 根据 subscriber 封装到 typesBySubscriber 中;第二件事就是对黏性事件的处理。

2.3 事件的发送

       在获取 EventBus 对象以后,可以通过 post 方法来进行对事件的提交。post方法的源码如下所示:
/** Posts the given event to the event bus. */
    public void post(Object event) {
        // PostingThreadState 保存着事件队列和线程状态信息
        PostingThreadState postingState = currentPostingThreadState.get();
        // 获取事件队列,并将当前事件插入事件队列
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                // 处理队列中的所有事件
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }
       首先从 PostingThreadState 对象中取出事件队列,然后再将当前的事件插入事件队列。最后将队列中的 事件依次交由 postSingleEvent 方法进行处理,并移除该事件。之后查看 postSingleEvent 方法里做了什么:
 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // eventInheritance 表示是否向上查找事件的父类,默认为true
        if (eventInheritance) {
            //获取所有事件并存放在List中,这里表示事件存在继承关系,向上查找事件的父类
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        // 找不到该事件时的异常处理
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
       eventInheritance 表示是否向上查找事件的父类,它的默认值为 true ,可以通过在 EventBusBuilder 中进行 配置。当eventInheritance true 时,则通过 lookupAllEventTypes 找到所有的父类事件并存在 List 中,然后通过postSingleEventForEventType方法对事件逐一处理。postSingleEventForEventType方法的源码如下所示:
 
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass); // 1
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) { // 2
                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;
    }
       上面代码注释 1 处同步取出该事件对应的 Subscriptions (订阅对象集合)。注释 2 处遍历 Subscriptions , 将事件 event 和对应的 Subscription (订阅对象)传递给 postingState 并调用 postToSubscription 方法对事件进 行处理。接下来查看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 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);
        }
    }
       取出订阅方法的 threadMode (线程模式),之后根据 threadMode 来分别处理。如果 threadMode 是 MAIN,若提交事件的线程是主线程,则通过反射直接运行订阅的方法;若其不是主线程,则需要 mainThreadPoster 将我们的订阅事件添加到主线程队列中。 mainThreadPoster HandlerPoster 类型的,继承 自Handler ,通过 Handler 将订阅方法切换到主线程执行。
 

2.4 订阅者取消注册

       取消注册则需要调用unregister方法,如下所示:

 /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); // 1
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType); // 2
            }
            typesBySubscriber.remove(subscriber); // 3
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
       我们在订阅者注册的过程中讲到过 typesBySubscriber ,它是一个map 集合。上面代码注释 1 处通过 subscriber找到 subscribedTypes (事件类型集合)。注释 3 处将 subscriber 对应的 eventType typesBySubscriber 中移除。注释2 处遍历 subscribedTypes ,并调用 unsubscribeByEventType 方法:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 1
        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--;
                }
            }
        }
    }
       上面代码注释 1 处通过 eventType 来得到对应的 Subscriptions (订阅对象集合),并在 for 循环中判断如果 Subscription (订阅对象)的 subscriber (订阅者)属性等于传进来的 subscriber ,则从 Subscriptions 中移除该 Subscription。
 

总结

  从整个EventBus的执行过程来它,他实际上就是一个典型的观察者模式。通过对事件的发布与订阅,实现了一种一对多的依赖关系,并有效的为我们事件的发送者与接收者之间进行了解耦。

猜你喜欢

转载自blog.csdn.net/lixiong0713/article/details/106539490
今日推荐