流行框架源碼分析(1)-EventBus3.0源碼解析

主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)
?因?yàn)閲?guó)慶放假的緣故,好幾天沒有寫文章媳板,今天抽空來(lái)寫一篇杏糙,那我們就從我們平常用的比較熟悉的框架開始講解,今天講解一款比較熟悉的框架EventBus3.0衬鱼,在實(shí)際項(xiàng)目用的也比較多,廢話不多說(shuō)憔杨,直接開始。

國(guó)慶快樂

一.目標(biāo)

今天寫這篇主要是從源碼角度來(lái)講解一個(gè)流行框架的原理蒜胖,使用方法我就不講了消别,因?yàn)楸容^簡(jiǎn)單抛蚤,那么我們今天的目標(biāo)很明確:
1.從源碼角度了解EventBus的用法;
2.通過源碼來(lái)學(xué)習(xí)一個(gè)開源項(xiàng)目可能用到的技術(shù)點(diǎn)寻狂。

二.源碼分析

我們知道我們EventBus3.0的用法分為創(chuàng)建岁经,注冊(cè),發(fā)送蛇券,粘性事件和后面加入的索引缀壤。今天我們將從一個(gè)一個(gè)開始講。為了后面說(shuō)的時(shí)候比較順利呢纠亚,我們先說(shuō)下索引的作用下塘慕。

1.Subscriber Index

如果要用到索引的話我們需要在gradle文件里面添加如下:

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
apply plugin: 'com.neenbedankt.android-apt'
 
dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
    arguments {
        eventBusIndex "com.example.myapp.MyEventBusIndex"
    }
}

這是典型的apt的配置,當(dāng)然現(xiàn)在一般框架用的annotationProcessor的方式蒂胞,其實(shí)也是一樣的图呢,都差不多。這里的配置主要是用來(lái)生成放置一個(gè)索引類的骗随,我們使用的時(shí)候會(huì)這樣使用:

 EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();        //開啟加速模式

當(dāng)然這是其中的一種使用方法蛤织。我們不去看其他的方式主要我們看到這個(gè)地方索引是用addIndex方法加入的,這里面的MyEventBusIndex是哪里生成的呢鸿染?其實(shí)這個(gè)地方就是用的apt的方式生成的(包名和類名是根據(jù)上面gradle里面配置的)指蚜。首先我們來(lái)看生成的文件長(zhǎng)什么樣:

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(com.example.zz.eventbusnewdemo.MainActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", com.example.zz.eventbusnewdemo.event.FirstEvent.class, ThreadMode.MAIN,
                    60, false),
        }));

        putIndex(new SimpleSubscriberInfo(com.example.zz.eventbusnewdemo.ThirdActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onStickyEvent", com.example.zz.eventbusnewdemo.event.StickyEvent.class,
                    ThreadMode.MAIN, 0, true),
        }));

        putIndex(new SimpleSubscriberInfo(com.example.zz.eventbusnewdemo.SecondActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onSecondEvent", com.example.zz.eventbusnewdemo.event.FirstEvent.class,
                    ThreadMode.POSTING, 1000, false),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

這個(gè)類其實(shí)很簡(jiǎn)單,就是調(diào)用putIndex方法將類與類對(duì)應(yīng)的訂閱信息放進(jìn)Map里面涨椒。因?yàn)檫@些方法是放進(jìn)static中的摊鸡,所以在new的時(shí)候就會(huì)調(diào)用了。具體這個(gè)類怎么生成我到時(shí)會(huì)在編譯期注解那里說(shuō)到丢烘,代碼主要在源碼中的下圖位置:

Processor

2.創(chuàng)建

我們知道我們一個(gè)類要想成為訂閱者必須在類中注冊(cè):

  EventBus.getDefault().register(this);

所以我們很自然來(lái)看getDefault()方法是干了什么:

   /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

這里就是獲取EventBus的實(shí)例柱宦,這里使用了單例的模式。那么我們這里接著就是看EventBus的構(gòu)造函數(shù)了:

 public EventBus() {
        this(DEFAULT_BUILDER);
    }

我們看到這里EventBus的構(gòu)造函數(shù)是public的播瞳,也就是說(shuō)我們可以自己去實(shí)例化一個(gè)EventBus掸刊,所以也就是說(shuō)允許多個(gè)EventBus,然后獨(dú)立處理自己的事件赢乓,但是如果通過getDefault()方法創(chuàng)建的話忧侧,因?yàn)槭鞘褂脝卫J竭M(jìn)行創(chuàng)建,所以只會(huì)有一個(gè)EventBus實(shí)例牌芋。我們繼續(xù)看這里this調(diào)用了另外一個(gè)構(gòu)造函數(shù):

    EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        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;
    }

我們看到這個(gè)類里面實(shí)例化了好多類蚓炬,首先我們看下以下幾個(gè):

   private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    private final Map<Class<?>, Object> stickyEvents;

subscriptionsByEventType:這個(gè)類主要是key為event,value為Subscription的集合躺屁,Subscription是個(gè)封裝的類肯夏,里面主要有訂閱者和訂閱類兩個(gè)類,這個(gè)map主要是在發(fā)送的時(shí)候根據(jù)event來(lái)查找訂閱者的。
typesBySubscriber:這個(gè)類主要是key為訂閱者驯击,value為event烁兰,主要是注冊(cè)的時(shí)候會(huì)用到。
stickyEvents:這個(gè)方法主要保存粘性事件的徊都。
這些都會(huì)在后面講到沪斟,所以大家放心,如果這個(gè)地方還不知道他們是干嘛用的暇矫。然后我們來(lái)看下面幾個(gè)Poster:

  private final HandlerPoster mainThreadPoster;
    private final BackgroundPoster backgroundPoster;
    private final AsyncPoster asyncPoster;

這幾個(gè)Poster在ThreadMode和粘性事件的時(shí)候都會(huì)用到主之。主要是線程有關(guān)。另外我們看構(gòu)造函數(shù)里面下面一些都是用builder給EventBus的屬性賦值,這里主要是通過建造者的設(shè)計(jì)模式給builder構(gòu)造不同的功能。我們可以通過builder構(gòu)造如下:

  eventBus = EventBus.builder().eventInheritance(false).build();

具體的建造者有哪些這里不展開講了看峻。我們有用到可以看下很簡(jiǎn)單。

3.注冊(cè)

注冊(cè)的話主要是關(guān)聯(lián)訂閱者和訂閱方法史翘,3.0以后的話訂閱方法支持注解@Subscribe,注解的話支持配置如下:

注解

支持配置線程模式冀续,是否是粘性事件琼讽,優(yōu)先級(jí)。然后我們會(huì)調(diào)用register()方法來(lái)進(jìn)行注冊(cè):

  public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

我們看到首先我們是查找訂閱者對(duì)應(yīng)的訂閱方法(一個(gè)訂閱者對(duì)應(yīng)多個(gè)訂閱方法)洪唐,然后就行訂閱钻蹬。首先我們看findSubscriberMethods方法干了些什么:

  List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先從緩存中查找訂閱者對(duì)應(yīng)的訂閱方法,如果有則直接返回即可
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
//是否忽略索引功能凭需,這個(gè)地方3.0用來(lái)加速的问欠,我們不忽略,要想忽略可以通過builder進(jìn)行設(shè)置
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
//最后放進(jìn)緩存粒蜈,然后返回對(duì)應(yīng)的訂閱方法集合
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

從我們注釋可以看到這里的ignoreGeneratedIndex為false即不忽略索引功能顺献,所以我們會(huì)走到findUsingInfo方法里面,我們看下這個(gè)方法到底是做了什么:

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//這里首先準(zhǔn)備一個(gè)FindState對(duì)象枯怖,這個(gè)類主要保存了訂閱者信息
        FindState findState = prepareFindState();
//用訂閱者類初始化findState對(duì)象
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
//獲取訂閱者信息注整,如果開啟了索引功能就不會(huì)為空否則會(huì)為空
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
//向上往父類進(jìn)行查找
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

上面我們看到一個(gè)FindState類,我們來(lái)看看FindState類主要有哪些信息呢度硝?

 static class FindState {
//訂閱方法列表
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//key為event肿轨,value為Method,主要后面checkAdd方法會(huì)用到
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//用方法和event生成的key蕊程,value為訂閱類椒袍,這個(gè)在checkAddWithMethodSignature方法里面會(huì)用到
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;
.......
}

我們看到這里面FindState保存了訂閱者和訂閱方法以及在檢查的方法里面會(huì)用到,我們程序調(diào)用findState.initForSubscriber(subscriberClass)就是將FindState的屬性賦值如下:

 void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

我們看到這個(gè)地方其實(shí)只有訂閱者被賦值了藻茂。所以循環(huán)會(huì)走進(jìn)去驹暑。然后我們看到getSubscriberInfo方法玫恳,這個(gè)方法具體是做了什么呢,看返回知道返回的是訂閱的信息优俘,那是怎么得到:

    private SubscriberInfo getSubscriberInfo(FindState findState) {
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        if (subscriberInfoIndexes != null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

我們知道我們第一個(gè)subscriberInfo是空的所以判斷不成功纽窟,第二個(gè)判斷的subscriberInfoIndexes 是什么呢?這個(gè)其實(shí)就是 List<SubscriberInfoIndex> subscriberInfoIndexes兼吓,這個(gè)類是什么時(shí)候賦值的呢?這個(gè)其實(shí)我們前面說(shuō)過了我們?cè)赼ddIndex方法的時(shí)候已經(jīng)賦值過了森枪。

  /** Adds an index generated by EventBus' annotation preprocessor. */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if(subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

也就是說(shuō)了添加了索引這個(gè)subscriberInfoIndexes才不會(huì)為空视搏,不然將返回null,顯然我們這里是能得到的县袱,因?yàn)榫幾g期我們已經(jīng)putIndex了浑娜,文章的開篇就說(shuō)了,不知道可以返回回去看下式散。
所以我們findUsingInfo方法就會(huì)繼續(xù)走到獲取訂閱信息里面的所有方法筋遭,然后調(diào)用findState的checkAdd方法:

        boolean checkAdd(Method method, Class<?> eventType) {
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                return true;
            } else {
                if (existing instanceof Method) {
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }

首先我們看下程序是檢驗(yàn)event對(duì)應(yīng)的訂閱方法存不存在(put方法將value放置進(jìn)去的同時(shí)會(huì)返回一個(gè)舊值,也就是說(shuō)已經(jīng)有event對(duì)應(yīng)的方法就會(huì)返回)如果是沒有訂閱過的直接返回true暴拄。不然接著判斷程序走到checkAddWithMethodSignature方法:

  private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());

            String methodKey = methodKeyBuilder.toString();
            Class<?> methodClass = method.getDeclaringClass();
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                // Only add if not already found in a sub class
                return true;
            } else {
                // Revert the put, old class is further down the class hierarchy
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

這個(gè)方法也不難看懂漓滔,這個(gè)方法首先是拼接了方法名和事件名稱作為key,然后將訂閱者作為value放置進(jìn)去乖篷,如果是第一次的話那肯定返回是null响驴,程序就返回true,但是這個(gè)訂閱方法已經(jīng)有對(duì)應(yīng)的話撕蔼,那么會(huì)接著判斷
methodClassOld.isAssignableFrom(methodClass)豁鲤,意思就是說(shuō)舊的注冊(cè)過的訂閱者是否是新的訂閱者的父類或者同個(gè)類,如果是的話還是返回ture即這是個(gè)新的訂閱者鲸沮,需要添加琳骡。那么到底這兩個(gè)方法(checkAdd,checkAddWithMethodSignature)適用的場(chǎng)景是什么呢讼溺?

場(chǎng)景一:假如一個(gè)類里面有多個(gè)訂閱方法楣号,而這些方法的方法名是一樣的,但是參數(shù)不一樣即接收的事件不一樣肾胯,那么在checkAdd中的existing不為null竖席,會(huì)到checkAddWithMethodSignature中,這個(gè)方法里面又會(huì)根據(jù)方法名和事件名進(jìn)行作為key判斷敬肚,顯然這個(gè)地方拼接的key在這種情況下是不同的毕荐。方法返回true,所以也就是說(shuō)艳馒,允許一個(gè)類有多個(gè)參數(shù)不同的相同方法憎亚。

場(chǎng)景二:這里假設(shè)兩個(gè)類有繼承關(guān)系员寇,B類繼承了A類,B類中重寫了A類中的訂閱方法第美,那么在checkAdd方法中的exiting不為null蝶锋,會(huì)到checkAddWithMethodSignature中,顯然這個(gè)地方因?yàn)榉椒褪录且粯拥氖餐詍ethodClassOld 不為空扳缕,然后會(huì)判斷isAssignableFrom,由于B不是A類的父類别威,所以這里會(huì)返回false躯舔,也就是說(shuō)A類里面的訂閱方法不會(huì)被通知到了。所以子類重寫父類的訂閱方法省古,那么父類的訂閱方法就失效了粥庄,顯然這也是符合設(shè)計(jì)思想的。

接著我們回到findUsingInfo方法豺妓,程序會(huì)接著走到最后一句getMethodsAndRelease方法:

  private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

首先我們會(huì)得到findstate對(duì)象里面的訂閱方法惜互,然后將findstate放進(jìn)緩存池里面。最后程序返回訂閱類對(duì)應(yīng)的訂閱方法集合琳拭,這樣我們程序回到EventBus的register()方法里面训堆,我們程序調(diào)用了subscribe方法:

   // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
//首先將訂閱者和訂閱的方法封裝到SubSciption中
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根據(jù)event獲取對(duì)應(yīng)的Subsciption        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
//如果為null表示沒有訂閱
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
//如果已經(jīng)訂閱過了就拋出錯(cuò)誤
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
//遍歷訂閱集合,然后將新添加的訂閱按照優(yōu)先級(jí)進(jìn)行添加
        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ù)訂閱者來(lái)獲取訂閱的事件集合臀栈,如果還沒有就創(chuàng)建然后放進(jìn)去
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
//判斷訂閱方法是粘性的蔫慧,如果是粘性的會(huì)直接就發(fā)送,后面也會(huì)詳細(xì)做說(shuō)明
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

到這里我們注冊(cè)的流程已經(jīng)講解完畢权薯,我們這里做個(gè)總結(jié):
在注冊(cè)的時(shí)候姑躲,會(huì)先查找這個(gè)訂閱類對(duì)應(yīng)于哪幾個(gè)訂閱方法,這里面會(huì)判斷是要從索引里面查找還是反射進(jìn)行遍歷然后找到符合條件的方法盟蚣,查找的時(shí)候會(huì)通過checkAdd和checkAddWithMethodSignature方法進(jìn)行篩選黍析。找到訂閱類對(duì)應(yīng)的訂閱方法然后就遍歷訂閱方法調(diào)用subscibe方法放進(jìn)subscriptions集合中。

4.發(fā)送

發(fā)送事件的時(shí)候我們一般會(huì)調(diào)用如下代碼:

 EventBus.getDefault().post(new FirstEvent("Post Content"));

所以我們直接來(lái)看EventBus的post方法:

  public void post(Object event) {
//從ThreadLocal中獲取一個(gè)PostingThreadState 
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
//將事件添加進(jìn)事件隊(duì)列中
        eventQueue.add(event);

        if (!postingState.isPosting) {
//判斷是否在主線程中
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
//隊(duì)列不為空就發(fā)送
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

程序第一句會(huì)獲取PostingThreadState對(duì)象屎开,那currentPostingThreadState到底是啥呢阐枣,我們可以看看:

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

我們看到這里new出這個(gè)對(duì)象,然后get的時(shí)候會(huì)從ThreadLocal中獲取奄抽,也就是說(shuō)這個(gè)對(duì)象是線程安全的蔼两,如果不同的線程這個(gè)對(duì)象是不同的。然后我們程序會(huì)調(diào)用postSingleEvent方法:

 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
//eventInheritance這個(gè)可以在builder中設(shè)置逞度,這個(gè)標(biāo)志的意思是是否考慮繼承關(guān)系额划,
//如果考慮的話那么如果事件繼承自父類,那么父類也會(huì)作為事件被發(fā)送
        if (eventInheritance) {
//查找該事件對(duì)應(yīng)的所有父類和接口
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
//遍歷所有的事件然后進(jìn)行發(fā)送
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
//如果沒有找到訂閱信息
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

從上面程序可以看到如果找到事件對(duì)應(yīng)的所有父類和接口則進(jìn)行遍歷發(fā)送档泽。程序會(huì)調(diào)用postSingleEventForEventType方法:

 private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
//根據(jù)事件來(lái)獲取subscriptions集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
//然后發(fā)送俊戳,參數(shù)為subscription和事件揖赴,是否在主線程
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }

程序接下來(lái)調(diào)用了postToSubscription,傳的參數(shù)我們已經(jīng)說(shuō)明了抑胎,我們可以具體來(lái)看這個(gè)方法干了啥:

  private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        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);
        }
    }

這個(gè)方法很簡(jiǎn)單有沒有燥滑,根據(jù)threadMode線程模式來(lái)判斷要怎么調(diào)用。如果是POSTING則會(huì)直接調(diào)用invokeSubscriber阿逃,如果是MAIN但是在主線程也會(huì)調(diào)用invokeSubscriber铭拧,如果是BACKGROUND,但是不在主線程也會(huì)調(diào)用invokeSubscriber恃锉,不然就會(huì)調(diào)用幾個(gè)poster進(jìn)行進(jìn)隊(duì)列羽历。首先我們看下invokeSubscriber:

 void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

這個(gè)方法很簡(jiǎn)單呀,就是利用反射進(jìn)行對(duì)方法進(jìn)行調(diào)用淡喜。然后我們看看其他的用poster進(jìn)行調(diào)用的。首先我們看下mainThreadPoster(是個(gè)HandlerPoster):

final class HandlerPoster extends Handler {
//待發(fā)送的post隊(duì)列
    private final PendingPostQueue queue;
//最大的運(yùn)行時(shí)間诵闭,因?yàn)樵谥骶€程不能執(zhí)行時(shí)間過長(zhǎng)
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }
........
}

這個(gè)類里面有PendingPostQueue 是個(gè)鏈表的結(jié)構(gòu)炼团,鏈表里面的節(jié)點(diǎn)PendingPost包括了event和subscription,這個(gè)poster主要是threadMode為MAIN然后不在主線程時(shí)候調(diào)用疏尿,我們看enqueue做了啥:

   void enqueue(Subscription subscription, Object event) {
//根據(jù)subscription和event來(lái)構(gòu)建PendingPost對(duì)象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
//加入隊(duì)列中
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
//用handler來(lái)發(fā)送消息
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

這個(gè)方法主要是獲取到PendingPost對(duì)象然后加入隊(duì)列瘟芝,接著發(fā)送消息。由于這個(gè)Handler初始化的時(shí)候new HandlerPoster(this, Looper.getMainLooper(), 10)是傳入主線程的Looper褥琐,所以這個(gè)是運(yùn)行在ui線程的:

 @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
//死循環(huán)锌俱,不斷從隊(duì)列中取出PendingPost
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
//接著調(diào)用這個(gè)方法,這樣的話這個(gè)方法就是在主線程的
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

我們看到敌呈,這個(gè)方法就是取出隊(duì)列里面的PendingPost然后進(jìn)行正常的invokeSubscriber贸宏。這樣的話就是在主線程運(yùn)行了。接著我們就看看backgroundPoster:

final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }
}

我們看到這個(gè)方法里面也是有一個(gè)PendingPostQueue磕洪,跟HandlerPost是一樣的吭练,這個(gè)BackgroundPoster實(shí)現(xiàn)了Runnable接口,其他沒有什么析显,我們來(lái)看enqueue方法:

 public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

我們看到這個(gè)方法前面都一樣鲫咽,就最后調(diào)用了 eventBus.getExecutorService().execute(this)執(zhí)行了這個(gè)線程,所以接下來(lái)會(huì)調(diào)用這個(gè)線程的run方法:

 @Override
    public void run() {
        try {
            try {
                while (true) {
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
}

我們看到這個(gè)run方法里面也是取出隊(duì)列中的PendingPost然后調(diào)用invokeSubscriber方法谷异,也就是說(shuō)這個(gè)方法是運(yùn)行在子線程中的分尸。最后我們看AsyncPoster,這個(gè)我就不認(rèn)真說(shuō)了歹嘹,其實(shí)和BackgroudPoster是類似的箩绍,只是沒有判斷是不是有一條線程正在運(yùn)行,就是說(shuō)每次運(yùn)行都是在不同的線程荞下。

5.取消

我們注冊(cè)事件之后伶选,沒有用的時(shí)候要取消史飞,即我們會(huì)調(diào)用方法如下:

EventBus.getDefault().unregister(this);

所以我們直接就來(lái)看看unregister是做了什么呢?

  /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
//根據(jù)訂閱者來(lái)獲取事件
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
//遍歷所有的事件
for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
//移除
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

我們找到訂閱者對(duì)應(yīng)的事件集合仰税,然后遍歷之后調(diào)用unsubscribeByEventType构资,那我們就直接看看這個(gè)方法做了啥:

  private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//根據(jù)事件獲取訂閱者信息
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
//取出訂閱者然后判斷是否跟要?jiǎng)h除的訂閱者相同,相同則刪除
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

我們看到注銷的流程是很簡(jiǎn)單的陨簇,從typesBySubscriber和subscriptionsByEventType移除跟訂閱者有關(guān)的信息吐绵。

6.粘性事件

粘性事件跟其他事件不同的是先發(fā)送出去,然后再注冊(cè)接收到事件河绽。粘性事件有時(shí)候還是很有用的己单。粘性的事件發(fā)送是通過postSticky方法來(lái)發(fā)送的,我們這里來(lái)看看:

  public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

我們看這個(gè)方法將事件為value和事件對(duì)應(yīng)的class對(duì)象為key放進(jìn)stickyEvents(Map)中耙饰,然后調(diào)用post事件纹笼,但是這時(shí)候因?yàn)檫€沒有注冊(cè)的話是找不到訂閱者的,但是為什么在注冊(cè)的時(shí)候又可以收到粘性事件呢苟跪?其實(shí)我們之前有看過廷痘,我們來(lái)看看:

   // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
//首先將訂閱者和訂閱的方法封裝到SubSciption中
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根據(jù)event獲取對(duì)應(yīng)的Subsciption        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
//如果為null表示沒有訂閱
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
//如果已經(jīng)訂閱過了就拋出錯(cuò)誤
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
//遍歷訂閱集合,然后將新添加的訂閱按照優(yōu)先級(jí)進(jìn)行添加
        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ù)訂閱者來(lái)獲取訂閱的事件集合件已,如果還沒有就創(chuàng)建然后放進(jìn)去
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
//判斷訂閱方法是粘性的笋额,如果是粘性的會(huì)直接就發(fā)送,后面也會(huì)詳細(xì)做說(shuō)明
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
//這個(gè)標(biāo)志我們之前也說(shuō)過篷扩,默認(rèn)是true的
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
//發(fā)送粘性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
//根據(jù)event獲取特定的事件然后發(fā)送
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

EventBus并不知道當(dāng)前的訂閱者對(duì)應(yīng)于哪個(gè)事件兄猩,所以要遍歷一遍找到匹配的粘性事件,然后調(diào)用checkPostStickyEventToSubscription方法鉴未,這個(gè)方法就是調(diào)用的postToSubscription枢冤,就是根據(jù)threadMode調(diào)用相應(yīng)的訂閱方法,我們之前已經(jīng)講過了铜秆。好啦掏导,到這里我們已經(jīng)講完了所有的源代碼,收獲還是很大的羽峰,有些知識(shí)技術(shù)點(diǎn)我們會(huì)一一講解的趟咆。
總結(jié):講完EventBus3.0的源碼,我們已經(jīng)對(duì)于應(yīng)用這個(gè)開源框架更有把握了梅屉,遇到問題我們也能輕易排除值纱,所以收獲還是很大的,最后祝大家國(guó)慶快樂哈坯汤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末虐唠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惰聂,更是在濱河造成了極大的恐慌疆偿,老刑警劉巖咱筛,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異杆故,居然都是意外死亡迅箩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門处铛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饲趋,“玉大人,你說(shuō)我怎么就攤上這事撤蟆∞人埽” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵家肯,是天一觀的道長(zhǎng)龄砰。 經(jīng)常有香客問我,道長(zhǎng)讨衣,這世上最難降的妖魔是什么寝贡? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮值依,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碟案。我一直安慰自己愿险,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布价说。 她就那樣靜靜地躺著辆亏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鳖目。 梳的紋絲不亂的頭發(fā)上扮叨,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音领迈,去河邊找鬼彻磁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛狸捅,可吹牛的內(nèi)容都是我干的衷蜓。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼尘喝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼磁浇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起朽褪,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤置吓,失蹤者是張志新(化名)和其女友劉穎无虚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衍锚,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡友题,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了构拳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咆爽。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖置森,靈堂內(nèi)的尸體忽然破棺而出斗埂,到底是詐尸還是另有隱情,我是刑警寧澤凫海,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布呛凶,位于F島的核電站,受9級(jí)特大地震影響行贪,放射性物質(zhì)發(fā)生泄漏漾稀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一建瘫、第九天 我趴在偏房一處隱蔽的房頂上張望崭捍。 院中可真熱鬧,春花似錦啰脚、人聲如沸殷蛇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)粒梦。三九已至,卻和暖如春荸实,著一層夾襖步出監(jiān)牢的瞬間匀们,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工准给, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泄朴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓露氮,卻偏偏與公主長(zhǎng)得像叼旋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沦辙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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