觀察者模式
自己對觀察者模式的理解:
定義:Define a one-to-many dependency between objects so that when one object changes state, all its dependents aer notified and updated automatically.
意圖:定義對象間的一種一對多的依賴關(guān)系庆揩,當(dāng)一個(gè)對象改變狀態(tài)時(shí)喻频,則所有依賴于它的對象都會(huì)得到通知并被自動(dòng)更新茸俭。
主要解決:一個(gè)對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合寨典,保證高度的協(xié)作。
使用場景:
1房匆、有多個(gè)子類共有的方法耸成,且邏輯相同。
2、重要的、復(fù)雜的方法怔球,可以考慮作為模板方法祖灰。
優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
1、觀察者和被觀察者是抽象耦合的侮攀。
2、建立一套觸發(fā)機(jī)制扫尺。缺點(diǎn):
1约急、如果一個(gè)被觀察者對象有很多的直接和間接的觀察者的話零远,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。
2厌蔽、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話牵辣,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰奴饮。
3服猪、觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化拐云。
注意事項(xiàng):
- JAVA 中已經(jīng)有了對觀察者模式的支持類罢猪。
- 避免循環(huán)引用。
- 如果順序執(zhí)行叉瘩,某一觀察者錯(cuò)誤會(huì)導(dǎo)致系統(tǒng)卡殼膳帕,一般采用異步方式。
觀察者模式的組成:
- 觀察者薇缅,我們稱它為Observer危彩,有時(shí)候我們也稱它為訂閱者,即Subscriber泳桦。
- 被觀察者汤徽,我們稱它為Observable,即可以被觀察的東西灸撰,有時(shí)候還會(huì)稱之為主題谒府,即Subject。
擼起袖子浮毯,為了驗(yàn)證我們的觀察者模式完疫,一波觀察者模式的場景,首先定義一個(gè)Theif為觀察目標(biāo)债蓝。
public class Theif {
//描述小偷形象
private String description;
public Theif(String description){
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "一名"+description+"小偷在進(jìn)行扒竊";
}
}
定義我們的被觀察者壳鹤,我們希望能夠通用,所以定義成泛型饰迹。泛型指具體的被觀察者芳誓,內(nèi)部暴露出register和unRegister方法供觀察者訂閱和取消訂閱,至于觀察者們的保存啊鸭,我們用ArrayList集合儲(chǔ)存即可锹淌,另外,當(dāng)小偷發(fā)生變化的時(shí)候莉掂,需要通知觀察者來做出響應(yīng)葛圃,還需要一個(gè)notifyObservers方法。
public class Observable<T> {
//|存放觀察者們
List<Observer<T>> mObservers = new ArrayList<>();
//供觀察者訂閱 傳入觀察者即可
public void register(Observer<T> observer) {
if (observer == null) {
throw new NullPointerException("observer == null");
}
//多線程 添加同步鎖
// 如果線程A進(jìn)入了該代碼,線程B 在等待库正,這是A線程創(chuàng)建完一個(gè)實(shí)例出來后曲楚,線程B 獲得鎖進(jìn)入同步代碼,
// 實(shí)例已經(jīng)存在褥符,木有必要再添加一個(gè)
synchronized (this) {
if (!mObservers.contains(observer))
mObservers.add(observer);
}
}
//根據(jù)傳入的觀察者 供觀察者取消訂閱
public synchronized void unregister(Observer<T> observer) {
mObservers.remove(observer);
}
//通知觀察者們
public void notifyObservers(T data) {
for (Observer<T> observer : mObservers) {
observer.onFindThief(this, data);
}
}
}
定義Observer接口為觀察者龙誊,接收到消息后,即進(jìn)行更新操作喷楣,對接收到的信息進(jìn)行處理趟大。觀察者們 只需要實(shí)現(xiàn)Observer接口,該接口也是泛型的
public interface Observer<T> {
//觀察者發(fā)現(xiàn)目標(biāo),通過調(diào)用這個(gè)方法铣焊,并攜帶被觀察者和數(shù)據(jù) 進(jìn)行更新
void onFindThief(Observable<T> observable,T data);
}
一旦訂閱逊朽,發(fā)現(xiàn)小偷有變化,就會(huì)調(diào)用onFindThief接口曲伊。一定要用register方法來注冊叽讳,否則觀察者收不到變化的信息,而一旦不感興趣坟募,就可以調(diào)用unregister方法岛蚤。
public class TestJava {
public static void main(String agr[]){
//一旦訂閱,發(fā)現(xiàn)小偷有變化懈糯,就會(huì)調(diào)用onFindThief接口
//Observable被觀察者 被觀察者的對象是小偷(Theif)
Observable<Theif> observable=new Observable<>();
//為了簡便直接在這里創(chuàng)建一個(gè)觀察者對象涤妒。觀察者1 對小偷進(jìn)行觀察,一旦發(fā)現(xiàn)小偷就打印出目標(biāo)
Observer<Theif> observer1 = new Observer<Theif>() {
@Override
public void onFindThief(Observable<Theif> observable, Theif data) {
System.out.println("觀察者1——發(fā)現(xiàn)目標(biāo):"+data.toString());
}
};
//觀察者2 對小偷進(jìn)行觀察赚哗,一旦發(fā)現(xiàn)小偷就打印出目標(biāo)
Observer<Theif> observer2 = new Observer<Theif>() {
@Override
public void onFindThief(Observable<Theif> observable, Theif data) {
System.out.println("觀察者2——發(fā)現(xiàn)目標(biāo):"+data.toString());
}
};
//一定要用register方法來注冊她紫,否則觀察者收不到變化的信息,而一旦不感興趣蜂奸,就可以調(diào)用unregister方法
observable.register(observer1);
observable.register(observer2);
//目標(biāo)
Theif theif = new Theif("穿紅色外套");
//被觀察的目標(biāo)有變化犁苏,通知觀察者們
observable.notifyObservers(theif);
//觀察者1 吃飯時(shí)間到,不再觀察了 調(diào)用unregister方法注銷被觀察對象扩所,其他觀察者還有觀察
observable.unregister(observer1);
//目標(biāo)
Theif theif2 = new Theif("中年男子,染黃頭發(fā)");
//被觀察的目標(biāo)有變化朴乖,通知觀察者們
observable.notifyObservers(theif2);
}
}
輸出結(jié)果為:
觀察者1——發(fā)現(xiàn)目標(biāo):一名穿紅色外套小偷在進(jìn)行扒竊
觀察者2——發(fā)現(xiàn)目標(biāo):一名穿紅色外套小偷在進(jìn)行扒竊
觀察者2——發(fā)現(xiàn)目標(biāo):一名中年男子祖屏,染黃頭發(fā)小偷在進(jìn)行扒竊
一個(gè)簡單的觀察者模式就出來了,當(dāng)然上面的代碼還可以優(yōu)化买羞。有興趣的可以自己進(jìn)一步優(yōu)化袁勺。
小結(jié)
觀察者模式主要包括兩個(gè)部分:
- Subject被觀察者。是一個(gè)接口或者是抽象類畜普,定義被觀察者必須實(shí)現(xiàn)的職責(zé)期丰,它必須能偶動(dòng)態(tài)地增加、取消觀察者,管理觀察者并通知觀察者钝荡。
- Observer觀察者街立。觀察者接收到消息后,即進(jìn)行更新操作埠通,對接收到的信息進(jìn)行處理赎离。
- ConcreteSubject具體的被觀察者。定義被觀察者自己的業(yè)務(wù)邏輯端辱。
- ConcreteObserver具體觀察者梁剔。每個(gè)觀察者在接收到信息后處理的方式不同,各個(gè)觀察者有自己的處理邏輯舞蔽。
- 上面的例子Observable為被觀察者 被觀察者的對象是小偷(Theif)荣病,也即是ConcreteSubject具體的被觀察者
- 上面的例子:可以自己定義一個(gè)類并實(shí)現(xiàn)Observer接口,那么這個(gè)類就是ConcreteObserver具體觀察者渗柿。
開源框架EventBus众雷、Rxjava等也是基于觀察者模式的,觀察者模式的注冊做祝,取消砾省,發(fā)送事件三個(gè)典型方法都有。以EventBus3.0作為例子混槐,分析其源碼编兄。
什么是EventBus
EventBus是一個(gè)消息總線,以觀察者模式實(shí)現(xiàn)声登,用于簡化程序的組件狠鸳、線程通信,可以輕易切換線程悯嗓、開辟線程件舵。EventBus3.0跟先前版本的區(qū)別在于加入了annotation @Subscribe,取代了以前約定命名的方式
優(yōu)點(diǎn)
開銷小脯厨,代碼優(yōu)雅铅祸。將發(fā)送者和接受者解耦。
EventBus的用法——常用的幾個(gè)方法
EventBus.getDefault().register(Object subscriber);
EventBus.getDefault().unregister(Object subscriber);
EventBus.getDefault().post(Object event);
EventBus.getDefault().postSticky(Object event);
當(dāng)我們要調(diào)用EventBus的功能時(shí)合武,比如注冊或者發(fā)送事件等临梗,總會(huì)調(diào)用EventBus.getDefault()來獲取EventBus實(shí)例
//源碼
static volatile EventBus defaultInstance;
/** 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;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
從源碼看出很明顯這是一個(gè)單例模式。如果對單例模式感興趣可以去研究一下單例模式稼跳。一看構(gòu)造函數(shù)this(DEFAULT_BUILDER)做了些什么盟庞。這里DEFAULT_BUILDER是默認(rèn)的EventBusBuilder用來構(gòu)造EventBus:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
進(jìn)入EventBusBuilder類看看,這么多個(gè)可選的配置屬性,這里變量含義大家直接看我的注釋,就不多作解釋了汤善。我們主要看最終的建造方法:
/**
* 根據(jù)參數(shù)創(chuàng)建對象,并賦值給EventBus.defaultInstance, 必須在默認(rèn)的eventbus對象使用以前調(diào)用
*
* @throws EventBusException if there's already a default EventBus instance in place
*/
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure " +
"consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
/**
* 根據(jù)參數(shù)創(chuàng)建對象
*/
public EventBus build() {
return new EventBus(this);
}
EventBusBuilder類提供了兩種建造方法,還記得之前的getDefault()方法嗎,維護(hù)了一個(gè)單例對象,installDefaultEventBus() 方法建造的EventBus對象最終會(huì)賦值給那個(gè)單例對象,但是有一個(gè)前提就是我們之前并沒有創(chuàng)建過那個(gè)單例對象.
為什么如果EventBus.defaultInstance不為null程序要拋出異常什猖?先把這個(gè)問題留著票彪,繼續(xù)往下看。
回到EventBus的構(gòu)造方法中不狮,this(DEFAULT_BUILDER)是指EventBus一個(gè)帶實(shí)參的構(gòu)造函數(shù):
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
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類提供了這么多個(gè)可選的配置屬性,這里Map變量含義大家直接看我的注釋降铸。EventBus構(gòu)造函數(shù)也涉及到了構(gòu)造模式,感興趣的可以查閱一下荤傲,這里就不多說了垮耳。
這里說說EventBus中的三個(gè)Poster類(HandlerPoster、BackgroundPoster遂黍、AsyncPoster)终佛。三個(gè)Poster類的作用是什么?Poster應(yīng)該是發(fā)布時(shí)候會(huì)涉及到雾家,不過我們之后看代碼再作解答铃彰,先解決這三個(gè)類,看代碼芯咧。
private final HandlerPoster mainThreadPoster; //前臺發(fā)送者
private final BackgroundPoster backgroundPoster; //后臺發(fā)送者
private final AsyncPoster asyncPoster; //后臺發(fā)送者(只讓隊(duì)列第一個(gè)待訂閱者去響應(yīng))
這幾個(gè)Poster的設(shè)計(jì)可以說是整個(gè)EventBus的一個(gè)經(jīng)典部分牙捉。每個(gè)Poster中都有一個(gè)發(fā)送任務(wù)隊(duì)列,PendingPostQueue queue。進(jìn)到隊(duì)列PendingPostQueue里面再看敬飒。
private PendingPost head; //待發(fā)送對象隊(duì)列頭節(jié)點(diǎn)
private PendingPost tail;//待發(fā)送對象隊(duì)列尾節(jié)點(diǎn)
接著我們再看這個(gè)PendingPost類的實(shí)現(xiàn):
//單例池,復(fù)用對象
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event; //事件類型
Subscription subscription; //訂閱者
PendingPost next; //隊(duì)列下一個(gè)待發(fā)送對象
首先是提供了一個(gè)池的設(shè)計(jì)邪铲,類似于我們的線程池,目的是為了減少對象創(chuàng)建的開銷无拗,當(dāng)一個(gè)對象不用了带到,我們可以留著它,下次再需要的時(shí)候返回這個(gè)保留的而不是再去創(chuàng)建英染。接著看最后的變量揽惹,PendingPost next 非常典型的隊(duì)列設(shè)計(jì),隊(duì)列中每個(gè)節(jié)點(diǎn)都有一個(gè)指向下一個(gè)節(jié)點(diǎn)的指針四康。
/**
* 首先檢查復(fù)用池中是否有可用,如果有則返回復(fù)用,否則返回一個(gè)新的
*
* @param subscription 訂閱者
* @param event 訂閱事件
* @return 待發(fā)送對象
*/
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
/**
* 回收一個(gè)待發(fā)送對象,并加入復(fù)用池
*
* @param pendingPost 待回收的待發(fā)送對象
*/
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// 防止池?zé)o限增長
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
obtainPendingPost()方法中對池復(fù)用的實(shí)現(xiàn)搪搏,每次新創(chuàng)建的節(jié)點(diǎn)尾指針都為 null 。細(xì)心的人還看到了一個(gè)關(guān)鍵字synchronized闪金。Synchronized是Java并發(fā)編程中最常用的用于保證線程安全的方式疯溺,其使用相對也比較簡單。(但是如果能夠深入了解其原理毕泌,對監(jiān)視器鎖等底層知識有所了解喝检,一方面可以幫助我們正確的使用Synchronized關(guān)鍵字,另一方面也能夠幫助我們更好的理解并發(fā)編程機(jī)制撼泛,有助我們在不同的情況下選擇更優(yōu)的并發(fā)策略來完成任務(wù)。對平時(shí)遇到的各種并發(fā)問題澡谭,也能夠從容的應(yīng)對愿题。不過這都是題個(gè)話了损俭,有興趣可以多了解一下。)
releasePendingPost()潘酗,回收pendingPost對象杆兵,既然可以從池中取,當(dāng)然需要可以存仔夺。非常細(xì)心的可以注意到池的大小小于10000琐脏,不禁會(huì)想這是為什么呢,想表達(dá)什么呢缸兔?日裙,if (pendingPostPool.size() < 10000) 其實(shí)我覺得10000都很大了,1000就夠了惰蜜,我們一次只可能創(chuàng)建一個(gè)pendingPost昂拂,如果ArrayList里面存了上千條都沒有取走,那么肯定是使用出錯(cuò)了抛猖。當(dāng)然ArrayList里不會(huì)存放那么多池格侯,我只能猜測他們也只是預(yù)防一下,但也不是不可能不存在這樣的情況财著。
三個(gè)Poster類的最底層都是PendingPost類联四,PendingPost的代碼我們就看完了。那么繼續(xù)往上一級PendingPostQueue類了解撑教。PendingPostQueue這是一個(gè)隊(duì)列的設(shè)計(jì)朝墩。那么我們首先看看PendingPostQueue的入隊(duì)方法:
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
從源碼可以看出首先來個(gè)判空處理。接著將當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)(入隊(duì)前整個(gè)隊(duì)列的最后一個(gè)節(jié)點(diǎn))的尾指針指向當(dāng)期正在入隊(duì)的節(jié)點(diǎn)(傳入的參數(shù)pendingPost)驮履,并將隊(duì)列的尾指針指向自己(自己變成隊(duì)列的最后一個(gè)節(jié)點(diǎn))鱼辙,這樣就完成了入隊(duì)。如果是隊(duì)列的第一個(gè)元素(隊(duì)列之前是空的),那么直接將隊(duì)列的頭尾兩個(gè)指針都指向自身就行了玫镐。
接著看看PendingPostQueue的出隊(duì)也是類似的隊(duì)列指針操作:
synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
首先將出隊(duì)前的頭節(jié)點(diǎn)保留一個(gè)臨時(shí)變量(它就是要出隊(duì)的節(jié)點(diǎn)),拿到這個(gè)將要出隊(duì)的臨時(shí)變量的下一個(gè)節(jié)點(diǎn)指針倒戏,將出隊(duì)前的第二個(gè)元素(出隊(duì)后的第一個(gè)元素)的賦值為現(xiàn)在隊(duì)列的頭節(jié)點(diǎn),出隊(duì)完成恐似。
非常細(xì)心的人會(huì)發(fā)現(xiàn) PendingPostQueue的所有方法都聲明了synchronized杜跷。這意味著在多線程下它依舊可以正常工作。如果對synchronized還是不太了解的可以查閱資料矫夷。
既然所有方法都聲明了synchronized葛闷,那就必定有他的道理。那么我們往上一級看看就知道方法前添加synchronized關(guān)鍵字是不是存在的意義双藕。接著看HandlerPoster的入隊(duì)方法enqueue():
/**
* @param subscription 訂閱者
* @param event 訂閱事件
*/
void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
入隊(duì)方法會(huì)根據(jù)參數(shù)創(chuàng)建淑趾,待發(fā)送對象pendingPost并加入隊(duì)列,從代碼上可以看出驗(yàn)證了synchronized關(guān)鍵字是有存在的意義。 handleMessage()沒有在運(yùn)行中,則發(fā)送一條空消息讓handleMessage響應(yīng)忧陪,那繼續(xù)看看handleMessage()方法里做了些什么:
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// 雙重校驗(yàn),類似單例中的實(shí)現(xiàn)
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
//如果訂閱者沒有取消注冊,則分發(fā)消息
eventBus.invokeSubscriber(pendingPost);
//如果在一定時(shí)間內(nèi)仍然沒有發(fā)完隊(duì)列中所有的待發(fā)送者,則退出
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
handleMessage()不停的在待發(fā)送隊(duì)列queue中去取消息扣泊。直得注意的是在循環(huán)之外有個(gè)臨時(shí)boolean變量rescheduled,最后是通過這個(gè)值去修改了handlerActive近范。而 handlerActive 是用來判斷當(dāng)前queue中是否有正在發(fā)送對象的任務(wù),看到上面的入隊(duì)方法enqueue(),如果已經(jīng)有任務(wù)在跑著了延蟹,就不需要再去sendMessage()喚起我們的handleMessage()评矩。最終通過eventBus對象的invokeSubscriber()最終發(fā)送出去,并回收這個(gè)pendingPost阱飘。那么這里有個(gè)問題了斥杜,什么時(shí)候才會(huì)通過eventBus對象的invokeSubscriber()發(fā)送出去?HandlePoster類已經(jīng)看完了沥匈,其實(shí)三個(gè)Poster類里面的實(shí)現(xiàn)都很相似易懂的蔗喂,以HandlePoster類來介紹,另外兩個(gè)類(BackgroundPoster、AsyncPoster)異步的發(fā)送者實(shí)現(xiàn)代碼也差不多,唯一的區(qū)別就是另外兩個(gè)是工作在異步,實(shí)現(xiàn)的Runnable接口咐熙。
整理一下上面出現(xiàn)和問題:
- 為什么如果EventBus.defaultInstance不為null程序要拋出異常弱恒?
- 三個(gè)Poster類(HandlerPoster、BackgroundPoster棋恼、AsyncPoster)的作用是什么返弹?
- invokeSubscriber()什么時(shí)候才觸發(fā)發(fā)送出去?
回顧一下Poster爪飘、PendingPostQueue义起、PendingPost這三個(gè)類,是不是有種似曾相識的感覺师崎。哈哈默终,沒有錯(cuò)。那是Handler犁罩、Message齐蔽、Looper的工作原理。如果不了解Handler可以查閱一下資料床估。
EventBus注冊
獲取到EventBus后含滴,可以將訂閱者注冊到EventBus中,下面來看一下register方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 用 subscriberMethodFinder 提供的方法丐巫,找到在 subscriber 這個(gè)類里面訂閱的內(nèi)容谈况。
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
通過SubscriberMethodFinder#findSubscriberMethods方法找出一個(gè)SubscriberMethod的集合,也就是傳進(jìn)來的訂閱者所有的訂閱的方法递胧,接下來遍歷訂閱者的訂閱方法來完成訂閱者的訂閱操作碑韵。對于SubscriberMethod(訂閱方法)類中,主要就是用保存訂閱方法的Method對象缎脾、線程模式祝闻、事件類型、優(yōu)先級遗菠、是否是粘性事件等屬性治筒,就不貼代碼了屉栓。那說說SubscriberMethodFinder類中的findSubscriberMethods方法吧舷蒲,SubscriberMethodFinder從字面理解耸袜,就是訂閱者方法發(fā)現(xiàn)者。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//從緩存中獲取SubscriberMethod集合
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex屬性表示是否忽略注解器生成的MyEventBusIndex
if (ignoreGeneratedIndex) {
//通過反射獲取subscriberMethods
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//在獲得subscriberMethods以后牲平,如果訂閱者中不存在@Subscribe注解并且為public的訂閱方法堤框,則會(huì)拋出異常。
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;
}
}
首先從緩存中查找纵柿,如果找到了就返回subscriberMethods蜈抓。如果緩存中沒有的話,則根據(jù) ignoreGeneratedIndex選擇如何查找訂閱方法昂儒,ignoreGeneratedIndex屬性表示是否忽略注解器生成的MyEventBusIndex沟使。ignoreGeneratedIndex 默認(rèn)就是false,如果ignoreGeneratedIndex是true 那么通反射獲取subscriberMethods渊跋,可以通過EventBusBuilder來設(shè)置它的值腊嗡。最后,找到訂閱方法后拾酝,放入緩存燕少,以免下次繼續(xù)查找。
可以看到其中findUsingInfo()方法就是去索引中查找訂閱者的回調(diào)方法蒿囤,我們戳進(jìn)去看看這個(gè)方法的實(shí)現(xiàn):
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 最新版的EventBus3中客们,尋找方法時(shí)所需的臨時(shí)變量都被封裝到了FindState這個(gè)靜態(tài)內(nèi)部類中
FindState findState = prepareFindState(); // 到對象池中取得上下文,避免頻繁創(chuàng)造對象材诽,這個(gè)設(shè)計(jì)很贊
findState.initForSubscriber(subscriberClass); // 初始化尋找方法的上下文
while (findState.clazz != null) { // 子類找完了底挫,會(huì)繼續(xù)去父類中找
findState.subscriberInfo = getSubscriberInfo(findState); // 獲得訂閱者類的相關(guān)信息
if (findState.subscriberInfo != null) { // 上一步能拿到相關(guān)信息的話,就開始把方法數(shù)組封裝成List
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
// checkAdd是為了避免在父類中找到的方法是被子類重寫的脸侥,此時(shí)應(yīng)該保證回調(diào)時(shí)執(zhí)行子類的方法
findState.subscriberMethods.add(subscriberMethod);
}
}
} else { // 索引中找不到建邓,降級成運(yùn)行時(shí)通過注解和反射去找
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass(); // 上下文切換成父類
}
return getMethodsAndRelease(findState); // 找完后,釋放FindState進(jìn)對象池湿痢,并返回找到的回調(diào)方法
}
通過prepareFindState()找到所需的臨時(shí)變量都被封裝到了FindState這個(gè)靜態(tài)內(nèi)部類中涝缝,初始化尋找方法的上下文,子類找完了譬重,會(huì)繼續(xù)去父類中找拒逮。然后getSubscriberInfo方法來獲取訂閱者信息。如果獲得到訂閱者類的相關(guān)信息臀规,不僅會(huì)去遍歷父類滩援,而且還會(huì)避免因?yàn)橹貙懛椒▽?dǎo)致執(zhí)行多次回調(diào),過慮了重重保險(xiǎn)關(guān)卡塔嬉,然后subscriberMethods添加subscriberMethod玩徊。如果沒有獲得到訂閱者類的相關(guān)信息租悄,便會(huì)執(zhí)行findUsingReflectionInSingleClass方法,將訂閱方法保存到findState中恩袱。最后再通過getMethodsAndRelease方法對findState做回收處理并反回訂閱方法的List集合泣棋。其中需要關(guān)心的是getSubscriberInfo()是如何返回索引數(shù)據(jù)的。
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { // subscriberInfo已有實(shí)例畔塔,證明本次查找需要查找上次找過的類的父類
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) { // 確定是所需查找的類
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) { // 從我們傳進(jìn)來的subscriberInfoIndexes中獲取相應(yīng)的訂閱者信息
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) { return info; }
}
}
return null;
}
在getSubscriberInfo方法中潭辈,傳入的實(shí)參如果已有實(shí)例,證明本次查找需要查找上次找過的類的父類澈吨。如果沒有實(shí)例把敢,應(yīng)用到了我們生成的索引,避免我們需要在findSubscriberMethods時(shí)去調(diào)用耗時(shí)的findUsingReflection方法谅辣。關(guān)于注解為我們生成的索引這里先不解答修赞,后面會(huì)有代碼解說(在我們開始查找訂閱方法的時(shí)候并沒有忽略注解器為我們生成的索引MyEventBusIndex,如果我們通過EventBusBuilder配置了MyEventBusIndex桑阶,便會(huì)獲取到subscriberInfo柏副,調(diào)用subscriberInfo的getSubscriberMethods方法便可以得到訂閱方法相關(guān)的信息,這個(gè)時(shí)候就不在需要通過注解進(jìn)行獲取訂閱方法联逻。如果沒有配置MyEventBusIndex搓扯,便會(huì)執(zhí)行findUsingReflectionInSingleClass方法,將訂閱方法保存到findState中包归。)
下面就來看一下findUsingReflectionInSingleClass的執(zhí)行過程:
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;
}
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 subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
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");
}
}
}
在這里主要是使用了Java的反射和對注解的解析锨推。首先通過反射來獲取訂閱者中所有的方法。并根據(jù)方法的類型公壤,參數(shù)和注解來找到訂閱方法换可。找到訂閱方法后將訂閱方法相關(guān)信息保存到FindState當(dāng)中。
當(dāng)獲取到了subscriberMethods數(shù)據(jù)集厦幅,接著開始對subscriberMethods數(shù)據(jù)集遍歷沾鳄,在訂閱方法里進(jìn)行注冊:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
////根據(jù)傳入的響應(yīng)方法名獲取到響應(yīng)事件(參數(shù)類型)
Class<?> eventType = subscriberMethod.eventType;
//根據(jù)訂閱者和訂閱方法構(gòu)造一個(gè)訂閱事件
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//獲取當(dāng)前訂閱事件中Subscription的List集合
//通過響應(yīng)事件作為key,并取得這個(gè)事件類型將會(huì)響應(yīng)的全部訂閱者
//沒個(gè)訂閱者至少會(huì)訂閱一個(gè)事件,多個(gè)訂閱者可能訂閱同一個(gè)事件(多對多)
//key:訂閱的事件,value:訂閱這個(gè)事件的所有訂閱者集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//該事件對應(yīng)的Subscription的List集合不存在,則重新創(chuàng)建并保存在subscriptionsByEventType中
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//訂閱者已經(jīng)注冊則拋出EventBusException
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//遍歷訂閱事件确憨,找到比subscriptions中訂閱事件小的位置译荞,優(yōu)先級插入到訂閱者集合中
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);
break;
}
}
//通過訂閱者獲取該訂閱者所訂閱事件的集合(當(dāng)前訂閱者訂閱了哪些事件)
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//將當(dāng)前的訂閱事件添加到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();
//如果eventtype是candidateEventType同一個(gè)類或是其子類
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//調(diào)用checkPostStickyEventToSubscription()做一次安全判斷,就調(diào)用
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
checkPostStickyEventToSubscription()#postToSubscription(...)
private void postToSubscription(...) {
switch (threadMode) {
case PostThread:
//直接調(diào)用響應(yīng)方法
invokeSubscriber(subscription, event);
break;
case MainThread:
//如果是主線程則直接調(diào)用響應(yīng)事件,否則使用handle去在主線程響應(yīng)事件
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
//休弃。吞歼。。
}
}
訂閱的代碼主要就做了兩件事塔猾,第一件事是將我們的訂閱方法和訂閱者封裝到subscriptionsByEventType和typesBySubscriber中篙骡,subscriptionsByEventType是我們投遞訂閱事件的時(shí)候,就是根據(jù)我們的EventType找到我們的訂閱事件,從而去分發(fā)事件糯俗,處理事件的尿褪;typesBySubscriber在調(diào)用unregister(this)的時(shí)候,根據(jù)訂閱者找到EventType得湘,又根據(jù)EventType找到訂閱事件杖玲,從而對訂閱者進(jìn)行解綁。第二件事忽刽,如果是粘性事件的話天揖,就立馬投遞、執(zhí)行跪帝。
我們就知道了 EventBus 中那幾個(gè)map的全部含義。同時(shí)也回答了上面的第一個(gè)問題:為什么如果EventBus.defaultInstance不為null以后程序要拋出異常些阅,就是因?yàn)檫@幾個(gè)Map集合不同了。Map變了以后,訂閱的事件就全部變?yōu)榱硪粋€(gè)EventBus對象的了完慧,就沒辦法響應(yīng)之前那個(gè)EventBus對象的訂閱方法了互墓。
如果不是sticky粘性事件都直接不執(zhí)行了,還怎么響應(yīng)缤谎。如果是sticky粘性事件抒倚,最后是調(diào)用checkPostStickyEventToSubscription()做一次安全判斷,就調(diào)用postToSubscription()發(fā)送事件了坷澡。這里就關(guān)聯(lián)到了我們之前講的Poster類的作用了托呕。也回答了上面的第二個(gè)問題:三個(gè)Poster類的作用,Poster類就是只負(fù)責(zé)粘滯事件的代碼频敛。同時(shí)也回答了上面的第三個(gè)問題:invokeSubscriber(项郊。。斟赚。)什么時(shí)候觸發(fā)的着降。之前我們不知道subscriberMethod是什么,現(xiàn)在我們能看懂了拗军,就是通過反射調(diào)用訂閱者類subscriber的訂閱方法onEventXXX()任洞,并將event作為參數(shù)傳遞進(jìn)去。源碼如下:
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);
}
}
前面提到注解器為我們生成的索引MyEventBusIndex发侵。索引是在初始化EventBus時(shí)通過EventBusBuilder.addIndex(SubscriberInfoIndex index)方法傳進(jìn)來的交掏。下面就說說添加事件總線注釋預(yù)處理器生成的索引。
//源碼
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
可以看到器紧,傳進(jìn)來的索引信息會(huì)保存在subscriberInfoIndexes這個(gè)List中耀销,后續(xù)會(huì)通過EventBusBuilder傳到相應(yīng)EventBus的SubscriberMethodFinder實(shí)例中。我們先來分析SubscriberInfoIndex這個(gè)參數(shù):
public interface SubscriberInfoIndex {
SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);
}
可見索引只需要做一件事情——就是能拿到訂閱者的信息。而實(shí)現(xiàn)這個(gè)接口的類如果我們沒有編譯過熊尉,是找不到的罐柳。
EventBus3.X新特性Subscriber Index,在默認(rèn)屬性中有一個(gè)屬性為ignoreGeneratedIndex狰住,在后面的源碼
分析中會(huì)介紹到這個(gè)屬性為true時(shí)會(huì)使用反射方法獲取訂閱者的事件處理函數(shù)张吉,為false時(shí)會(huì)使用subscriber Index生成的SubscriberInfo來獲取訂閱者的事件處理函數(shù),具體內(nèi)容會(huì)在源碼分析中介紹
首先催植,Subscriber Index會(huì)在編譯期間生成SubscriberInfo肮蛹,然后在運(yùn)行時(shí)使用SubscriberInfo中保存的事件處理函數(shù)處理事件,減少了反射時(shí)需要是耗時(shí)创南,會(huì)有運(yùn)行速度上的提升伦忠,但是用起來會(huì)比較麻煩。
注意事項(xiàng):只有用注解@Subscriber描述的public方法才能被索引稿辙,并且由于Java的特性匿名內(nèi)部類就算用@Subscriber描述也不能被索引昆码,不過別擔(dān)心,當(dāng)EventBus不能使用索引時(shí)會(huì)自動(dòng)在運(yùn)行時(shí)使用反射方法獲取事件處理函數(shù)邻储。
要添加事件處理函數(shù)到索引需要借助EventBus的注解處理器赋咽,添加EventBus的注解處理器到Module的Gradle文件中:
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
eventBusIndex "包名.MyEventBusIndex"http://生成索引的名稱
}
}
在導(dǎo)入這些庫沒有問題后,Gradle之后build項(xiàng)目吨娜。這個(gè)時(shí)候就可以初始化EventBus脓匿,上面講過,自定義EventBus的兩種方式宦赠。
我們可以自定義設(shè)置自己的EventBus來為其添加MyEventBusIndex對象陪毡。代碼如下所示:
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
我們也能夠?qū)yEventBusIndex對象安裝在默認(rèn)的EventBus對象當(dāng)中。代碼如下所示:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();
如果有多個(gè)索引文件還可以使用下面的代碼:
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.addIndex(new MyEventBusLibIndex()).build();
順便說一句袱瓮,MyEventBusIndex的路徑為:項(xiàng)目根目錄\build\generated\source\apt\debug\包名\MyEventBusIndex.java
EventBus 事件的發(fā)送
一缤骨、post方式
在獲取到EventBus對象以后,可以通過post方法將給定事件發(fā)布到事件總線尺借。
public void post(Object event) {
//PostingThreadState保存著事件隊(duì)列和線程狀態(tài)信息
PostingThreadState postingState = currentPostingThreadState.get();
//獲取事件隊(duì)列绊起,并將當(dāng)前事插入到事件隊(duì)列中
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 {
//處理隊(duì)列中的所有事件
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
我們知道,對應(yīng)的值燎斩,是通過post去完成的虱歪。這里用ThreadLocal的線程包裝類,去完成對應(yīng)的線程記錄和操作
對應(yīng)的泛型PostingThreadState對象栅表,記錄對應(yīng)的信息笋鄙,將對應(yīng)的線程放入一個(gè)List中,通過對應(yīng)的狀態(tài)怪瓶,判斷Looper.getMainLooper() == Looper.myLooper() 是否是主線程等操作萧落, 將狀態(tài)存入PostingThreadState的屬性中,遍歷容器中的Event,最后通過存儲(chǔ)的Subscription中的subscriberMethod的method找岖,反射去invoke調(diào)用方法陨倡。
先從PostingThreadState對象中取出事件隊(duì)列,然后再將當(dāng)前的事件插入到事件隊(duì)列當(dāng)中许布。如果postingState.isPosting為true說明正在發(fā)布兴革,則不做處理。如果不是正在發(fā)布蜜唾,則最后將隊(duì)列中的事件依次交由postSingleEvent方法進(jìn)行處理杂曲,并移除該事件。這里也涉及到了Handler袁余、looper擎勘、ThreadLocal就不進(jìn)行解答了。
進(jìn)入postSingleEvent方法中了解其操作過程:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//eventInheritance表示是否向上查找事件的父類,默認(rèn)為true
if (eventInheritance) {
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);
}
//找不到該事件時(shí)的異常處理
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表示是否向上查找事件的父類,它的默認(rèn)值為true泌霍,可以通過在EventBusBuilder中來進(jìn)行配置货抄。當(dāng)eventInheritance為true時(shí),則通過lookupAllEventTypes找到所有的父類事件并存在List中朱转,然后通過postSingleEventForEventType方法對事件逐一處理。
進(jìn)入postSingleEventForEventType方法看看是怎么進(jìn)一步對事件處理:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
//取出該事件對應(yīng)的Subscription集合
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//將該事件的event和對應(yīng)的Subscription中的信息(包擴(kuò)訂閱者類和訂閱方法)傳遞給postingState
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//對事件進(jìn)行處理
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;
}
同步取出該事件對應(yīng)的Subscription集合并遍歷該集合將事件event和對應(yīng)Subscription傳遞給postingState并調(diào)用postToSubscription方法對事件進(jìn)行處理积暖。
進(jìn)一步進(jìn)入postToSubscription方法藤为,了解內(nèi)部是怎么處理的:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//直接調(diào)用響應(yīng)方式
invokeSubscriber(subscription, event);
break;
case MAIN:
//如果是主線程則直接調(diào)用響應(yīng)事件,否則使用handle去在主線程響應(yīng)事件
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,即訂閱方法運(yùn)行的線程模式夺刑,如果是POSTING缅疟,那么直接調(diào)用invokeSubscriber()方法即可,如果是MAIN遍愿,則要判斷當(dāng)前線程是否是MAIN線程存淫,如果是也是直接調(diào)用invokeSubscriber()方法,否則會(huì)交給mainThreadPoster來處理沼填,其他情況相類似桅咆。舉個(gè)例子,如果線程模式是MAIN坞笙,提交事件的線程是主線程的話則通過反射岩饼,直接運(yùn)行訂閱的方法,如果不是主線程薛夜,我們需要mainThreadPoster將我們的訂閱事件入隊(duì)列籍茧,mainThreadPoster是HandlerPoster類型的繼承自Handler,通過Handler將訂閱方法切換到主線程執(zhí)行梯澜。
二寞冯、postSticky方式
粘性事件發(fā)布
/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
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);
}
將給定事件發(fā)布到事件總線并保存到事件(因?yàn)樗钦承缘模W罱恼承允录愋偷氖录槐4嬖趦?nèi)存中以供訂閱者使用。用stickyEvents來存放粘性事件吮龄,并且從這個(gè)put我們可以知道stickyEvents的key是event的Class對象俭茧,value是event,然后進(jìn)行Post流程。我們可以去驗(yàn)證一下螟蝙,在一個(gè)對象如Activity中先調(diào)用EventBus.getDefault().postSticky(new MessageEvent(“Hello everyone!”));恢恼,再調(diào)用EventBus.register,結(jié)果如何呢胰默?從結(jié)果可以預(yù)想在register(更準(zhǔn)確的說應(yīng)該是subscribe)中肯定會(huì)有post的調(diào)用场斑。帶著這個(gè)設(shè)想,我們繼續(xù)看subcribe方法牵署,里面就有粘性事件的判斷漏隐。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
。奴迅。青责。。
//將當(dāng)前的訂閱事件添加到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);
}
}
}
在代碼端取具,接收事件時(shí)這樣脖隶。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onJump(MsgEvent msg){
}
//粘性事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void onJump(MsgEvent msg){
}
注解@Subscribe
ThreadMode 是enum(枚舉)類型暇检,threadMode默認(rèn)值是POSTING产阱。ThreadMode有四種模式:
POSTING :Subscriber會(huì)在post event的所在線程回調(diào),故它不需要切換線程來分發(fā)事件块仆,因此開銷最小构蹬。它要求task完成的要快,不能請求MainThread悔据,適用簡單的task庄敛。
MAIN :Subscriber會(huì)在主線程(有時(shí)候也被叫做UI線程)回調(diào),如果post event所在線程是MainThread,則可直接回調(diào)科汗。注意不能阻塞主線程藻烤。
BACKGROUND :Subscriber會(huì)在后臺線程中回調(diào)。如果post event所在線程不是MainThread肛捍,那么可直接回調(diào)隐绵;如果是MainThread,EventBus會(huì)用單例background thread來有序地分發(fā)事件。注意不能阻塞background thread拙毫。
ASYNC:當(dāng)處理事件的Method是耗時(shí)的依许,需要使用此模式。盡量避免同時(shí)觸發(fā)大量的耗時(shí)較長的異步操作缀蹄,EventBus使用線程池高效的復(fù)用已經(jīng)完成異步操作的線程峭跳。
再回到@Subscribe,還有兩個(gè)參數(shù)膘婶,sticky(粘性)默認(rèn)值是false,如果是true,那么可以通過EventBus的postSticky方法分發(fā)最近的粘性事件給該訂閱者(前提是該事件可獲得)蛀醉。
EventBus 取消注冊
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
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--;
}
}
}
}
unsubscribeByEventType方法中獲取相應(yīng)事件中的相應(yīng)列表刪除掉該訂閱者悬襟。typesBySubscriber我們在訂閱者注冊的過程中講到過這個(gè)屬性,他根據(jù)訂閱者找到EventType拯刁,然后根據(jù)EventType和訂閱者來得到訂閱事件來對訂閱者進(jìn)行解綁脊岳。
對EventBus小結(jié)
弄清楚上面的代碼之后,我們一起來梳理一下設(shè)計(jì)者是如何完成觀察者模式中的訂閱和發(fā)送事件以及取消訂閱垛玻。
我們先看subscriber割捅。看到注冊方法register(Object subscriber)帚桩,傳入的就是一個(gè)Objec對象亿驾。一個(gè)subscriber可以訂閱多個(gè)類型的Event,而且對于同一種類型的Event可以有多個(gè)method進(jìn)行處理(盡管我們一般不會(huì)這樣做)账嚎。
在subscribe方法中莫瞬,引入Subscription對subscriber、event進(jìn)行了封裝郭蕉。經(jīng)過判斷之后疼邀,把“合格”的subscription加入subscriptionsByEventType中。
當(dāng)我們分發(fā)事件時(shí)召锈,也就是post(Object event)時(shí)檩小,他會(huì)間接調(diào)用postSingleEventForEventType這個(gè)方法,通過傳入的參數(shù)event烟勋,在subscriptionsByEventType找到event對應(yīng)的value,再繼續(xù)相應(yīng)的操作筐付。
我們?nèi)∠粋€(gè)subscriber的訂閱是卵惦,也就是unregister(Object subscriber).我們會(huì)在typesBySubscriber中找到該subsriber對應(yīng)的evnt。然后再由此event去subscriptionsByEventType找到一系列的subscription瓦戚,并把他們r(jià)emove沮尿。
EventBus訂閱了,post發(fā)布了事件较解,就會(huì)觸發(fā)invokeSubscriber()方法畜疾,訂閱者類subscriber的訂閱方法onEventXXX(),并將event作為參數(shù)傳遞進(jìn)去印衔。即調(diào)用subscription.subscriberMethod.method.invoke(subscription.subscriber, event)方法去響應(yīng)啡捶。那么訂閱者類就會(huì)收到信息