自己眼中的EventBus

項(xiàng)目上線有一段時(shí)間了,自己總結(jié)了下項(xiàng)目牡彻。項(xiàng)目中的EventBus真的是到處都有它的影子,說實(shí)話缎除,用上EventBus后总寻,個(gè)人覺得項(xiàng)目中的業(yè)務(wù)變得很輕松渐行,不用考慮哪個(gè)類發(fā)的,哪個(gè)類去回調(diào)肴沫。別人都說EventBus是一個(gè)"事務(wù)總線",就像一個(gè)公司的項(xiàng)目小組一樣颤芬。項(xiàng)目經(jīng)理給各個(gè)模塊的分配好任務(wù)了,然后各人模塊的人員處理自己的事情夺艰,最后完成整個(gè)事務(wù)。

Bus類:

public class Bus {   
     static volatile Bus sInstance;   
     Finder mFinder;   
     //CopyOnWriteArrayList一般用在讀和寫同時(shí)存在的情況下使用    
     Map<Class<?>, CopyOnWriteArrayList<Subscriber>> mSubscriberMap;    
     PostHandler mPostHandler; 
     private Bus() {      
         mFinder = new NameBasedFinder();  
         mSubscriberMap = new HashMap<>();    
         mPostHandler = new PostHandler(Looper.getMainLooper(), this);  
     }   
     /**   
      * 得到一個(gè)單例的上下文對(duì)象 
      * @return  
     */    
     public static Bus getDefault() {    
        if (sInstance == null) {            
             synchronized (Bus.class) {          
                  if (sInstance == null) {            
                      sInstance = new Bus();       
                  }           
             }    
         }      
         return sInstance;    
      }  

      public void register(Object subscriber) {    
          /**       
           * 找到該類下面的所有以onEvent開頭的方法   
           */       
          List<Method> methods = mFinder.findSubscriber(subscriber.getClass());     
          if (methods == null || methods.size() < 1) {     
             return;   
          }     
          CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.get(subscriber.getClass());     
          if (subscribers == null) {       
             subscribers = new CopyOnWriteArrayList<>();    
             /**          
              * 該map用來存儲(chǔ)onevent開頭的方法的第一個(gè)參數(shù)為key和          
              * subscribers的集合          
              */        
             mSubscriberMap.put(methods.get(0).getParameterTypes()[0], subscribers);   
          }     
          for (Method method : methods) {      
            /**            
             * 封裝了該類和該類帶有onevent的方法  
             */          
             Subscriber newSubscriber = new Subscriber(subscriber, method);       
             subscribers.add(newSubscriber);   
          }   
      }    

      public void unregister(Object subscriber) {       
         CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.remove(subscriber.getClass());  
         if (subscribers != null) {        
            for (Subscriber s : subscribers) {       
               s.mMethod = null;             
               s.mSubscriber = null;       
            }    
         }   
      } 

      public void post(Object event) {       
        //TODO post with handler      
        mPostHandler.enqueue(event);  
      }
}

Bus類主要是消息的注冊(cè)存谎、發(fā)送既荚、注銷幾個(gè)動(dòng)作。
在注冊(cè)中句各,首先找到要注冊(cè)類中onEvent方法晴叨,這里是怎么做到的呢。大家找找成員變量mFinder是怎么生成的。其實(shí)它是一個(gè)接口:

public interface Finder {   
    List<Method> findSubscriber(Class<?> subscriber);
}

再來看看它的實(shí)現(xiàn)類吧:


實(shí)現(xiàn)類.png
public class NameBasedFinder implements Finder {    
   /**     
    * public Method[] getMethods()返回某個(gè)類的所有公用(public)方法包括其繼承類的公用方法胚宦,當(dāng)然也包括它所實(shí)現(xiàn)接口的方法浓体。   
      public Method[] getDeclaredMethods()返回自身類的所有公用(public)方法包括私有(private)方法,但包括其繼承類的方法油吭,當(dāng)然也包括它所實(shí)現(xiàn)接口的方法纽绍。   
    * 找到某個(gè)類的所有的    
    * @param subscriber     
    * @return  
    */   
    @Override 
    public List<Method> findSubscriber(Class<?> subscriber) {       
       List<Method> methods = new ArrayList<>(); 
       for (Method method : subscriber.getDeclaredMethods()) {   
           if (method.getName().startsWith("onEvent") && 
              method.getParameterTypes().length == 1) {                
              methods.add(method);                
           }   
       }     
       return methods; 
    }
}

看到了沒拌夏,該實(shí)現(xiàn)類只是簡(jiǎn)單地將onEvent開頭的方法給封裝到一個(gè)集合里面去了障簿。
接著看注冊(cè)里面栅迄,獲取到以onEvent開頭的方法后毅舆,將該method集合添加到一個(gè)mSubscriberMap中,該map中的key就是onEvent開頭的方法的第一個(gè)參數(shù)岂津,value就是Subscriber的集合吮成。
那就再來看看Subscriber類吧:

public class Subscriber {    
   Object mSubscriber;    
   Method mMethod;   
   Class<?> mEventType; 
   public Subscriber(Object subscriber, Method method) {        
      mSubscriber = subscriber;    
      mMethod = method;   
     mEventType = method.getParameterTypes()[0];  
   }
}

該類有三個(gè)成員變量粱甫,可能現(xiàn)在有人不知道為啥要這么定義作瞄,到了后面用到的時(shí)候就知道為啥這么定義了宗挥。
好了,整個(gè)注冊(cè)的過程就這樣安拟,最終的目的就是得到一個(gè)map對(duì)象糠赦。
下面來看看發(fā)送的過程吧:
mPostHandler.enqueue(event);
就這一行代碼拙泽,下面就看看mPostHandler類是怎么得來的吧顾瞻。
mPostHandler = new PostHandler(Looper.getMainLooper(), this);
下面來看看PostHandler類吧:

public class PostHandler extends Handler {  
  final Bus mBus;   
  public PostHandler(Looper looper, Bus bus) {        
    super(looper);    
    mBus = bus; 
  }  
  @Override  
  public void handleMessage(Message msg) {        
    CopyOnWriteArrayList<Subscriber> subscribers = mBus.mSubscriberMap.get(msg.obj.getClass()); 
       for (Subscriber subscriber : subscribers) {            
          subscriber.mMethod.setAccessible(true);      
          try {           
              /**           
               * 第二個(gè)參數(shù)是方法的參數(shù)荷荤,第-個(gè)參數(shù)是該類的對(duì)象                 
               */                
              subscriber.mMethod.invoke(subscriber.mSubscriber, msg.obj);     
          } catch (Exception e) {    
             e.printStackTrace();      
          }      
       }  
  }  

  void enqueue(Object event) {   
     Message message = obtainMessage();    
     message.obj = event;    
     sendMessage(message);   
  } 

}

看到該類了沒蕴纳,還是逃不過handler通過handleMessage方法來接收處理消息了,這個(gè)地方就是核心的地方了翻翩。這里實(shí)際上bus類的post方法的參數(shù)就是這里的bus類中mSubscriberMap的key了嫂冻,通過該key可以獲取到mSubscriberMap中的value值了塞椎,也就是Subscriber集合了,現(xiàn)在知道Subscriber類的定義了吧蹬敲,mMethod是方法名,mSubscriber是該類對(duì)象急波,實(shí)際上它的第三個(gè)成員變量沒用到澄暮。最后通過反射來調(diào)用onEvent開頭的方法,整個(gè)過程就這么簡(jiǎn)單伸辟,是不是現(xiàn)在一目了然了呢信夫。
最后就是注銷了:

public void unregister(Object subscriber) {       
   CopyOnWriteArrayList<Subscriber> subscribers = mSubscriberMap.remove(subscriber.getClass());  
   if (subscribers != null) {        
        for (Subscriber s : subscribers) {       
            s.mMethod = null;             
            s.mSubscriber = null;       
        }    
    }   
} 

這里沒什么好說的吧警没,就是將map中的對(duì)象給至空了杀迹。
好了树酪,整個(gè)過程就這樣了嗅回,是不是幾個(gè)過程都清楚了绵载。

下面用一個(gè)demo來測(cè)試該事例吧:

demo.gif

整個(gè)的內(nèi)容就這么多了娃豹,完全是靠自己的理解懂版。如果有什么說得不清楚的地方躯畴,還需大家指出來蓬抄。

借鑒的項(xiàng)目:https://github.com/avenwu/support

關(guān)于我:

email:a1002326270@163.com
github:enter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市耍贾,隨后出現(xiàn)的幾起案子荐开,更是在濱河造成了極大的恐慌晃听,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仍翰,死亡現(xiàn)場(chǎng)離奇詭異予借,居然都是意外死亡频蛔,警方通過查閱死者的電腦和手機(jī)灵迫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晦溪,“玉大人瀑粥,你說我怎么就攤上這事∪玻” “怎么了狞换?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舟肉。 經(jīng)常有香客問我修噪,道長(zhǎng),這世上最難降的妖魔是什么路媚? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任黄琼,我火速辦了婚禮脏款,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘周循。我一直安慰自己饮怯,他們只是感情好库倘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布饱亿。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泵肄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天银还,我揣著相機(jī)與錄音,去河邊找鬼。 笑死列吼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蹄溉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了驮瞧?” 一聲冷哼從身側(cè)響起千所,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤籽孙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玻熙,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡称杨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唤殴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乡范。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡芋膘,死狀恐怖潜腻,靈堂內(nèi)的尸體忽然破棺而出精钮,到底是詐尸還是另有隱情,我是刑警寧澤根蟹,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站凌净,受9級(jí)特大地震影響没卸,放射性物質(zhì)發(fā)生泄漏煤蚌。R本人自食惡果不足惜蜘犁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一鹰晨、第九天 我趴在偏房一處隱蔽的房頂上張望棚潦。 院中可真熱鬧纬朝,春花似錦澄峰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽郊霎。三九已至购对,卻和暖如春陶因,著一層夾襖步出監(jiān)牢的瞬間烘苹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留曾雕,地道東北人揩环。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親灰伟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理栈源,服務(wù)發(fā)現(xiàn)挡爵,斷路器,智...
    卡卡羅2017閱讀 134,657評(píng)論 18 139
  • 項(xiàng)目到了一定階段會(huì)出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動(dòng)性越來越大甚垦,代碼維護(hù)與測(cè)試回歸流程越來越繁瑣茶鹃。這個(gè)...
    fdacc6a1e764閱讀 3,183評(píng)論 0 6
  • 我每周會(huì)寫一篇源代碼分析的文章,以后也可能會(huì)有其他主題.如果你喜歡我寫的文章的話,歡迎關(guān)注我的新浪微博@達(dá)達(dá)達(dá)達(dá)s...
    SkyKai閱讀 24,931評(píng)論 23 184
  • 對(duì)于Android開發(fā)老司機(jī)來說肯定不會(huì)陌生,它是一個(gè)基于觀察者模式的事件發(fā)布/訂閱框架艰亮,開發(fā)者可以通過極少的代碼...
    飛揚(yáng)小米閱讀 1,476評(píng)論 0 50
  • 有一個(gè)同學(xué)和我說了一件事迄埃,覺得可以作為日記素材疗韵,就寫出來了。 一個(gè)客戶的房子需要裝修侄非,他得到了兩個(gè)報(bào)價(jià)蕉汪,熟人報(bào)價(jià)是...
    安梓閱讀 475評(píng)論 0 1