EventBus的使用及原理解析

EventBus 是一款在 Android 開(kāi)發(fā)中使用的發(fā)布/訂閱事件總線框架渴析,基于觀察者模式谷扣,將事件的接收者和發(fā)送者分離渣蜗,避免復(fù)雜且容易出錯(cuò)的依賴關(guān)系和生命周期問(wèn)題,簡(jiǎn)化了組件之間的通信棺耍,使用簡(jiǎn)單、效率高种樱、體積忻膳邸!下邊是官方的 EventBus 原理圖:

EventBus 原理圖

一呀舔、使用EventBus

EventBus支持訂閱者方法在不同于發(fā)布事件所在線程的線程中被調(diào)用肢执。你可以使用線程模式來(lái)指定調(diào)用訂閱者方法的線程赃绊。這就需要了解EventBus支持的五種線程模式(ThreadMode):POSTING、MAIN以现、MAIN_ORDERED、BACKGROUND、ASYNC邑遏。

1.線程模式

  • POSTING
    POSTING是默認(rèn)線程模式佣赖,在哪個(gè)線程發(fā)送事件就在對(duì)應(yīng)線程處理事件,避免了線程切換无宿,效率高茵汰。因此,對(duì)于不要求是主線程并且耗時(shí)很短的簡(jiǎn)單任務(wù)推薦使用該模式孽鸡。在線程模型為POSTING的事件處理函數(shù)中盡量避免執(zhí)行耗時(shí)操作蹂午,因?yàn)樗鼤?huì)阻塞事件的傳遞,甚至有可能會(huì)引起ANR彬碱。
  • MAIN
    訂閱者方法將在主線程(UI線程)中被調(diào)用豆胸。因此,可以在該模式的訂閱者方法中直接更新UI界面巷疼。事件處理的時(shí)間不能太長(zhǎng)晚胡,長(zhǎng)了會(huì)導(dǎo)致ANR。
  • MAIN_ORDERED
    訂閱者在主線程中調(diào)用嚼沿。該事件總是排隊(duì)等待以后交付給訂閱者估盘,因此對(duì)post的調(diào)用將立即返回。這為事件處理提供了更嚴(yán)格且更一致的順序骡尽,例如遣妥,在具有MAIN線程模式的事件處理程序中發(fā)布另一個(gè)事件,則第二個(gè)事件處理程序?qū)⒃诘谝粋€(gè)事件處理程序之前完成攀细,事件處理的時(shí)間不能太長(zhǎng)箫踩,長(zhǎng)了會(huì)導(dǎo)致ANR。
  • BACKGROUND
    訂閱者將在后臺(tái)線程中調(diào)用谭贪。如果發(fā)布線程不是主線程境钟,則將在發(fā)布線程中直接調(diào)用事件處理程序方法。如果發(fā)布線程是主線程俭识,則EventBus使用單個(gè)后臺(tái)線程慨削,該線程將按順序傳遞其所有事件。在此事件處理函數(shù)中禁止進(jìn)行UI更新操作套媚。
  • ASYNC
    無(wú)論事件在哪個(gè)線程中發(fā)布缚态,該事件處理函數(shù)都會(huì)在新建的子線程中執(zhí)行;同樣凑阶,此事件處理函數(shù)中禁止進(jìn)行UI更新操作猿规。

2.基本使用

EventBus的使用分4步
(1)定義事件

public static class MessageEvent {... }

(2)準(zhǔn)備訂閱者(指定模式)

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {...};

(3)注冊(cè)/注銷訂閱者(注冊(cè)和注銷是成對(duì)出現(xiàn)的)

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }
 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }

(4)發(fā)送事件

EventBus.getDefault().post(new MessageEvent());

3.使用示例

前面已經(jīng)介紹了基本用法,下面看下使用示例:
首先配置gradle宙橱,如下所示:

implementation 'org.greenrobot:eventbus:3.1.1'

(1)定義消息事件類

public class MessageEvent {
    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

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

(2)訂閱者處理事件

   @Subscribe(threadMode = ThreadMode.MAIN)
    public void onShowEventMessage(MessageEvent messageEvent){
        //將數(shù)據(jù)顯示到頁(yè)面上
        tvMessage.setText(messageEvent.getMessage());
    }

我們?cè)贛ainActivity中創(chuàng)建一個(gè)訂閱者處理事件onShowEventMessage方法姨俩,添加Subscribe注解蘸拔,ThreadMode設(shè)置為MAIN,事件的處理會(huì)在UI線程中執(zhí)行环葵,用TextView來(lái)展示接收到的消息调窍。

(3)注冊(cè)和取消訂閱事件

public class MainActivity extends AppCompatActivity {

    private Button btnJump;
    private Button btnReg;
    private TextView tvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnJump = findViewById(R.id.btn_jump);
        btnReg = findViewById(R.id.btn_reg);
        tvMessage = findViewById(R.id.tv_message);
        btnJump.setOnClickListener(v -> {
            //跳轉(zhuǎn)到SecondActivity
            startActivity(new Intent(MainActivity.this,SecondActivity.class));
        });
        btnReg.setOnClickListener(v -> {
            //注冊(cè)事件
            EventBus.getDefault().register(this);
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注銷事件
        EventBus.getDefault().unregister(this);
    }
}

我們?cè)贛ainAtivity中點(diǎn)擊按鈕注冊(cè)事件,在onDestroy中注銷事件张遭。當(dāng)我們注冊(cè)事件后邓萨,跳轉(zhuǎn)到SecondActivity中去發(fā)布事件。

(4)發(fā)布者發(fā)布事件

public class SecondActivity extends AppCompatActivity {

    private Button btnSend;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        btnSend = findViewById(R.id.btn_send);
        btnSend.setOnClickListener(v -> {
            //發(fā)送事件
            EventBus.getDefault().post(new MessageEvent("我要學(xué)習(xí)EventBus"));
            finish();
        });

    }
}

在SecondActivity中點(diǎn)擊按鈕發(fā)布事件后菊卷,返回到MainActivity中缔恳,可以看到頁(yè)面上顯示的就是我們發(fā)布的消息“我要學(xué)習(xí)EventBus”。在這里就不展示頁(yè)面了洁闰。
EventBus的粘性事件
除了普通事件歉甚,EventBus還支持發(fā)送粘性事件,粘性事件就是在發(fā)送事件之后再訂閱該事件也能收到該事件扑眉,這跟粘性廣播類似纸泄。
(1)訂閱者處理粘性事件

 @Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
    public void onShowStickyEventMessage(MessageEvent messageEvent){
        //顯示粘性事件
        tvMessage.setText(messageEvent.getMessage());
    }

在MainActivity中新寫一個(gè)方法用來(lái)處理粘性事件,可以看到和普通事件基本類似腰素,在注解中多加了一個(gè)sticky = true屬性聘裁。我們先不注冊(cè)事件,直接跳轉(zhuǎn)到MainActivity中弓千,回來(lái)后在點(diǎn)擊注冊(cè)事件衡便,看看效果。
(2)發(fā)送粘性事件

 btnSend.setOnClickListener(v -> {
            //發(fā)送事件
            EventBus.getDefault().postSticky(new MessageEvent("我要學(xué)習(xí)EventBus的粘性事件"));
            finish();
        });

我們?cè)赟econdActivity中點(diǎn)擊按鈕發(fā)送粘性事件计呈,我們可以看到普通事件是post()方法砰诵,而粘性事件是postSticky()方法征唬,這點(diǎn)需要注意捌显。再者,我們發(fā)送后总寒,回到MainActivity中點(diǎn)擊注冊(cè)事件扶歪,我們就可以看到發(fā)送的事件了。

三摄闸、源碼解析EventBus

下面我們看下EventBus的源碼

1.注冊(cè)訂閱者

1.1EventBus構(gòu)造方法
 EventBus.getDefault().register(this);

我們?cè)谑褂肊ventBus時(shí)善镰,首先會(huì)調(diào)用EventBus.getDefault()來(lái)獲取實(shí)例,其中g(shù)etDefault()是一個(gè)單例方法年枕,保證當(dāng)前只有一個(gè)EventBus實(shí)例:

    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

這個(gè)采用了雙重檢查模式的單例模式炫欺,下面看下EventBus的構(gòu)造方法做了什么事情:

  public EventBus() {
        this(DEFAULT_BUILDER);
    }

this調(diào)用了EventBus的另一個(gè)構(gòu)造方法。而DEFAULT_BUILDER是默認(rèn)的EventBusBuilder 熏兄,用來(lái)構(gòu)造EventBus品洛。

 private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

又創(chuàng)建EventBusBuilder對(duì)象树姨,構(gòu)造一個(gè)EventBusBuilder來(lái)對(duì)EventBus進(jìn)行配置。

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        //主線程初始化操作
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        //子線程初始化操作
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        //得到訂閱信息的數(shù)量
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        //訂閱者發(fā)送事件異常
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        //訂閱者沒(méi)有發(fā)送事件
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        //訂閱者拋出異常
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        //訂閱者拋出異常
        executorService = builder.executorService;
    }

這里采用了建造者模式桥状。當(dāng)然我們也可以通過(guò)配置EventBusBuilder來(lái)更改EventBus的屬性帽揪,如下方式也可以注冊(cè):

EventBus.builder()
        .eventInheritance(false)
        .logSubscriberExceptions(false)
        .build()
        .register(this);

下面我們看下register方法

    public void register(Object subscriber) {
        //獲取類 通過(guò)debug可以看出獲取出來(lái)的結(jié)果是com.monkey.eventbus.MainActivity
        Class<?> subscriberClass = subscriber.getClass();
        //找出傳進(jìn)來(lái)的訂閱者的所有訂閱方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        //進(jìn)行同步操作
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

可以看到register()方法主要分為查找和注冊(cè)兩部分。

1.2查找訂閱者的訂閱方法

我們先看下SubscriberMethod都包含了什么:

public class SubscriberMethod {
    final Method method; //方法
    final ThreadMode threadMode;//執(zhí)行線程
    final Class<?> eventType;//接收的事件類型
    final int priority;//優(yōu)先級(jí)
    final boolean sticky;//是否粘性事件
    /** Used for efficient comparison */
    String methodString;

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }
      ...
}

我們可以看到SubscriberMethod 類中主要保存了訂閱方法的Method對(duì)象辅斟,線程模式转晰、事件類型、優(yōu)先級(jí)士飒、是否是粘性事件

下面來(lái)看查找的過(guò)程查邢,findSubscriberMethods()方法:

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //從緩存中查找是否有訂閱方法,返回一個(gè)集合
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        //如果緩存中存在訂閱方法,則直接返回
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        //根據(jù) ignoreGeneratedIndex屬性值來(lái)選擇采用何種方法查找訂閱方法
        //ignoreGeneratedIndex默認(rèn)為false酵幕,是否忽略注解器生成的
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //會(huì)通過(guò)此方法進(jìn)行查找
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            //找到訂閱方法的集合后侠坎,放入緩存,以免下次繼續(xù)查找
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

findSubscriberMethods()流程很清晰裙盾,即先從緩存中查找实胸,如果找到則直接返回,否則去做下一步的查找過(guò)程番官,然后緩存查找到的集合庐完。我們?cè)陧?xiàng)目中經(jīng)常通過(guò)EventBus單例模式來(lái)獲取默認(rèn)的EventBus對(duì)象,也就是ignoreGeneratedIndex為false的情況徘熔,這種情況會(huì)調(diào)用findUsingInfo()方法:

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        //FindState其實(shí)就是一個(gè)里面保存了訂閱者和訂閱方法信息的一個(gè)實(shí)體類门躯,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        //循環(huán)判斷
        while (findState.clazz != 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 {
                // 通過(guò)反射查找訂閱事件的方法
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        //獲取方法并進(jìn)行釋放
        return getMethodsAndRelease(findState);
    }

findUsingInfo()方法會(huì)在當(dāng)前要注冊(cè)的類以及其父類中查找訂閱事件的方法酷师,最后再通過(guò)getMethodsAndRelease方法對(duì)findState做回收處理并返回訂閱方法的List集合讶凉。具體的查找過(guò)程在findUsingReflectionInSingleClass()方法,它主要通過(guò)反射查找訂閱事件的方法:

    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();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        //循環(huán)遍歷當(dāng)前類的方法山孔,篩選出符合條件的
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                // 獲得當(dāng)前方法所有參數(shù)的類型
                Class<?>[] parameterTypes = method.getParameterTypes();
                //保證只有一個(gè)事件參數(shù)
                if (parameterTypes.length == 1) {
                    //得到注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    // 如果當(dāng)前方法使用了Subscribe注解
                    if (subscribeAnnotation != null) {
                        // 得到該參數(shù)的類型
                        Class<?> eventType = parameterTypes[0];
                        // checkAdd()方法用來(lái)判斷FindState的anyMethodByEventType map是否已經(jīng)添加過(guò)以當(dāng)前eventType為key的鍵值對(duì)懂讯,沒(méi)添加過(guò)則返回true
                        if (findState.checkAdd(method, eventType)) {
                            // 得到Subscribe注解的threadMode屬性值,即線程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 創(chuàng)建一個(gè)SubscriberMethod對(duì)象台颠,并添加到subscriberMethods集合
                            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");
            }
        }
    }

查找的過(guò)程褐望,就是對(duì)當(dāng)前類中的方法進(jìn)行遍歷,找到符合條件的方法(即添加了Subscribe注解)串前,獲取相關(guān)屬性值瘫里,添加到SubscriberMethod對(duì)象,將符合條件的對(duì)象添加到subscriberMethods集合中荡碾,即保存到findState中谨读。

1.3訂閱者的注冊(cè)過(guò)程

在查找完訂閱者的訂閱方法后,就要對(duì)其進(jìn)行注冊(cè)坛吁。調(diào)用subscribe()方法進(jìn)行注冊(cè)

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 得到當(dāng)前訂閱了事件的方法的參數(shù)類型
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription類保存了要注冊(cè)的類對(duì)象以及當(dāng)前的subscriberMethod
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // subscriptionsByEventType是一個(gè)HashMap劳殖,保存了以eventType為key,Subscription對(duì)象集合為value的鍵值對(duì)
        // 先查找subscriptionsByEventType是否存在以當(dāng)前eventType為key的值
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            // 如果不存在贼邓,則創(chuàng)建一個(gè)subscriptions,并保存到subscriptionsByEventType
            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();
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                // 添加上邊創(chuàng)建的newSubscription對(duì)象到subscriptions中
                subscriptions.add(i, newSubscription);
                break;
            }
        }
        // typesBySubscriber也是一個(gè)HashMap闷尿,保存了以當(dāng)前要注冊(cè)類的對(duì)象為key塑径,注冊(cè)類中訂閱事件的方法的參數(shù)類型的集合為value的鍵值對(duì)
        // 查找是否存在對(duì)應(yīng)的參數(shù)類型集合
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            // 不存在則創(chuàng)建一個(gè)subscribedEvents,并保存到typesBySubscriber
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        // 保存當(dāng)前訂閱了事件的方法的參數(shù)類型
        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>).
                // stickyEvents就是發(fā)送粘性事件時(shí)填具,保存了事件類型和對(duì)應(yīng)事件
                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);
            }
        }
    }

subscribe()方法需要兩個(gè)參數(shù)统舀,第一個(gè)是訂閱者,第二個(gè)是訂閱者方法劳景,然后將符合條件的訂閱者信息保存到subscriptionsByEventType誉简、typesBySubscriber兩個(gè)HashMap中,當(dāng)我們?cè)诎l(fā)送事件的時(shí)候要用到subscriptionsByEventType盟广,完成事件的處理闷串,當(dāng)取消 EventBus 注冊(cè)的時(shí)候要用到typesBySubscriber、subscriptionsByEventType筋量,完成相關(guān)資源的釋放烹吵。
處理粘性事件就是在 EventBus 注冊(cè)時(shí),遍歷stickyEvents桨武,如果當(dāng)前要注冊(cè)的事件訂閱方法是粘性的肋拔,并且該方法接收的事件類型和stickyEvents中某個(gè)事件類型相同或者是其父類,則取出stickyEvents中對(duì)應(yīng)事件類型的具體事件呀酸,通過(guò)checkPostStickyEventToSubscription()方法做進(jìn)一步處理凉蜂。

    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
            // --> Strange corner case, which we don't take care of here.
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

最終還是通過(guò)postToSubscription()方法完成粘性事件的處理,這就是粘性事件的整個(gè)處理流程性誉。

注冊(cè)訂閱者總結(jié):
(1).首先用register()方法注冊(cè)一個(gè)訂閱者
(2).獲取該訂閱者的所有訂閱的方法
(3).根據(jù)該訂閱者的所有訂閱的事件類型窿吩,將訂閱者存入到每個(gè)以 事件類型為key 以所有訂閱者為values的map集合中
(4).然后將訂閱事件添加到以訂閱者為key 以訂閱者所有訂閱事件為values的map集合中
(5).如果是訂閱了粘性事件的訂閱者,從粘性事件緩存區(qū)獲取之前發(fā)送過(guò)的粘性事件错览,響應(yīng)這些粘性事件纫雁。

2.注銷訂閱者

下面看下注銷訂閱者

 EventBus.getDefault().unregister(this);

注銷訂閱者是調(diào)用unregister()方法

    public synchronized void unregister(Object subscriber) {
       // 得到當(dāng)前注冊(cè)類對(duì)象 對(duì)應(yīng)的訂閱事件方法的參數(shù)類型的集合
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
           // 遍歷參數(shù)類型集合,釋放之前緩存的當(dāng)前類中的Subscription
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            // 刪除以subscriber為key的鍵值對(duì)
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        // 得到當(dāng)前參數(shù)類型對(duì)應(yīng)的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) {
                    // 如果當(dāng)前subscription對(duì)象對(duì)應(yīng)的注冊(cè)類對(duì)象和要取消注冊(cè)的注冊(cè)類對(duì)象相同蝗砾,則刪除當(dāng)前subscription對(duì)象
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

我們可以看到在unregister()方法中先较,釋放了typesBySubscriber携冤、subscriptionsByEventType中緩存的資源悼粮。

注銷訂閱者總結(jié):
(1).首先通過(guò)unregister方法拿到要取消的訂閱者
(2).得到該訂閱者的所有訂閱事件類型
(3).遍歷事件類型,根據(jù)每個(gè)事件類型獲取到所有的訂閱者集合曾棕,并從集合中刪除該訂閱者

3.發(fā)送及處理事件

當(dāng)我們要發(fā)送普通事件扣猫,就需要用到post()方法,
當(dāng)我們要發(fā)送粘性事件翘地,就需要用到postSticky()方法

//發(fā)送普通事件
EventBus.getDefault().post("我要學(xué)習(xí)EventBus")申尤;
//發(fā)送粘性事件
EventBus.getDefault().postSticky(new MessageEvent("我要學(xué)習(xí)EventBus的粘性事件"));
3.1粘性事件 postSticky()方法
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

postSticky()方法主要做了兩件事癌幕,先將事件類型和對(duì)應(yīng)事件保存到stickyEvents中,方便后續(xù)使用昧穿;然后執(zhí)行post(event)繼續(xù)發(fā)送事件勺远,這個(gè)post()方法就是之前發(fā)送普通消息的post()方法。所以时鸵,如果在發(fā)送粘性事件前胶逢,已經(jīng)有了對(duì)應(yīng)類型事件的訂閱者,不是非粘性的饰潜,依然可以接收到發(fā)送出的粘性事件初坠。

3.2普通事件 post()方法
    public void post(Object event) {
       //currentPostingThreadState是一個(gè)PostingThreadState類型的ThreadLocal
       // PostingThreadState類保存了事件隊(duì)列和線程模式等信息
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        // 將要發(fā)送的事件添加到事件隊(duì)列
        eventQueue.add(event);
        // isPosting默認(rèn)為false
        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()) {
                    // 發(fā)送單個(gè)事件,并且從事件隊(duì)列移除事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

post()方法先將發(fā)送的事件保存的事件隊(duì)列,然后通過(guò)循環(huán)出隊(duì)列彭雾,將事件交給postSingleEvent()方法處理:

   private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // eventInheritance默認(rèn)為true碟刺,表示是否向上查找事件的父類
        if (eventInheritance) {
            // 查找當(dāng)前事件類型的Class,連同當(dāng)前事件類型的Class保存到集合
            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) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

postSingleEvent()方法中薯酝,根據(jù)eventInheritance屬性半沽,決定是否向上遍歷事件的父類型,然后用postSingleEventForEventType()方法進(jìn)一步處理事件:

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
          // 獲取事件類型對(duì)應(yīng)的Subscription集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                // 記錄事件
                postingState.event = event;
                // 記錄對(duì)應(yīng)的subscription
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                    // 最終的事件處理
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    //將postingState置為初始狀態(tài)
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

遍歷發(fā)送的事件類型對(duì)應(yīng)的Subscription集合吴菠,然后調(diào)用postToSubscription()方法處理事件抄囚。

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        // 判斷訂閱事件方法的線程模式
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                // 默認(rèn)的線程模式,在那個(gè)線程發(fā)送事件就在那個(gè)線程處理事件
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    // 如果在主線程發(fā)送事件橄务,則直接在主線程通過(guò)反射處理事件
                    invokeSubscriber(subscription, event);
                } else {
                    // 如果是在子線程發(fā)送事件幔托,則將事件入隊(duì)列,通過(guò)Handler切換到主線程執(zhí)行處理事件
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    // 無(wú)論在那個(gè)線程發(fā)送事件蜂挪,都先將事件入隊(duì)列重挑,然后通過(guò) Handler 切換到主線程,依次處理事件棠涮。
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    // 如果在主線程發(fā)送事件谬哀,則先將事件入隊(duì)列,然后通過(guò)線程池依次處理事件
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    // 如果在子線程發(fā)送事件严肪,則直接在發(fā)送事件的線程通過(guò)反射處理事件
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                // 無(wú)論在那個(gè)線程發(fā)送事件史煎,都將事件入隊(duì)列,然后通過(guò)線程池處理驳糯。
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

可以看到篇梭,postToSubscription()方法就是根據(jù)訂閱事件方法的線程模式、以及發(fā)送事件的線程來(lái)判斷如何處理事件酝枢。
發(fā)送事件總結(jié):
(1).首先獲取當(dāng)前線程的事件隊(duì)列
(2).將要發(fā)送的事件添加到事件隊(duì)列中
(3).根據(jù)發(fā)送事件類型獲取所有的訂閱者
(4).根據(jù)響應(yīng)方法的執(zhí)行模式恬偷,在相應(yīng)線程通過(guò)反射執(zhí)行訂閱者的訂閱方法

4.Subscribe注解

EventBus3.0 開(kāi)始用Subscribe注解配置事件訂閱方法,不再使用方法名了帘睦,如:

 @Subscribe(threadMode = ThreadMode.MAIN)
    public void onShowEventMessage(MessageEvent messageEvent) {
        tvMessage.setText(messageEvent.getMessage());
    }

看下Subscribe注解的實(shí)現(xiàn):

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // 指定事件訂閱方法的線程模式袍患,即在那個(gè)線程執(zhí)行事件訂閱方法處理事件坦康,默認(rèn)為POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
    // 是否支持粘性事件,默認(rèn)為false
    boolean sticky() default false;
    // 指定事件訂閱方法的優(yōu)先級(jí)诡延,默認(rèn)為0滞欠,如果多個(gè)事件訂閱方法可以接收相同事件的,則優(yōu)先級(jí)高的先接收到事件
    int priority() default 0;
}

所以在使用Subscribe注解時(shí)可以根據(jù)需求指定threadMode肆良、sticky仑撞、priority三個(gè)屬性。關(guān)于threadMode的屬性妖滔,文章開(kāi)始已經(jīng)介紹了隧哮。

關(guān)于EventBus的源碼就學(xué)習(xí)到這里,如理解有誤座舍,還望指正沮翔!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市曲秉,隨后出現(xiàn)的幾起案子采蚀,更是在濱河造成了極大的恐慌,老刑警劉巖承二,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榆鼠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡亥鸠,警方通過(guò)查閱死者的電腦和手機(jī)妆够,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)负蚊,“玉大人神妹,你說(shuō)我怎么就攤上這事〖易保” “怎么了鸵荠?”我有些...
    開(kāi)封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伤极。 經(jīng)常有香客問(wèn)我蛹找,道長(zhǎng),這世上最難降的妖魔是什么哨坪? 我笑而不...
    開(kāi)封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任庸疾,我火速辦了婚禮,結(jié)果婚禮上齿税,老公的妹妹穿的比我還像新娘彼硫。我一直安慰自己,他們只是感情好凌箕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布拧篮。 她就那樣靜靜地躺著,像睡著了一般牵舱。 火紅的嫁衣襯著肌膚如雪串绩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天芜壁,我揣著相機(jī)與錄音礁凡,去河邊找鬼。 笑死慧妄,一個(gè)胖子當(dāng)著我的面吹牛顷牌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播塞淹,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼窟蓝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了饱普?” 一聲冷哼從身側(cè)響起运挫,我...
    開(kāi)封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎套耕,沒(méi)想到半個(gè)月后谁帕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冯袍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年匈挖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片康愤。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡关划,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出翘瓮,到底是詐尸還是另有隱情贮折,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布资盅,位于F島的核電站调榄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏呵扛。R本人自食惡果不足惜每庆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望今穿。 院中可真熱鬧缤灵,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至胚嘲,卻和暖如春作儿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背馋劈。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工攻锰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妓雾。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓娶吞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親械姻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子妒蛇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容