手寫Android事件總線框架Eventbus(簡(jiǎn)易版)

開篇廢話

近期利用業(yè)余時(shí)間后添,跟著大神把Eventbus的框架學(xué)習(xí)了一下愤钾,在這里触创,記錄一下這次學(xué)習(xí)的心得坎藐。
EventBus是針一款對(duì)Android的發(fā)布/訂閱事件總線。它可以讓我們很輕松的實(shí)現(xiàn)在Android各個(gè)組件之間傳遞消息哼绑,并且代碼的可讀性更好岩馍,耦合度更低。
這次學(xué)習(xí) 抖韩,大概有以下這些知識(shí)點(diǎn):

  1.注解和反射的使用
  2.學(xué)習(xí)Eventbus的實(shí)現(xiàn)原理

需要注意的是蛀恩,本次記錄是從實(shí)現(xiàn)的角度來進(jìn)行記錄的,可能跟我們調(diào)用的邏輯順序不一樣茂浮。


技術(shù)詳情

1. 事件接收者訂閱想要的事件

這一步實(shí)現(xiàn)的是双谆,告訴事件生產(chǎn)者,我有這些事件需要處理席揽,通過注解進(jìn)行標(biāo)記顽馋,方便我們的框架進(jìn)行收集此類需要處理的事件,例如以下這些操作:

@Subscribe(threadMode = ThreadMode.MainThread)
public void showTextView(SenduoEvent senduoEvent){
    tvSenduobusInfo.setText(senduoEvent.toString());
}

@Subscribe(threadMode = ThreadMode.Async)
public void showLog(SenduoEvent senduoEvent){
    Log.e(TAG,senduoEvent.toString());
}

通過以上步驟幌羞,就相當(dāng)于告訴我們的框架寸谜,這里需要處理這兩個(gè)事件

2. 往事件生產(chǎn)者注冊(cè) 和 反注冊(cè)想要訂閱的事件

通過第一步,事件接收者已經(jīng)把想要接收的事件告知了我們這個(gè)框架新翎,我們這個(gè)框架程帕,就需要通過不同的類來收集這些事件,調(diào)用者地啰,通過調(diào)用以下這行代碼愁拭,框架就會(huì)進(jìn)行收集記錄各自訂閱的事件:

  Senduobus.getDefault().register(this);

這一步,框架其實(shí)做了蠻多工作的亏吝。
首先岭埠,通過方法的注解,記錄當(dāng)前類訂閱的所有事件

public void register(Object activity){

    List<SubscribleMethod> list = cacheMap.get(activity);

    if(list == null){
        list = getSubscribleMethods(activity);
        cacheMap.put(activity,list);
    }

}

這里的getSubscribleMethods(Object)方法就是通過類的注解,來獲取處理事件的方法:

   private List<SubscribleMethod> getSubscribleMethods(Object activity) {

    List<SubscribleMethod> list = new ArrayList<>();

    Class clazz = activity.getClass();

    while(clazz != null){
        String name = clazz.getName();
        if(name.startsWith("java.")
                || name.startsWith("javax.")
                || name.startsWith("android.")){//如果類全名以這些字符開頭惜论,則認(rèn)為是jdk的许赃,不是我們自定義的,自然沒必要去拿注解
            break;
        }

        Method[] methods = clazz.getDeclaredMethods();//獲得當(dāng)前class所有生命的public方法
        for(Method method : methods){
            Subscribe subscribe = method.getAnnotation(Subscribe.class);

            if(subscribe == null){
                continue;
            }

            Class[] paratems = method.getParameterTypes();
            if(paratems.length != 1){
                throw new RuntimeException("senduobus 只能接收到一個(gè)參數(shù)");
            }

            ThreadMode threadMode = subscribe.threadMode();

            SubscribleMethod subscribleMethod = new SubscribleMethod(method,threadMode,paratems[0]);
            list.add(subscribleMethod);
        }

        clazz = clazz.getSuperclass();

    }
    return list;
}

然后馆类,通過一個(gè)內(nèi)存緩存Map記錄類與對(duì)應(yīng)類的訂閱事件混聊,減少框架反射的次數(shù)

3. 事件生產(chǎn)者分發(fā)事件

通過以上兩步,事件接收者乾巧,以及其訂閱的事件都已經(jīng)收集并且綁定好了句喜,就差事件生產(chǎn)者進(jìn)行分發(fā)消息了。
這里需要注意的是沟于,如何確認(rèn)當(dāng)前這個(gè)事件需要由哪個(gè)接收者來處理咳胃,框架中,是通過方法的參數(shù)類型進(jìn)行分發(fā)確認(rèn)的旷太,以下這種形式:

    Senduobus.getDefault().post(new SenduoEvent("1","測(cè)試發(fā)送消息"));

其中SenduoEvent類展懈,通過這個(gè)類來確認(rèn)該由哪些事件接收者來處理事件:

  public void post(final Object senduoEvent){

    Set<Object> set = cacheMap.keySet();

    Iterator iterator = set.iterator();
    while(iterator.hasNext()){
        final Object activity = iterator.next();
        List<SubscribleMethod> list = cacheMap.get(activity);
        for(final SubscribleMethod subscribleMethod : list){
            if(subscribleMethod.getEventType().isAssignableFrom(senduoEvent.getClass())){


                switch(subscribleMethod.getThreadMode()){
                    case Async:
                        if(Looper.myLooper() == Looper.getMainLooper()){
                            executorService.execute(new Runnable() {
                                @Override
                                public void run() {
                                    invoke(subscribleMethod,activity,senduoEvent);
                                }
                            });
                        }else{
                            invoke(subscribleMethod,activity,senduoEvent);
                        }
                        break;
                    case PostThread:

                        break;
                    case MainThread:
                        if(Looper.myLooper() == Looper.getMainLooper()){
                            invoke(subscribleMethod,activity,senduoEvent);
                        }else{
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    invoke(subscribleMethod,activity,senduoEvent);
                                }
                            });
                        }
                        break;
                    case BackgroundThread:
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

里面有一些線程切換的邏輯,具體可查看ThreadMode中的注釋供璧,方便理解存崖。

以下是此項(xiàng)目的源碼:

Senduobus

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗜傅,隨后出現(xiàn)的幾起案子金句,更是在濱河造成了極大的恐慌檩赢,老刑警劉巖吕嘀,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異贞瞒,居然都是意外死亡偶房,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門军浆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棕洋,“玉大人,你說我怎么就攤上這事乒融£蹋” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵赞季,是天一觀的道長(zhǎng)愧捕。 經(jīng)常有香客問我,道長(zhǎng)申钩,這世上最難降的妖魔是什么次绘? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上邮偎,老公的妹妹穿的比我還像新娘管跺。我一直安慰自己,他們只是感情好禾进,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布豁跑。 她就那樣靜靜地躺著,像睡著了一般泻云。 火紅的嫁衣襯著肌膚如雪贩绕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天壶愤,我揣著相機(jī)與錄音淑倾,去河邊找鬼。 笑死征椒,一個(gè)胖子當(dāng)著我的面吹牛娇哆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勃救,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碍讨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蒙秒?” 一聲冷哼從身側(cè)響起勃黍,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晕讲,沒想到半個(gè)月后覆获,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓢省,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年弄息,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勤婚。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摹量,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出馒胆,到底是詐尸還是另有隱情缨称,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布祝迂,位于F島的核電站睦尽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏液兽。R本人自食惡果不足惜骂删,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一掌动、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宁玫,春花似錦粗恢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至佛掖,卻和暖如春妖碉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背芥被。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工欧宜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拴魄。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓冗茸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親匹中。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夏漱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,144評(píng)論 25 707
  • 量上來了挂绰?馬上都不想玩了,就不喜歡跟大批次人玩一樣的服赎,膈應(yīng)葵蒂![摳鼻] 風(fēng)起大了就有點(diǎn)瘋的癥狀,不能隨专肪!再約摸個(gè)玩意...
    縱情嬉戲天地間閱讀 325評(píng)論 0 0
  • Anby群閱讀 716評(píng)論 9 15
  • 每天大腦最活躍的時(shí)候就是在下班回家的公交車上刹勃。 終于找到了一個(gè)地方,可以自說自話嚎尤,不是QQ空間,沒有同學(xué)伍宦,也不是朋...
    移動(dòng)的五花肉閱讀 230評(píng)論 0 0