1. 程式人生 > >面試題之---EventBus原始碼解析

面試題之---EventBus原始碼解析

(一)介紹

1,EvenetBus是一種釋出-訂閱事件匯流排.程式碼簡潔,開銷小,並很好的實現了傳送者和接收者的解耦.(是一種觀察者模式)


2,三要素:
A,Event:事件,
B,Publisher:釋出者,可以在任意執行緒釋出事件
C,Subscrible:訂閱者,

3,通常情況下安卓下資料的傳遞有下面幾種方法:

3.1.通過intent傳遞,包括顯式意圖和隱式意圖,廣播(Broadcast)和服務都能通過Intent傳遞

    傳遞的資料型別包括8大基本資料型別    實現Parcelable或Serializable介面的型別   以及集合陣列型別

3.2.靜態變數傳遞  在工具類下 宣告一個Object型別的靜態變數   在A中將要傳遞的值,在B中通過這個靜態變數取出來

3.3.通過handle在不同的執行緒中傳遞Object型別的資料

3.4.通過構造方法傳遞Object型別的資料

3.5.通過SharedPreferences傳遞八大基本資料型別

3.6.通過ContentProvider在程序間共享資料

3.7.通過aidl在程序程序傳遞資料

3.8.通過流(本地檔案)傳遞八大基本資料型別和實現Serializable介面的資料型別

3.9.通過基類中的屬性或者方法

    屬性: 基類公有屬性  在某個子類中賦值   其他子類中都能使用

    方法: 子類呼叫父類的某個方法給父類某個屬性賦值  另外一個子類通過父類的另一個公有方法獲取這個值(這個方法把值返回)


(二)基本使用

##先訂閱,後釋出

1,新增依賴
    compile 'org.greenrobot:eventbus:3.1.1'
2,註冊事件
    public class MessageEvent {

    private String message;

    public MessageEvent(String message){
        this.message = message;
    }

    public String getMessage(){
        return message;
    }
}

3,在接受訊息的程式碼
      //註冊成為訂閱者


      EventBus.getDefault().register(this);

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除註冊
        EventBus.getDefault().unregister(this);
    }

    //訂閱方法,當接收到事件的時候,會呼叫該方法
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(MessageEvent messageEvent){
        Log.e("date","receive it");
        Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
    }
4,在傳送訊息的地方  
     EventBus.getDefault().post(new MessageEvent("從fragment將資料傳遞到activity22222222"));

###先發布,再訂閱,黏性事件
- 5,在接受訊息的程式碼
     //註冊成為訂閱者
      EventBus.getDefault().register(this);

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除註冊
        EventBus.getDefault().unregister(this);
    }

    //訂閱方法,當接收到事件的時候,會呼叫該方法
    @Subscribe(threadMode = ThreadMode.MAIN,stick = true)
    public void onEvent(MessageEvent messageEvent){
        Log.e("date","receive it");
        Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
    }
- 6,在傳送訊息的地方
     EventBus.getDefault().postSticky(new MessageEvent("從fragment將資料傳遞到activity22222222"));

(三)四個訂閱方法

onEvent:

如果使用onEvent作為訂閱函式,那麼該事件在哪個執行緒釋出出來的,onEvent就會在這個執行緒中執行,也就是說釋出事件和接收事件執行緒在同一個執行緒。使用這個方法時,在onEvent方法中不能執行耗時操作,如果執行耗時操作容易導致事件分發延遲。

onEventMainThread:

如果使用onEventMainThread作為訂閱函式,那麼不論事件是在哪個執行緒中釋出出來的,onEventMainThread都會在UI執行緒中執行,接收事件就會在UI執行緒中執行,這個在Android中是非常有用的,因為在Android中只能在UI執行緒中跟新UI,所以在onEvnetMainThread方法中是不能執行耗時操作的。

onEventBackground:

如果使用onEventBackgrond作為訂閱函式,那麼如果事件是在UI執行緒中釋出出來的,那麼onEventBackground就會在子執行緒中執行,如果事件本來就是子執行緒中釋出出來的,那麼onEventBackground函式直接在該子執行緒中執行。

onEventAsync:

使用這個函式作為訂閱函式,那麼無論事件在哪個執行緒釋出,都會建立新的子執行緒在執行onEventAsync。

(四)原始碼解析

                                  

                

         可以看到,釋出者(Publisher)使用post()方法將Event傳送到Event Bus,而後Event Bus自動將Event傳送到多個訂閱者(Subcriber)。這裡需要注意兩個地方:

         (1)一個釋出者可以對應多個訂閱者。

        (2)3.0以前訂閱者的訂閱方法為onEvent()onEventMainThread()onEventBackgroundThread()onEventAsync()。在Event Bus3.0之後統一採用註解@Subscribe的形式,具體實現方式見下文。

   // 1主執行緒呼叫
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void eventBusMain(String str){
        Log.i("TAG", "MAIN:"+str+" Thread="+Thread.currentThread().getId());
    }

    // 2.釋出執行緒為主執行緒,新開執行緒呼叫
    // 2.釋出執行緒為子執行緒,釋出執行緒呼叫
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void eventBusBg(String str){
        Log.i("TAG", "BACKGROUND:"+str+" Thread="+Thread.currentThread().getId());
    }

    //3,哪個執行緒釋出,就在哪個執行緒呼叫
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void eventBusPosting(String str){
        Log.i("TAG", "POSTING:"+str+" Thread="+Thread.currentThread().getId());
    }

    // 4,每次都新開執行緒呼叫
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void eventBusAsync(String str){
        Log.i("TAG", "ASYNC:"+str+" Thread="+Thread.currentThread().getId());
    }

1,呼叫getDefault(),裡面採用單利雙重鎖模式建立Eventbus物件

static volatile EventBus defaultInstance;
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}


2,構造方法
2.1,粘性事件,儲存到ConCurrenHashMap集合,(在構造方法中實現),
HashMap效率高,但執行緒不安全,在多執行緒的情況下,儘量用ConcurrentHashMap,避免多執行緒併發異常

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>(); //執行緒安全,
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    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;
}



3,註冊register()方法主要做了2件事:
3.1,找到訂閱者的方法.找出傳進來的訂閱者的所有訂閱方法,然後遍歷訂閱者的方法.
A,通過反射來獲取訂閱者中所有的方法,並根據方法的型別,引數和註解找到訂閱方法.
3.2,訂閱者的註冊

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    //找到訂閱者的方法.找出傳進來的訂閱者的所有訂閱方法,然後遍歷訂閱者的方法.
  List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //訂閱者的註冊
             subscribe(subscriber, subscriberMethod);
        }
    }
}


private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // 通過反射來獲取訂閱者中所有的方法
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // 
,並根據方法的型別,引數和註解找到訂閱方法.
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;}

//通過CopyOnWriteArrayList儲存Subscription,
    //Arraylist效率高,但執行緒不安全,在多執行緒的情況下,使用CopyOnWriteArrayList,避免多執行緒併發異常

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    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);
        }
    }



3,事件傳送post(),

public void post(Object event) {
  //PostingThreadState 儲存著事件佇列和執行緒狀態資訊
   PostingThreadState postingState = currentPostingThreadState.get();
    //獲取事假佇列,並將當期事件插入事件佇列
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
         //處理佇列中的所有事件,
        //將所有的事情交給postSingleEvent處理,並移除該事件
          while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

4,取消事件訂閱

public synchronized void unregister(Object subscriber) {
    //typesBySubscriber是一個map集合,
    //通過subscriber找到 subscribedTypes (事件型別集合),
     List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        //將subscriber對應的eventType從typesBySubscriber移除
    typesBySubscriber.remove(subscriber);

} else { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); }}


借鑑:劉望舒先生的進階之光