面試題之---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
StringgetMessage
()
{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
EventBusgetDefault
()
{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();//找到訂閱者的方法.找出傳進來的訂閱者的所有訂閱方法,然後遍歷訂閱者的方法.
Listthis
) {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); CopyOnWriteArrayListget
(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
();//獲取事假隊列,並將當期事件插入事件隊列
List4,取消事件訂閱
public
synchronizedvoid
unregister
(Object subscriber
) {//typesBySubscriber是一個map集合,
//通過subscriber找到 subscribedTypes (事件類型集合),
Listget
(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進階:源碼及其設計模式 完全解析
【點擊成為源碼大神】
推薦閱讀: