基于反射的EventBus

為什么要手寫一個(gè)EventBus?

有人可能要說(shuō)了,晚上有寫好的EventBus,直接用不得了,自己寫的能有人家寫的好嗎澜薄?確實(shí),我做不到官方那么好摊册。但我們做開發(fā)以來(lái)肤京,一直都是“拿來(lái)主義”,感覺(jué)會(huì)用api實(shí)現(xiàn)項(xiàng)目功能就可以了茅特。即使看過(guò)官方的源碼忘分,也是似懂非懂的,很快就忘記了温治。因?yàn)閷?duì)第三方框架的認(rèn)識(shí)饭庞,如果加上自己的理解,可以寫出一個(gè)簡(jiǎn)易版本的框架熬荆。那么下次有人再問(wèn)你Eventbus的原理舟山,你就可以將你的思路告訴他。

框架思路

寫代碼的時(shí)候卤恳,不是來(lái)了需求就埋頭苦寫累盗,好的設(shè)計(jì)流程能幫你事半功倍。


手寫eventbus.png

思路詳解

注冊(cè)

注冊(cè)的基本思路突琳,通過(guò)上圖的流程圖很清楚了:根據(jù)當(dāng)前的注冊(cè)類若债,獲取SubscribeMethod集合,如果獲取不到就去遍歷帶有指定注解的方法拆融,得到所有注解方法后蠢琳,加入到集合中。

  1. 緩存镜豹,這里通過(guò)map實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的緩存傲须,避免了每次都去遍歷注冊(cè)類中的方法。優(yōu)秀的框架都會(huì)考慮緩存趟脂,平時(shí)寫代碼的時(shí)候注意這一點(diǎn)泰讽。
  2. getSubsribeMethods方法∥羝冢考慮下代碼封裝已卸,而不是把所有的代碼抖寫到register這個(gè)方法中。
  3. isAnnotationPresent 這個(gè)方法表示 某個(gè)方法上面是否含有指定的注解
  4. SubscribeMethod這個(gè)類 是對(duì)注冊(cè)方法的封裝硼一。里面有三個(gè)參數(shù)method(方法),ThreadMode(線程方式),paramType(參數(shù)類型)
public class EventBus {
    public static EventBus eventBus = new EventBus();

    public static EventBus getDefault() {
        return eventBus;
    }

    private Map<Object, List<SubscribeMethod>> cacheMap;
    private Handler handler;
    private ExecutorService executor;

    private EventBus() {
        cacheMap = new HashMap<>();
        handler = new Handler(Looper.getMainLooper());
        executor = Executors.newCachedThreadPool();
    }

    public void register(Object obj) {
        List<SubscribeMethod> subscribeMethods = cacheMap.get(obj);
        if (subscribeMethods != null) {
            return;
        }
        List<SubscribeMethod> list = getSubsribeMethods(obj);
        cacheMap.put(obj, list);


    }

    private List<SubscribeMethod> getSubsribeMethods(Object obj) {
        Class<?> aClass = obj.getClass();
        ArrayList<SubscribeMethod> list = new ArrayList<>();
        while (aClass != null) {

            //如果父類是android java開頭,就不需要
            if (aClass.getName().startsWith("android.")
                    || aClass.getName().startsWith("androidx.")
                    || aClass.getName().startsWith("java.")
                    || aClass.getName().startsWith("javax.")
            ) {
                break;
            }
            // 獲取注冊(cè)類下的所有方法
            Method[] methods = aClass.getDeclaredMethods();
            for (Method method : methods) {
                //如果方法上有指定的注解
                if (method.isAnnotationPresent(Subscribe.class)) {
                    Subscribe annotation = method.getAnnotation(Subscribe.class);
                    //獲取參數(shù)類型
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length != 1) {
                        throw new RuntimeException("參數(shù)必須是一個(gè)");
                    }
                    ThreadMode mode = annotation.getMode();
                    SubscribeMethod subscribeMethod = new SubscribeMethod(method, mode, parameterTypes[0]);
                    subscribeMethod.toString();
                    list.add(subscribeMethod);

                }

            }
            aClass = aClass.getSuperclass();
        }

        return list;
    }

   

  
    public void unregister(Object subscriber) {
        List<SubscribeMethod> list = cacheMap.get(subscriber);
        if (list != null) {
            cacheMap.remove(subscriber);
        }


    }
}
package com.autoai.bus.core;

import java.lang.reflect.Method;

public class SubscribeMethod {

    private Method method;
    private ThreadMode threadMode;

    private Class<?> paramType;

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public ThreadMode getThreadMode() {
        return threadMode;
    }

    public void setThreadMode(ThreadMode threadMode) {
        this.threadMode = threadMode;
    }

    public Class<?> getParamType() {
        return paramType;
    }

    public void setParamType(Class<?> paramType) {
        this.paramType = paramType;
    }

    public SubscribeMethod(Method method, ThreadMode threadMode, Class<?> paramType) {
        this.method = method;
        this.threadMode = threadMode;
        this.paramType = paramType;
    }
}

package com.autoai.bus.core;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
    ThreadMode getMode() default ThreadMode.MAIN;
}

發(fā)送消息

 //發(fā)送消息
    public void post(final Object obj) {
        Set<Object> keySet = cacheMap.keySet();
        Iterator<Object> iterator = keySet.iterator();
        while (iterator.hasNext()) {
            final Object next = iterator.next();
            List<SubscribeMethod> subscribeMethodList = cacheMap.get(next);
            if (subscribeMethodList == null) return;
            for (SubscribeMethod subscribeMethod : subscribeMethodList) {
                final Method method = subscribeMethod.getMethod();
                Class<?> paramType = subscribeMethod.getParamType();
                ThreadMode mode = subscribeMethod.getThreadMode();
                //發(fā)送消息的類型和注冊(cè)方法的參數(shù)是一致的
                if (paramType.isAssignableFrom(obj.getClass())) {
                    switch (mode) {
                        case MAIN:
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                invoke(next, method, obj);
                            } else {
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(next, method, obj);
                                    }
                                });
                            }
                            break;
                        case ASYNC:
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                executor.submit(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(next, method, obj);
                                    }
                                });
                            } else {
                                invoke(next, method, obj);
                            }
                            break;
                        case DEFALUT:
                            break;
                    }


                }

            }
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末累澡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子般贼,更是在濱河造成了極大的恐慌愧哟,老刑警劉巖惑申,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異翅雏,居然都是意外死亡圈驼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門望几,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绩脆,“玉大人,你說(shuō)我怎么就攤上這事橄抹⊙テ龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵楼誓,是天一觀的道長(zhǎng)玉锌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)疟羹,這世上最難降的妖魔是什么主守? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮榄融,結(jié)果婚禮上参淫,老公的妹妹穿的比我還像新娘。我一直安慰自己愧杯,他們只是感情好涎才,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著力九,像睡著了一般耍铜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跌前,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天棕兼,我揣著相機(jī)與錄音,去河邊找鬼舒萎。 笑死程储,一個(gè)胖子當(dāng)著我的面吹牛蹭沛,可吹牛的內(nèi)容都是我干的臂寝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼摊灭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼咆贬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起帚呼,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掏缎,失蹤者是張志新(化名)和其女友劉穎皱蹦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眷蜈,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沪哺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酌儒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辜妓。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖忌怎,靈堂內(nèi)的尸體忽然破棺而出籍滴,到底是詐尸還是另有隱情,我是刑警寧澤榴啸,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布孽惰,位于F島的核電站,受9級(jí)特大地震影響鸥印,放射性物質(zhì)發(fā)生泄漏勋功。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一库说、第九天 我趴在偏房一處隱蔽的房頂上張望酝润。 院中可真熱鬧,春花似錦璃弄、人聲如沸要销。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)疏咐。三九已至,卻和暖如春脐供,著一層夾襖步出監(jiān)牢的瞬間浑塞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工政己, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酌壕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓歇由,卻偏偏與公主長(zhǎng)得像卵牍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沦泌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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

  • 之前寫過(guò)一篇關(guān)于EventBus的文章谢谦,大家的反饋還不錯(cuò)(EventBus3.0使用詳解)释牺,如果你還沒(méi)有使用過(guò)Ev...
    Android平頭哥閱讀 7,380評(píng)論 0 38
  • EventBus是在Android中使用到的發(fā)布-訂閱事件總線框架萝衩,基于觀察者模式,將事件的發(fā)送者和接收者解耦没咙,簡(jiǎn)...
    BrotherTree閱讀 407評(píng)論 0 1
  • EventBus 是一款在 Android 開發(fā)中使用的發(fā)布/訂閱事件總線框架猩谊,基于觀察者模式,將事件的接收者和發(fā)...
    SheHuan閱讀 65,558評(píng)論 13 185
  • EventBus源碼分析(一) EventBus官方介紹為一個(gè)為Android系統(tǒng)優(yōu)化的事件訂閱總線祭刚,它不僅可以很...
    蕉下孤客閱讀 4,017評(píng)論 4 42
  • 前言 在之前的文章 Android 注解系列之APT工具(三) 中预柒,我們介紹了 APT 技術(shù)的及其使用方式,也提到...
    AndyJennifer閱讀 604評(píng)論 0 2