組件化通信

LocalBroadcastManager

  • 單例獲取LocalBroadcastManager實(shí)體邮弹,dooublecheck

    LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this)
    
  • 注冊(cè)廣播接收者腌乡,

    lbm.registerReceiver(mrevicer, new    IntentFilter("LOCAL_ACTION"));
    

    構(gòu)建一個(gè)廣播信息實(shí)體ReceiverRecord entry = new ReceiverRecord(filter, receiver)到ArrayList中
    最后action的string做key,包含ReceiverRecord 的ArrayList做value放入HashMap mActions 中

    mActions.put(action, entries)
    
  • 發(fā)生廣播

    lbm.sendBroadcast(new Intent().setAction("LOCAL_ACTION").putExtra("text","ni shi local"));
    

    從mActions中獲取廣播匹配"LOCAL_ACTION"的實(shí)體ReceiverRecord的集合
    循環(huán)集合得到ReceiverRecord對(duì)每個(gè)進(jìn)行math与纽,把match的結(jié)果放入集合mPendingBroadcasts,然后發(fā)生message
    內(nèi)部類Handler處理消息調(diào)用executePendingBroadcasts()
    從集合mPendingBroadcasts中拿到每個(gè)receiver調(diào)用receiver.onReceive(mAppContext, br.intent)

    filter.match(action, type, scheme, data,
            categories, "LocalBroadcastManager")
    
  • 取消廣播
    從mActions中取出對(duì)應(yīng)action的集合
    然后有當(dāng)前對(duì)象影所,就從集合中移除

    lbm.unregisterReceiver(mrevicer);
    

優(yōu)點(diǎn)比全局廣播效率高猴娩,
缺點(diǎn)勺阐,使用Android原生API渊抽,不適合javalib懒闷,LocalBroadcastManager的傳遞不方便需要context不是所有module都有context對(duì)象毛雇,組件化能用但是不適合

EventBus

EventBus可以代替Android傳統(tǒng)的Intent,Handler,Broadcast或接口函數(shù),在Fragment,Activity,Service線程之間傳遞數(shù)據(jù)灵疮,只是傳遞震捣。Handler是很多時(shí)候逃不掉的坑蒿赢。

register

通過(guò)getDefault獲取EventBus實(shí)例羡棵,這是一條默認(rèn)的事件總線,通過(guò)單例模式實(shí)現(xiàn),其構(gòu)造函數(shù)是Public的皂冰,也可以通過(guò)EventBus.builder().build()來(lái)創(chuàng)建多條事件總線,一般在oncreate中創(chuàng)建养篓。

EventBus.getDefault().register(this);
//這是一個(gè)構(gòu)建者模式+鏈?zhǔn)秸{(diào)用秃流,build之前可以鏈?zhǔn)秸{(diào)用添加參數(shù)
EventBus.builder().build()
unregister

如果是默認(rèn)直接通過(guò)下面的方法取消柳弄,自己新建的就要拿到之前保留的對(duì)象舶胀,然后調(diào)用unregister方法

EventBus.getDefault().unregister(this);
post

發(fā)送消息,可以是任意對(duì)象嚣伐。

EventBus.getDefault().post(daLaBa);
Subscribe
  1. threadMode
    • POSTING
      默認(rèn)的模式,開銷最小的模式,因?yàn)槁暶鳛镻OSTING的訂閱者會(huì)在發(fā)布的同一個(gè)線程調(diào)用糖赔,發(fā)布者在主線程那么訂閱者也就在主線程,反之亦,避免了線程切換,如果不確定是否有耗時(shí)操作轩端,謹(jǐn)慎使用挂捻,因?yàn)榭赡苁窃谥骶€程發(fā)布

    • MAIN
      主線程調(diào)用,視發(fā)布線程不同處理不同,如果發(fā)布者在主線程那么直接調(diào)用(非阻塞式),如果發(fā)布者不在主線程那么阻塞式調(diào)用

    • MAIN_ORDERED
      和MAIN差不多,主線程調(diào)用刻撒,和MAIN不同的是他保證了post是非阻塞式的(默認(rèn)走M(jìn)AIN的非主線程的邏輯,所以可以做到非阻塞)

    • BACKGROUND
      在子線程調(diào)用,如果發(fā)布在子線程那么直接在發(fā)布線程調(diào)用,如果發(fā)布在主線程那么將開啟一個(gè)子線程來(lái)調(diào)用,這個(gè)子線程是阻塞式的,按順序交付所有事件,所以也不適合做耗時(shí)任務(wù),因?yàn)槎鄠€(gè)事件共用這一個(gè)后臺(tái)線程

    • ASYNC
      在子線程調(diào)用,總是開啟一個(gè)新的線程來(lái)調(diào)用,適用于做耗時(shí)任務(wù),比如數(shù)據(jù)庫(kù)操作,網(wǎng)絡(luò)請(qǐng)求等,不適合做計(jì)算任務(wù),會(huì)導(dǎo)致開啟大量線程

流程
register
  1. 獲得傳入對(duì)象的類對(duì)象沥曹。

  2. 獲得這個(gè)類中定義的訂閱函數(shù)。
    subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    //subscriberClass傳入對(duì)象的class

    • 從緩存中取出METHOD_CACHE訂閱方法集合茬高。如果有直接返回丽猬。

      List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
      
    • 如果緩存中沒(méi)有,選擇合適的方法尋找訂閱方法熏瞄。
      subscriberMethods = findUsingReflection(subscriberClass);直接通過(guò)反射查找
      subscriberMethods = findUsingInfo(subscriberClass);默認(rèn)查找
      兩者區(qū)別第二個(gè)多出從subscriberMethodFinder.subscriberInfoIndexes中獲取SubscriberInfoIndex來(lái)獲取SubscriberInfo

      1. 從狀態(tài)池獲得一個(gè)狀態(tài)對(duì)象脚祟。
        FindState findState = prepareFindState

      2. 將訂閱者類傳入,設(shè)置一些狀態(tài)對(duì)象的參數(shù)强饮。
        findState.initForSubscriber(subscriberClass);

      3. 通過(guò)狀態(tài)對(duì)象尋找訂閱者信息由桌,如果找到就獲取到了訂閱方法數(shù)組,并循環(huán)檢測(cè)合法性邮丰,通過(guò)findState.subscriberMethods.add(subscriberMethod)放入其集合中行您。

        // 尋找方法findState.subscriberInfo = getSubscriberInfo(findState);
        // 這一步就是多出來(lái)的通過(guò)subscriberMethodFinder.subscriberInfoIndexes的索引緩存查找
        
      4. 如果沒(méi)有沒(méi)有找到訂閱者信息,則調(diào)用方法柠座,用反射去尋找訂閱方法邑雅。
        findUsingReflectionInSingleClass(findState);

        • 獲取訂閱類中定義的方法。(方法類型妈经、方法參數(shù)長(zhǎng)度之類)
        • 遍歷方法集合淮野,檢測(cè)完合法性捧书,將合法的訂閱方法封裝,存入狀態(tài)對(duì)象
        Class<?> eventType = parameterTypes[0]
        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                              subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
        
      5. 狀態(tài)對(duì)象指向父類骤星,再次循環(huán)经瓷,找父類中的訂閱方法。
        findState.moveToSuperclass();

      6. 返回subscriberMethods洞难,并且回收f(shuō)indState

        return getMethodsAndRelease(findState)
        
    • 如果沒(méi)有找到訂閱方法舆吮,拋出異常程序崩潰。
      如果找到了訂閱方法队贱,將它和訂閱者類對(duì)象組成鍵值對(duì)存儲(chǔ)到緩存METHOD_CACHE色冀。

      METHOD_CACHE.put(subscriberClass, subscriberMethods);
      
  3. 對(duì)每個(gè)訂閱函數(shù)進(jìn)行訂閱操作。

    • 根據(jù)訂閱方法獲取事件類型柱嫌。

      Class<?> eventType = subscriberMethod.eventType;
      
    • 將訂閱者和訂閱方法組合成一個(gè)訂閱對(duì)象锋恬。

      Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
      
    • 獲取訂閱對(duì)象集合,如果為空直接新建集合后放入编丘,
      將訂閱對(duì)象集合中對(duì)象按優(yōu)先級(jí)排序后

      if (subscriptions == null) {
          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) {
              subscriptions.add(i, newSubscription);
              break;
          }
      }
      
    • 根據(jù)注冊(cè)方法傳入的對(duì)象獲取到它可以相應(yīng)的事件類型集合与学,
      如果沒(méi)有新建一個(gè)集合,將對(duì)象做key嘉抓,集合做value存Map typesBySubscriber當(dāng)中索守。
      最后都會(huì)將事件類型存入集合

       //根據(jù)訂閱者獲取事件類型集合
      List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
          //沒(méi)有存儲(chǔ)過(guò)事件類型,新建一個(gè)集合
          if (subscribedEvents == null) {
              subscribedEvents = new ArrayList<>();
              typesBySubscriber.put(subscriber, subscribedEvents);
        }
      //put當(dāng)前事件類型進(jìn)去
      subscribedEvents.add(eventType);
      
    • 處理粘性事件抑片。如果之前有粘性事件卵佛,則取出并發(fā)送給訂閱者

post

使用ThreadLocal來(lái)存儲(chǔ)事件,他可以隔離多個(gè)線程對(duì)數(shù)據(jù)的訪問(wèn)沖突

  1. 獲得post線程狀態(tài)對(duì)象。

    PostingThreadState postingState = currentPostingThreadState.get();
    
  2. 從這個(gè)線程狀態(tài)對(duì)象中取得事件隊(duì)列蓝丙。加入事件對(duì)象级遭。

    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    
  3. 如果事件沒(méi)有在分發(fā)中,

    • 設(shè)置分發(fā)狀態(tài)和是否主線程渺尘。

    • 循環(huán)取出事件隊(duì)列中的事件進(jìn)行分發(fā)過(guò)程。

      while (!eventQueue.isEmpty()) {
          postSingleEvent(eventQueue.remove(0), postingState);
      }
      

      找到我們自定義事件類型的類對(duì)象说敏。
      --->eventInheritance若為true鸥跟,會(huì)找到這個(gè)事件類型的自身和父類,逐一進(jìn)行事件分發(fā)盔沫。
      --->eventInheritance為true医咨,只對(duì)自身事件類型進(jìn)行分發(fā)。
      --->最終都會(huì)通過(guò)下面的postSingleEventForEventType分發(fā)

      subscriptionFound = postSingleEventForEventType(event, postingState, eventClass)
      
      1. 根據(jù)事件類型獲得訂閱對(duì)象集合架诞。

        1. subscriptions = subscriptionsByEventType.get(eventClass);
        
      2. 如果集合不為空拟淮,循環(huán)subscriptions集合

        將event和subscription給賦值postingState的屬性

        調(diào)用postToSubscription(subscription, event, postingState.isMainThread);

        賦值aborted = postingState.canceled;

        然后重置postingState

        postingState.event = null;
        postingState.subscription = null;
        postingState.canceled = false;
        

        如果aborted = ture跳槽循環(huán)

        • 根據(jù)threadMode找到合適的線程來(lái)調(diào)用訂閱函數(shù)而已

          private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
                //根據(jù)訂閱方法注解中標(biāo)明的執(zhí)行線程執(zhí)行
                  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);
                  }
             }
          

          **主線程mainThread: **

          Poster:通過(guò)繼承Handler重寫handleMessage增加新的enqueue方法

          enqueue方法:加入自己的隊(duì)列,發(fā)送空消息

          handleMessage:invokeSubscriber(subscription, event)省略了很多邏輯

          **backgroundPoster: **

          enqueue方法:加入自己的隊(duì)列谴忧,如果第一次啟動(dòng)線程就傳入自己(實(shí)現(xiàn)了Runnable)然后啟動(dòng)線程

          run方法:無(wú)限循環(huán)很泊,從隊(duì)列取pendingPost角虫,然后調(diào)用eventBus.invokeSubscriber(pendingPost)

          **asyncPoster: **

          enqueue方法:加入自己的隊(duì)列,啟動(dòng)線程傳入自己(實(shí)現(xiàn)了Runnable)

          run方法:從隊(duì)列取pendingPost委造,然后調(diào)用eventBus.invokeSubscriber(pendingPost)

          eventBus.invokeSubscriber(pendingPost):

          method.invoke(subscription.subscriber, event):

      3. 返回flase

      4. 通過(guò)postToSubscription向訂閱者進(jìn)行事件分發(fā)戳鹅。
        接下來(lái)調(diào)用postToSubscription繼續(xù)實(shí)際分發(fā)。

      如果都沒(méi)有就發(fā)送沒(méi)有訂閱者的事件post(new NoSubscriberEvent(this, event));

  • 重置postingState

    postingState.isPosting = false;
    postingState.isMainThread = false; 
    
unregister

根據(jù)傳入對(duì)象找到訂閱的事件類型集合昏兆,
for循環(huán)事件集合
從subscriptionsByEventType獲取subscriptions集合
如何subscription.subscriber == subscriber(傳入的對(duì)象)就從subscriptions集合移出

Rxbus

是基于Rxjava的一種封裝要比EventBus好枫虏,因?yàn)樾矢撸€程調(diào)度和鏈?zhǔn)絻?yōu)于Eventbus

ModuleBus

跟Eventbus類似爬虱,能傳遞基礎(chǔ)數(shù)據(jù)類型隶债。自定義的事件信息模型還是要添加到BaseModule中才能讓其他模塊引用

ModularizationArchitecture

是一個(gè)路由帶事件功能,已經(jīng)停工了跑筝,需要context燃异,使用場(chǎng)景有限,不多說(shuō)继蜡。

LocalRouter.getInstance()

組件化中使用總線怎么辦

可以抽離成一個(gè)單獨(dú)的模塊回俐,讓BaseModule依賴,把事件抽離到XXXBusModule稀并,來(lái)解耦仅颇,減少對(duì)BaseModule的影響。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碘举,一起剝皮案震驚了整個(gè)濱河市忘瓦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌引颈,老刑警劉巖耕皮,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蝙场,居然都是意外死亡凌停,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門售滤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罚拟,“玉大人,你說(shuō)我怎么就攤上這事完箩〈退祝” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵弊知,是天一觀的道長(zhǎng)阻逮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)秩彤,這世上最難降的妖魔是什么叔扼? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任事哭,我火速辦了婚禮,結(jié)果婚禮上币励,老公的妹妹穿的比我還像新娘慷蠕。我一直安慰自己,他們只是感情好食呻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布流炕。 她就那樣靜靜地躺著,像睡著了一般仅胞。 火紅的嫁衣襯著肌膚如雪每辟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天干旧,我揣著相機(jī)與錄音渠欺,去河邊找鬼。 笑死椎眯,一個(gè)胖子當(dāng)著我的面吹牛挠将,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播编整,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼舔稀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了掌测?” 一聲冷哼從身側(cè)響起内贮,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汞斧,沒(méi)想到半個(gè)月后夜郁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粘勒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年竞端,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仲义。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡婶熬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出埃撵,到底是詐尸還是另有隱情,我是刑警寧澤虽另,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布暂刘,位于F島的核電站,受9級(jí)特大地震影響捂刺,放射性物質(zhì)發(fā)生泄漏谣拣。R本人自食惡果不足惜募寨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望森缠。 院中可真熱鬧拔鹰,春花似錦、人聲如沸贵涵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宾茂。三九已至瓷马,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跨晴,已是汗流浹背欧聘。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留端盆,地道東北人怀骤。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像焕妙,于是被迫代替她去往敵國(guó)和親蒋伦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

  • 項(xiàng)目到了一定階段會(huì)出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動(dòng)性越來(lái)越大访敌,代碼維護(hù)與測(cè)試回歸流程越來(lái)越繁瑣凉敲。這個(gè)...
    fdacc6a1e764閱讀 3,179評(píng)論 0 6
  • EventBus基本使用 EventBus基于觀察者模式的Android事件分發(fā)總線。 從這個(gè)圖可以看出寺旺,Even...
    顧氏名清明閱讀 623評(píng)論 0 1
  • EventBus源碼分析 Android開發(fā)中我們最常用到的可以說(shuō)就是EventBus了爷抓,今天我們來(lái)深入研究一下E...
    BlackFlag閱讀 508評(píng)論 3 4
  • EventBus用法及解析 EventBus介紹: EventBus主要是用來(lái)組件之間進(jìn)行信息傳遞的,相對(duì)于接口回...
    111_222閱讀 547評(píng)論 0 1
  • 先吐槽一下博客園的MarkDown編輯器阻塑,推出的時(shí)候還很高興博客園支持MarkDown了蓝撇,試用了下發(fā)現(xiàn)支持不完善就...
    Ten_Minutes閱讀 562評(píng)論 0 2