面試題之---EventBus源碼解析

點擊上方「

程序員大咖

」,選擇「置頂公眾號」

關鍵時刻,第一時間送達!

來源:Android開發中文站

https://mp.weixin.qq.com/s/zPmzcuaIrgpRiKxBtM92Jg

程序員大咖整理髮布,轉載請聯繫作者獲得授權

(一)介紹

1,EvenetBus是一種發布-訂閱事件匯流排.

代碼簡潔,開銷小,並很好的實現了發送者和接收者的解耦.(是一種觀察者模式)

2,三要素:

  • Event:事件

  • Publisher:發布者,可以在任意線程發布事件

  • 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 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 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 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> 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()); }}

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

  • Android EventBus3.1.1從使用到源碼解析

  • Android EventBus 使用詳解

  • EventBus的使用,數據傳遞

  • EventBus 3.0進階:源碼及其設計模式 完全解析

【點擊成為源碼大神】


推薦閱讀:

TAG:面試 | 解析 | 源碼 | EventBus |