EventBus 3.0 原理

EventBus 簡介

eventbus 是一個Android publish/subscrible 框架眉菱,通過解耦發(fā)布者和訂閱者,簡化Android事件傳遞塘装,從而使代碼整潔赴涵。
eventbus 3.0和2.0有區(qū)別辞色,本文基于3.0描述相關問題

源碼分析

  • 創(chuàng)建eventbus

EventBus.getDefault().register 或者是 post 等

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

使用了單例模式雙重同步鎖保證線程安全,效率高

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        //key:訂閱的事件,value:訂閱這個事件的所有訂閱者集合
        //private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
        subscriptionsByEventType = new HashMap<>();
        //key:訂閱者對象,value:這個訂閱者訂閱的事件集合
        //private final Map<Object, List<Class<?>>> typesBySubscriber;
        typesBySubscriber = new HashMap<>();
        //粘性事件 key:粘性事件的class對象, value:事件對象
        //private final Map<Class<?>, Object> stickyEvents;
        stickyEvents = new ConcurrentHashMap<>();
        //事件主線程處理
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        //事件 Background 處理
        backgroundPoster = new BackgroundPoster(this);
        //事件異步線程處理
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        //訂閱者響應函數(shù)信息存儲和查找類
        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的一些配置,當我們在寫一個需要自定義配置的框架的時候,這種實現(xiàn)方法非常普遍,將配置解耦出去,使我們的代碼結構更清晰 這種寫法很值得學習

  • register 流程
Paste_Image.png
  • post 流程

PostThread:默認的 ThreadMode米罚,表示在執(zhí)行 Post 操作的線程直接調(diào)用訂閱者的事件響應方法钧汹,不論該線程是否為主線程(UI 線程)。當該線程為主線程時录择,響應方法中不能有耗時操作拔莱,否則有卡主線程的風險。適用場景:對于是否在主線程執(zhí)行無要求隘竭,但若 Post 線程為主線程塘秦,不能耗時的操作;
MainThread:在主線程中執(zhí)行響應方法动看。如果發(fā)布線程就是主線程尊剔,則直接調(diào)用訂閱者的事件響應方法,否則通過主線程的 Handler 發(fā)送消息在主線程中處理——調(diào)用訂閱者的事件響應函數(shù)菱皆。顯然须误,MainThread類的方法也不能有耗時操作,以避免卡主線程搔预。適用場景:必須在主線程執(zhí)行的操作霹期;
BackgroundThread:在后臺線程中執(zhí)行響應方法叶组。如果發(fā)布線程不是主線程拯田,則直接調(diào)用訂閱者的事件響應函數(shù),否則啟動唯一的后臺線程去處理甩十。由于后臺線程是唯一的船庇,當事件超過一個的時候,它們會被放在隊列中依次執(zhí)行侣监,因此該類響應方法雖然沒有PostThread類和MainThread類方法對性能敏感鸭轮,但最好不要有重度耗時的操作或太頻繁的輕度耗時操作,以造成其他操作等待橄霉。適用場景:操作輕微耗時且不會過于頻繁窃爷,即一般的耗時操作都可以放在這里;
Async:不論發(fā)布線程是否為主線程姓蜂,都使用一個空閑線程來處理按厘。和BackgroundThread不同的是,Async類的所有線程是相互獨立的钱慢,因此不會出現(xiàn)卡線程的問題逮京。適用場景:長耗時操作,例如網(wǎng)絡訪問束莫。

Paste_Image.png
  • unRegister
public synchronized void unregister(Object subscriber) {
        //通過typesBySubscriber來取出這個subscriber訂閱者訂閱的事件類型,
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            //分別解除每個訂閱了的事件類型
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            //從typesBySubscriber移除subscriber
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        //subscriptionsByEventType里拿出這個事件類型的訂閱者列表.
        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--;
                }
            }
        }
    }

優(yōu)點:EventBus并不是標準的觀察者模式的實現(xiàn),但是它的整體就是一個發(fā)布/訂閱框架,也擁有觀察者模式的優(yōu)點,比如:發(fā)布者和訂閱者的解耦

缺點:EventBus是一個輕量級的東西懒棉,這個東西甚至我們自己實現(xiàn)都可以草描,而廣播雖然原理一樣,但是實際是個很復雜的東西策严。兩者還有個共同的地方穗慕,那就是需要注冊與注銷,為什么需要注銷妻导,因為其一如果你不注銷揍诽,那么在你不想接收消息的時候,例如你的頁面已經(jīng)關閉了栗竖,實際消息還是能接收到暑脆,這時候在一個已經(jīng)關閉了的頁面中執(zhí)行代碼是很不安全的;其二在注冊時EventBus保存了你訂閱者狐肢,例如Activity的引用添吗,而你不去注銷,那么EventBus就會一直保存著你的引用份名,這樣就引起了內(nèi)存泄漏碟联。EventBus和Broadcast還有個不同點,那就是Broadcast傳遞的消息只能是Intent僵腺,而Intent中除了序列化的對象外只能傳遞值鲤孵,不能傳遞引用,但是EventBus可以傳遞任何你想要的東西辰如。我們來想一想EventBus的這種特性的利弊普监。利,咱們這篇文檔一直再說琉兜,粗暴點說就是用的爽凯正,不同頁面,甚至不同模塊之間通訊是如此的省事豌蟋。而弊廊散,同樣是因為太省事了,因為我們忽略了系統(tǒng)設計的初衷梧疲≡识茫可以想想,為什么Activity之間啟動用Intent幌氮,之間想要通訊也是用Broadcast傳遞Intent缭受,而Intent又不能傳遞復雜的東西,只能是基本類型浩销,即使是序列化的贯涎,也是對象的拷貝,原因就在于慢洋,系統(tǒng)設計為了使頁面與頁面之間的耦合降到最低塘雳,使他們之間的接觸變的最少陆盘,自己的邏輯在自己模塊中寫,而不是通過對象來回傳遞败明。所以說從這一點出發(fā)隘马,EventBus又打破了這種設計的合理性,一旦使用不合理妻顶,會使代碼非常的糟糕酸员。例如頁面邏輯和業(yè)務邏輯可以隨便放置,因為只需要通過發(fā)送對象就可以讓另一個頁面另一個模塊幫你做處理讳嘱,如果工程中大量的如此使用幔嗦,維護者可能就要罵街了。所以使用要合理沥潭,呵呵邀泉。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钝鸽,隨后出現(xiàn)的幾起案子汇恤,更是在濱河造成了極大的恐慌,老刑警劉巖拔恰,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因谎,死亡現(xiàn)場離奇詭異,居然都是意外死亡颜懊,警方通過查閱死者的電腦和手機财岔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饭冬,“玉大人使鹅,你說我怎么就攤上這事〔伲” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵鲁僚,是天一觀的道長炊苫。 經(jīng)常有香客問我,道長冰沙,這世上最難降的妖魔是什么侨艾? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮拓挥,結果婚禮上唠梨,老公的妹妹穿的比我還像新娘。我一直安慰自己侥啤,他們只是感情好当叭,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布茬故。 她就那樣靜靜地躺著,像睡著了一般蚁鳖。 火紅的嫁衣襯著肌膚如雪磺芭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天醉箕,我揣著相機與錄音钾腺,去河邊找鬼。 笑死讥裤,一個胖子當著我的面吹牛放棒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播己英,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼哨查,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了剧辐?” 一聲冷哼從身側響起寒亥,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荧关,沒想到半個月后溉奕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡忍啤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年加勤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片同波。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡鳄梅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出未檩,到底是詐尸還是另有隱情戴尸,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布冤狡,位于F島的核電站孙蒙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏悲雳。R本人自食惡果不足惜挎峦,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望合瓢。 院中可真熱鬧坦胶,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至岖圈,卻和暖如春讹语,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜂科。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工顽决, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人导匣。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓才菠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贡定。 傳聞我的和親對象是個殘疾皇子赋访,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,328評論 25 707
  • 我每周會寫一篇源代碼分析的文章,以后也可能會有其他主題.如果你喜歡我寫的文章的話,歡迎關注我的新浪微博@達達達達s...
    SkyKai閱讀 24,949評論 23 184
  • 對于Android開發(fā)老司機來說肯定不會陌生,它是一個基于觀察者模式的事件發(fā)布/訂閱框架缓待,開發(fā)者可以通過極少的代碼...
    飛揚小米閱讀 1,480評論 0 50
  • 后羿射日是我國古代傳統(tǒng)神話傳說蚓耽。古時候天上有十個太陽,平時每天一個太陽值班旋炒,直到有一天步悠,十個太陽貪玩也好、好奇也罷...
    半夜樓主閱讀 1,238評論 5 2
  • 靈川縣海洋鄉(xiāng)離銀杏俗稱白果樹,有"世界第一活化石"的美譽铣除,海洋鄉(xiāng)共有銀杏近百萬株谚咬,其中百年以上的銀杏就有1.7萬株...
    珊珊三丫頭閱讀 549評論 0 0