国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

icp備案網(wǎng)站建設(shè)方案書網(wǎng)站收錄一般多久

icp備案網(wǎng)站建設(shè)方案書,網(wǎng)站收錄一般多久,最好的網(wǎng)站建設(shè)哪家好,手機微網(wǎng)站制作系統(tǒng)Google Guava EventBus(事件總線)的使用和源碼的簡單解析 什么是EventBus? 事件總線(EventBus)是一種廣泛用于軟件架構(gòu)中的設(shè)計模式,用于實現(xiàn)解耦和松散耦合的通信機制。它可以幫助組織和管理應(yīng)用程序中不同組件之間的通信&…

Google Guava EventBus(事件總線)的使用和源碼的簡單解析

什么是EventBus?

事件總線(EventBus)是一種廣泛用于軟件架構(gòu)中的設(shè)計模式,用于實現(xiàn)解耦和松散耦合的通信機制。它可以幫助組織和管理應(yīng)用程序中不同組件之間的通信,以提高應(yīng)用程序的可維護性、可擴展性和靈活性。

在事件總線模式中,不同的組件通過訂閱和發(fā)布事件來進行通信。發(fā)布者發(fā)布一個事件,訂閱者可以訂閱該事件并在事件發(fā)生時執(zhí)行相關(guān)的操作。事件總線充當了中介者的角色,它負責(zé)管理所有的事件和訂閱者,并確保事件正確地傳遞到所有訂閱者。

事件總線模式可以在很多場景中使用,例如 Android 應(yīng)用程序中的通信、分布式系統(tǒng)中的消息傳遞等。常見的事件總線實現(xiàn)包括 Google Guava 的 EventBus 和 Square 的 Otto。

總的來說,EventBus就是應(yīng)用了發(fā)布者/訂閱者模式,用來幫助我們進行各組件間通信的工具。我們這里解析的是Google Guava 的 EventBus,它的官方文檔在這里

EventBus三要素

github上的圖:
來自官網(wǎng)的圖片
EventBus里有三個比較重要的概念:

  1. Event 消息事件
  2. Publisher 消息發(fā)布者
  3. Subscriber 消息訂閱者

乍一看這訂閱者/發(fā)布者模式還和觀察者模式有幾分相似之處,不同之處就在于EventBus的存在,它作為一個中間件充當了消息傳遞的助手,使得訂閱者不必直接訂閱發(fā)布者,訂閱事件總線即可。

EventBus的五種線程模型

EventBus的線程模型指的是根據(jù)事件的發(fā)布和訂閱所在的線程,決定應(yīng)該在哪個線程中處理事件。EventBus中有以下四種線程模型:

    1. POSTING(默認):在發(fā)布事件的線程中執(zhí)行,即同步執(zhí)行,速度快。
    1. MAIN:在主線程(UI線程)中執(zhí)行,如果當前線程是主線程,直接執(zhí)行訂閱方法;否則,通過主線程的Poster來執(zhí)行。
    1. BACKGROUND:在后臺線程中執(zhí)行,如果當前線程是主線程,通過后臺的Poster來執(zhí)行;否則,直接執(zhí)行訂閱方法。
    1. ASYNC:在新的子線程中執(zhí)行,每個事件都會創(chuàng)建一個新的子線程,即異步執(zhí)行,速度較慢。

除此之外,還有一種模型:

    1. MAIN_ORDERED :MAIN_ORDERED 模式也是運行在主線程上的模式,不同于 MAIN 模式的是,它保證了事件的順序性。也就是說,當一個事件在主線程中被發(fā)布時,它會先進入一個隊列中,之后再一個個的被處理。這樣可以保證相同事件類型的事件按照發(fā)送的順序依次被執(zhí)行。如果當前線程不是主線程,那么它就直接被執(zhí)行,這也是為了避免在子線程中的事件被阻塞。

我們會在消息處理方法中利用@Subscribe注解指定線程模型。

EventBus的簡單使用

使用EventBus三步走

這里我們根據(jù)github上的三步分為四步走:

  1. 定義Event事件類:
public class MessageEvent {private String message;public MessageEvent(String message){this.message = message;}public String getMessage(){return message;}public void setMessage(String message){this.message = message;}
}

這里定義了一個MessageEvent用于傳遞事件。

  1. 聲明訂閱者,注冊訂閱方法并且指定線程模型
    @Subscribe(threadMode = ThreadMode.MAIN)//選擇線程模型,說明事件將在主線程中處理public void onMoonEvent(MessageEvent messageEvent){tv_message.setText(messageEvent.getMessage());}

這里在MainActivity里注冊了一個訂閱方法(消息處理方法),指定了線程模型為MAIN,說明訂閱方法在主線程里執(zhí)行。完整Demo在后面放出。

3.注冊訂閱者與訂閱默認的事件總線

   bt_subscription.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//MainActivity注冊了這條事件總線if(!EventBus.getDefault().isRegistered(MainActivity.this)){EventBus.getDefault().register(MainActivity.this);}}});

這里先判斷MainActivity是否已經(jīng)與默認的事件總線訂閱,如果沒有訂閱就進行訂閱。

4.發(fā)送消息事件–觸發(fā)訂閱方法

    bt_message.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {EventBus.getDefault().post(new MessageEvent("歡迎來到,德萊聯(lián)盟"));finish();}});

這里我在第二個Activity里發(fā)送了一個消息事件到默認的事件總線中,這樣MainActivity中的訂閱方法就會被觸發(fā)。

2.小Demo

這里我貼出我的小Demo:

  1. MainActivity:
public class MainActivity extends AppCompatActivity {private TextView tv_message;private Button bt_message;private Button bt_subscription;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_message = findViewById(R.id.tv_message);bt_message = findViewById(R.id.bt_message);bt_subscription = findViewById(R.id.bt_subscription);bt_message.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(MainActivity.this,SecondActivity.class));}});bt_subscription.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//MainActivity注冊了這條事件總線if(!EventBus.getDefault().isRegistered(MainActivity.this)){EventBus.getDefault().register(MainActivity.this);}}});}@Overrideprotected void onDestroy() {super.onDestroy();EventBus.getDefault().unregister(this);}//通過參數(shù)類型來區(qū)分應(yīng)該執(zhí)行哪個方法@Subscribe(threadMode = ThreadMode.MAIN)//選擇線程模型,說明事件將在主線程中處理public void onMoonEvent(MessageEvent messageEvent){tv_message.setText(messageEvent.getMessage());}@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)public void ononStickyEvent(MessageEvent messageEvent){tv_message.setText(messageEvent.getMessage());}@Subscribe(threadMode = ThreadMode.MAIN)public void secondBack(String mes){Toast.makeText(this, mes, Toast.LENGTH_SHORT).show();}
}
  1. SecondActivity:
public class SecondActivity extends AppCompatActivity {TextView tv_message;Button bt_message;Button bt_sticky;Button mes;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);tv_message = findViewById(R.id.tv_message1);bt_message = findViewById(R.id.bt_message1);bt_sticky = findViewById(R.id.bt_sticky);mes = findViewById(R.id.mes_event);bt_message.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {EventBus.getDefault().post(new MessageEvent("歡迎來到,德萊聯(lián)盟"));finish();}});bt_sticky.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {EventBus.getDefault().postSticky(new MessageEvent("粘性事件"));finish();}});mes.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {EventBus.getDefault().post(new String("另一個事件"));finish();}});}
}
  1. MessageEvent:
//自定義的Event事件
public class MessageEvent {private String message;public MessageEvent(String message){this.message = message;}public String getMessage(){return message;}public void setMessage(String message){this.message = message;}
}

https://github.com/MonsterTTL/Android-Impove/tree/master/EventBusDemo
具體的實例代碼放在我的github上👆了,里面還涉及到了粘性事件以及多個訂閱事件。

源碼解析

getDefault()方法

先來看我們最常用的獲取EventBus的方法

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

很顯然這里用到了DCL單例模式,確保缺省狀態(tài)下EventBus的實例只有一個。我們順著這個方法往下捋,看new EventBus()方法。

new EventBus()方法

	private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();public EventBus() {this(DEFAULT_BUILDER);}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;}

很顯然,獲取default EventBus的調(diào)用流程是這樣的:getDefault() -> EventBus()->EventBus(DEFAULT_BUILDER)。說道Builder,那么應(yīng)該是采取建造者模式創(chuàng)建的,我們接下來分析EventBusBuilder。

EventBusBuilder

builder里延伸下去的內(nèi)容有點多,我們直接先看EventBusBuilder的build方法:

public EventBus build() {return new EventBus(this);}

很顯然,該類的作用是構(gòu)建一個EventBus實例,并提供一些配置選項。該類具有多個屬性,如是否記錄訂閱者異常、是否發(fā)送沒有訂閱者事件等,以及設(shè)置自定義線程池和日志處理程序等。類中的各種方法可用于配置這些屬性,例如logSubscriberExceptions()方法可用于設(shè)置是否記錄訂閱者異常。 這里我們就不關(guān)注打印日志,記錄異常等配置了,我們關(guān)注具體的創(chuàng)建過程。

我們先來看看以下幾個可選的方法:

/*** 設(shè)置 EventBus 是否開啟事件類繼承的支持。默認情況下,EventBus會考慮事件類繼承關(guān)系,即父類有訂閱者時,子類的訂閱者也會被
通知。但是,關(guān)閉這個特性可以提高事件發(fā)布的速度。如果事件類的繼承層級比較復(fù)雜,關(guān)閉繼承支持的性能提升應(yīng)該會大于20%。但需要注意
的是,事件發(fā)布通常只占應(yīng)用程序內(nèi)部CPU時間的一小部分,除非高速發(fā)布事件,例如每秒鐘發(fā)布數(shù)百/數(shù)千個事件。                                ***/public EventBusBuilder eventInheritance(boolean eventInheritance) {this.eventInheritance = eventInheritance;return this;}/*** 這是用來指定線程池類型的,除了使用Builder內(nèi)部自帶的線程池,我們也可以使用自己傳入的線程池*/public EventBusBuilder executorService(ExecutorService executorService) {this.executorService = executorService;return this;}/*** 這是用來跳過指定訂閱者的方法驗證的,當你注冊一個類作為事件訂閱者時,EventBus會自動檢查該類是否具有合法的事件訂閱方法。* 這些方法必須是公共的,沒有返回值,只有一個參數(shù),并且在方法上需要使用@Subscribe注解。然而,有些情況下,你可能希望讓某* 個類作為訂閱者,但是它并不符合這些要求。例如,它可能有一些不是用@Subscribe注解的方法,但是你仍然希望這些方法能夠被* EventBus識別并調(diào)用。在這種情況下,你可以使用skipMethodVerificationFor()方法來跳過驗證,允許這些方法被注冊為事件訂閱    * 方法。*/public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {if (skipMethodVerificationForClasses == null) {skipMethodVerificationForClasses = new ArrayList<>();}skipMethodVerificationForClasses.add(clazz);return this;}/**  允許在生成了索引的情況下也強制使用反射進行訂閱者的查找。默認情況下,如果有生成的索引,則會使用它來查找訂閱者,而不是使用反射。通過調(diào)用這個方法并將其參數(shù)設(shè)置為 true,可以強制使用反射進行查找。 */public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {this.ignoreGeneratedIndex = ignoreGeneratedIndex;return this;}/** 開啟嚴格方法驗證 . 在 EventBus 中,嚴格檢查是指在注冊和訂閱事件時檢查相關(guān)方法的參數(shù)和返回類型是否正確匹配,以確保事件可以正確地被分發(fā)。如果開啟了嚴格檢查,那么在注冊或訂閱事件時,如果發(fā)現(xiàn)有方法的參數(shù)或返回類型與事件類型不匹配,EventBus 會拋出一個異常,防止程序出現(xiàn)意料之外的錯誤。*/public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {this.strictMethodVerification = strictMethodVerification;return this;}/** 用于添加 SubscriberInfoIndex 對象到 EventBusBuilder 中。SubscriberInfoIndex 接口用于提供訂閱者類和訂閱方法信息的索引,以便 EventBus 能夠快速找到訂閱者及其訂閱方法。在 EventBus 中, */public EventBusBuilder addIndex(SubscriberInfoIndex index) {if (subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();}subscriberInfoIndexes.add(index);return this;}

接下來看下比較重要的幾個方法:

 public EventBus installDefaultEventBus() {synchronized (EventBus.class) {if (EventBus.defaultInstance != null) {throw new EventBusException("Default instance already exists." +" It may be only set once before it's used the first time to ensure consistent behavior.");}EventBus.defaultInstance = build();return EventBus.defaultInstance;}}

這個方法是用來安裝默認EventBus里的defaultInstance的:

class EventBus{
...static volatile EventBus defaultInstance;//默認的實例private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();//默認的Builder
...
}

這段代碼是通過構(gòu)建者模式構(gòu)建一個默認的EventBus實例,并安裝在默認的EventBus上,只能在第一次使用默認的EventBus之前調(diào)用,否則會拋出EventBusException異常。在synchronized塊內(nèi),首先判斷是否已經(jīng)存在默認的EventBus實例,如果存在則拋出異常,否則構(gòu)建EventBus實例并將其設(shè)置為默認實例,最后返回默認實例。

接下來看另一個方法:

    MainThreadSupport getMainThreadSupport() {if (mainThreadSupport != null) {return mainThreadSupport;} else if (AndroidComponents.areAvailable()) {return AndroidComponents.get().defaultMainThreadSupport;} else {return null;}}

這個方法是用來獲取主線程支持的,那什么是主線程支持呢,我們點進去看看:

public interface MainThreadSupport {boolean isMainThread();Poster createPoster(EventBus eventBus);
}

這里我們可以看到,MainThreadSupport是一個接口,定義了isMainThread和createPoster方法,根據(jù)這兩個方法名,我們可以推測這個接口的作用就是用來支持消息在主線程里傳遞的,isMainThread用于判斷當前線程是否是主線程,createPoster用于創(chuàng)建一個poster以在主線程里傳遞消息。

當然這只是一個接口,我們要看具體的實現(xiàn)還是得折回去看getMainThreadSupport方法,我們看這一段:

	else if (AndroidComponents.areAvailable()) {return AndroidComponents.get().defaultMainThreadSupport;}

這段代碼的作用就是判斷當前平臺是不是Android平臺,如果是Android平臺,就調(diào)用Android.get().defaultMainThreadSupport獲取默認的主線程支持類。那這里又得涉及到AndroidComponents類了,我們開一個子標題討論這個類。

AndroidComponents類

AndroidComponents類是一個抽象類,內(nèi)容比較短,這里先貼出它的全部源碼:

public abstract class AndroidComponents {private static final AndroidComponents implementation;static {implementation = AndroidDependenciesDetector.isAndroidSDKAvailable()? AndroidDependenciesDetector.instantiateAndroidComponents(): null;}public static boolean areAvailable() {return implementation != null;}public static AndroidComponents get() {return implementation;}public final Logger logger;public final MainThreadSupport defaultMainThreadSupport;public AndroidComponents(Logger logger, MainThreadSupport defaultMainThreadSupport) {this.logger = logger;this.defaultMainThreadSupport = defaultMainThreadSupport;}
}

這里將其聲明為抽象的意義應(yīng)該就是防止其實例化,因為AndroidComponents的具體實現(xiàn)是由AndroidDependenciesDetector來決定的,這個類會檢測當前是否在Android環(huán)境下,如果是,則返回實際的實現(xiàn),否則返回null。而AndroidDependenciesDetector這個類是不允許被外部直接實例化的,因此AndroidComponents也應(yīng)該遵循同樣的規(guī)則。

真正重要的代碼部分是在靜態(tài)代碼塊內(nèi),代碼塊在初始化時會會檢測當前是否在Android環(huán)境下,如果是,則返回實際的實現(xiàn),否則返回null。

接下來我們再深入向下扒,找到MainThreadSupport的具體實現(xiàn)類,再往下看我們可以發(fā)現(xiàn)這個類是由反射機制實現(xiàn)的,不過我們先不關(guān)心這個,直接找到具體實現(xiàn)類DefaultAndroidMainThreadSupport,直接看它的源碼:

public class DefaultAndroidMainThreadSupport implements MainThreadSupport {@Overridepublic boolean isMainThread() {return Looper.getMainLooper() == Looper.myLooper();}@Overridepublic Poster createPoster(EventBus eventBus) {return new HandlerPoster(eventBus, Looper.getMainLooper(), 10);}
}

可以發(fā)現(xiàn),這個類實際上十分簡單,通過Looper判斷是否是主線程,接下來看他的Poster類。
具體的Poster類:

public class HandlerPoster extends Handler implements Poster {private final PendingPostQueue queue;private final int maxMillisInsideHandleMessage;private final EventBus eventBus;private boolean handlerActive;public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {super(looper);this.eventBus = eventBus;this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;queue = new PendingPostQueue();}public void enqueue(Subscription subscription, Object event) {PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {queue.enqueue(pendingPost);if (!handlerActive) {handlerActive = true;if (!sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message");}}}}
...

我們先來看這前半部分,在構(gòu)造方法中新出現(xiàn)了一個queue,是一個post隊列,是用于存儲post請求的隊列,具體的PendingPostQueue類我們就不在具體看了,只要知道其內(nèi)部使用鏈表的結(jié)構(gòu)連接的,而PendingPostQueue類里記錄了這個post隊列的頭部和尾部。

enqueue方法中將發(fā)送的Post請求進行入隊,如果handler不處于活躍狀態(tài),就將其置位活躍狀態(tài),然后獲取一個Message對象并將其發(fā)送給MessageQueue中,接下來我們看它是如何處理post請求的:

public void handleMessage(Message msg) {boolean rescheduled = false;try {long started = SystemClock.uptimeMillis();while (true) {PendingPost pendingPost = queue.poll();if (pendingPost == null) {synchronized (this) {// Check again, this time in synchronizedpendingPost = queue.poll();if (pendingPost == null) {handlerActive = false;return;}}}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;}}

由于HandlerPoster本身就是一個Handler,所以上一步發(fā)送的Message實際上就是由它自己處理的,就是在這個方法中處理,Post請求具體是在這里執(zhí)行的:

eventBus.invokeSubscriber(pendingPost);

這個方法會將Event事件推送到具體的訂閱者類中去執(zhí)行,接下來我們重新回到EventBus類中看看這個過程是怎樣執(zhí)行的。

Subscription類

在正式介紹EventBus是怎樣執(zhí)行推送之前,我們需要先了解兩個前置類的信息,首先我們先來看Subscriptiont類:

final class Subscription {final Object subscriber;final SubscriberMethod subscriberMethod;/*** Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.*/volatile boolean active;Subscription(Object subscriber, SubscriberMethod subscriberMethod) {this.subscriber = subscriber;this.subscriberMethod = subscriberMethod;active = true;}@Overridepublic boolean equals(Object other) {if (other instanceof Subscription) {Subscription otherSubscription = (Subscription) other;return subscriber == otherSubscription.subscriber&& subscriberMethod.equals(otherSubscription.subscriberMethod);} else {return false;}}@Overridepublic int hashCode() {return subscriber.hashCode() + subscriberMethod.methodString.hashCode();}
}

這個類里面的具體的SubscriberMethod和Object subscriber就不詳細介紹了,根據(jù)名字我們也可以知道,final Object subscriber 存儲的是訂閱事件的對象,而 final SubscriberMethod subscriberMethod 則包含了訂閱事件的方法的信息,例如該方法的名稱、參數(shù)類型等等。通過這兩個屬性,EventBus 可以將事件正確地分發(fā)給訂閱者的對應(yīng)方法。

PendingPost類

然后我們再來看PendingPost類

final class PendingPost {private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();Object event;Subscription subscription;PendingPost next;...}

我們看到這個類里有一個靜態(tài)的變量:

private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();

就是說,所有的PendingPost類的實例都會共享這一個池子,這個池子主要是用來優(yōu)化GC性能的,用于重用已創(chuàng)建的對象,避免了大量的對象創(chuàng)建和銷毀,減小了內(nèi)存開銷。

1.構(gòu)造方法

    private PendingPost(Object event, Subscription subscription) {this.event = event;this.subscription = subscription;}

其創(chuàng)建方法主要是傳入發(fā)送的事件和訂閱信息相關(guān)的Subscription,然后將其包裝成一個PendingPost對象。

static PendingPost obtainPendingPost(Subscription subscription, Object event) {synchronized (pendingPostPool) {int size = pendingPostPool.size();if (size > 0) {PendingPost pendingPost = pendingPostPool.remove(size - 1);pendingPost.event = event;pendingPost.subscription = subscription;pendingPost.next = null;return pendingPost;}}return new PendingPost(event, subscription);}

obtainPendingPost方法是根據(jù)pendingPostPool這個共享池來創(chuàng)建PendingPost對象,如果共享池里還有可用對象,就復(fù)用這個對象來創(chuàng)建新的PendingPost對象,否則就直接創(chuàng)建一個新的對象。

 static void releasePendingPost(PendingPost pendingPost) {pendingPost.event = null;pendingPost.subscription = null;pendingPost.next = null;synchronized (pendingPostPool) {// Don't let the pool grow indefinitelyif (pendingPostPool.size() < 10000) {pendingPostPool.add(pendingPost);}}}

最后是release方法,這個方法是用來將PendingPost對象釋放會共享池中的,不過這個共享池的大小也是有限制的,最大不能超過10000。

EventBus類中如何推送事件

上面說到,具體是通過invokeSubscriber這個方法來推送事件的,事不宜遲,我們馬上來看看這個方法的內(nèi)容:

    void invokeSubscriber(PendingPost pendingPost) {Object event = pendingPost.event;Subscription subscription = pendingPost.subscription;PendingPost.releasePendingPost(pendingPost);if (subscription.active) {invokeSubscriber(subscription, event);}}

可以發(fā)現(xiàn),具體又調(diào)用了invokeSubscriber(subscription, event);方法,我們緊接著看invokeSubscriber(subscription, event)方法:

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

很顯然,這里是通過了反射機制,subscription.subscriberMethod.method.invoke(subscription.subscriber, event)表示將event事件發(fā)送到subscriber中并觸發(fā)subscriber的處理方法。method是SubscriberMethod中的一個字段,表示訂閱方法本身,通過invoke方法來調(diào)用這個方法,并將subscriber作為方法的調(diào)用者,event作為參數(shù)傳遞進去。到這里,我們就看完Event事件是如何傳遞給訂閱者的了。

Register(訂閱事件總線)

接下來我們看第二個內(nèi)容,訂閱事件總線時做了什么:

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

核心方法在于subscribe方法:

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {Class<?> eventType = subscriberMethod.eventType;Subscription newSubscription = new Subscription(subscriber, subscriberMethod);CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);//獲取同一個類型的消息事件下的所有Subscrption對象if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<>();subscriptionsByEventType.put(eventType, subscriptions);//如果原來還沒有注冊這一類型的消息事件,則新創(chuàng)建一個List并加入} else {if (subscriptions.contains(newSubscription)) {//如果當前注冊的對象已經(jīng)注冊過了,就拋出異常throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType);}}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;}}//這一段是用來將新的subscription對象插入到同一類型的消息事件隊列的合適位置中--將訂閱者與注冊方法關(guān)聯(lián)起來List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<>();typesBySubscriber.put(subscriber, subscribedEvents);}subscribedEvents.add(eventType);//這一段是用來將訂閱者與消息事件類型關(guān)聯(lián)起來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);}}}

具體來說,subscribe方法做了三件事情:
1. 維護了當前EventBus中的subscriptionsByEventType表;
2. 維護了當前EventBus中的 typesBySubscriber表;
3. 處理了粘性事件;

詳細的說明我放在上面的注釋里面了,我們先說維護的兩個表,第一個表是代表消息事件類型和Subscription關(guān)聯(lián)的一個表,
第二個表是表示訂閱者Subscriber和消息事件類型關(guān)聯(lián)的兩個表,這兩個表可以在發(fā)布事件時可以快速地找到對應(yīng)的訂閱者并調(diào)用其處理方法。

然后是粘性事件的處理,這里有一個標志位eventInheritance,這個標志位我們在之前提到過,我們可以回到EventBuilder的內(nèi)容查看,主要是涉及到繼承問題的,除去繼承的問題,粘性事件主要就是調(diào)用:

   checkPostStickyEventToSubscription(newSubscription, stickyEvent);

直接點開這個方法會發(fā)現(xiàn)其調(diào)用了:

    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {if (stickyEvent != null) {// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)// --> Strange corner case, which we don't take care of here.postToSubscription(newSubscription, stickyEvent, isMainThread());}}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 MAIN_ORDERED:if (mainThreadPoster != null) {mainThreadPoster.enqueue(subscription, event);} else {// temporary: technically not correct as poster not decoupled from subscriberinvokeSubscriber(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);}}

實際上是調(diào)用了postToSubscription方法,該方法根據(jù)回調(diào)方法指定的線程模型來傳遞粘性事件,至于上面提到的三種poster,我們之后再解析。

Post方法

訂閱者注冊完畢后,我們就要用Post方法進行消息的發(fā)送了,Post方法又可以分為post和postSticky,接下來我們先看post方法:

    public void post(Object event) {PostingThreadState postingState = currentPostingThreadState.get();List<Object> eventQueue = postingState.eventQueue;//獲取發(fā)送線程的eventQueueeventQueue.add(event);//將當前要發(fā)送時間添加進eventQueue中if (!postingState.isPosting) {//如果當前線程處于空閑狀態(tài)postingState.isMainThread = isMainThread();postingState.isPosting = true;if (postingState.canceled) {throw new EventBusException("Internal error. Abort state was not reset");}try {while (!eventQueue.isEmpty()) {//eventQueue不為空postSingleEvent(eventQueue.remove(0), postingState);//通過postSingleEvent方法依次將eventQueue中的事件發(fā)送}} finally {postingState.isPosting = false;postingState.isMainThread = false;}}}

這里又冒出來了一個PostingThreadState,我們看它是從哪里來的:

    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {@Overrideprotected PostingThreadState initialValue() {return new PostingThreadState();}};
...final static class PostingThreadState {final List<Object> eventQueue = new ArrayList<>();boolean isPosting;boolean isMainThread;Subscription subscription;Object event;boolean canceled;}

這里涉及到一個ThreadLocal類,我們先來看看:

在Java中,ThreadLocal是一個非常有用的類,它允許您在單個線程中存儲和檢索數(shù)據(jù),而不會與其他線程共享。ThreadLocal通過創(chuàng)建一個副本來解決線程安全問題,每個線程都有自己的副本,所以每個線程都可以獨立地改變自己的副本,而不會影響其他線程。
ThreadLocal提供了三個主要方法:
get() - 返回當前線程的變量副本(如果有);如果沒有,則返回默認值。
set(T value) - 設(shè)置當前線程的變量副本。
remove() - 移除當前線程的變量副本。
ThreadLocal通常用于在單個線程中存儲和檢索上下文信息,例如數(shù)據(jù)庫連接、事務(wù)對象等。使用ThreadLocal可以避免在多線程環(huán)境下對這些資源進行同步操作,從而提高性能。

所以在這里的語境下,PostingThreadState就是每個發(fā)送post請求線程中的局部變量,且各個發(fā)送線程之間是不會共享這個PostingThreadState變量的,每個發(fā)送post請求的線程都會有自己私有的PostingThreadState變量。

post方法做的大致如下:

  1. 先獲取到當前發(fā)送線程的PostingThreadState變量

  2. 接著獲取當前線程要發(fā)送的事件隊列eventQueue,然后將當前要發(fā)送的事件添加進入eventQueue隊列中

  3. 如果當前發(fā)送線程處于空閑狀態(tài),就將eventQueue中的事件通過postSingleEvent方法依次發(fā)送

    既然說調(diào)用了postSingleEvent方法,接下來我們理所當然地查看postSingleEvent方法:

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();boolean subscriptionFound = false;if (eventInheritance) {List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);int countTypes = eventTypes.size();for (int h = 0; h < countTypes; h++) {Class<?> clazz = eventTypes.get(h);subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);}} else {subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);}if (!subscriptionFound) {if (logNoSubscriberMessages) {logger.log(Level.FINE, "No subscribers registered for event " + eventClass);}if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&eventClass != SubscriberExceptionEvent.class) {post(new NoSubscriberEvent(this, event));}}}

這里又涉及到了eventInheritance標志位,這里我們不管這個標志位,查看這個方法的核心在于:

 subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);

我們接著查看這個postSingleEventForEventType方法:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {subscriptions = subscriptionsByEventType.get(eventClass);}if (subscriptions != null && !subscriptions.isEmpty()) {for (Subscription subscription : subscriptions) {postingState.event = event;postingState.subscription = subscription;boolean aborted;try {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;}

這個方法接收了發(fā)送的事件event,發(fā)送線程的局部變量postingState,還有事件類型eventClass。接下來它獲取了對應(yīng)事件類型下的subscriptions隊列,對其中的每個subscription對象調(diào)用了postToSubscription方法,如果一切成功,就返回true,否則返回false。至于postToSubscription這個方法我們在之前也提到過,就是:

 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) 

上次提到它是用來傳遞粘性事件,當然它也可以用來傳遞普通事件,這個方法將根據(jù)指定的線程模型來選擇如何傳遞事件給訂閱者回調(diào)處理。

至于粘性事件的POST:

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

不同點就在于在執(zhí)行post方法之前還將其加入到了粘性事件隊列stickyEvents,以便在其他訂閱者完成注冊時能接收到粘性事件。

三種Poster

之前提到過EventBus里有三種Poster,在MainThreadSuppot中我們已經(jīng)認識了HandlerPoster,接下來我們看看其他兩種Poster,這里我們?yōu)榱朔奖?#xff0c;貼出postToSubscription的源碼方便理解:

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 MAIN_ORDERED:if (mainThreadPoster != null) {mainThreadPoster.enqueue(subscription, event);} else {// temporary: technically not correct as poster not decoupled from subscriberinvokeSubscriber(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);}}

很顯然,根據(jù)不同的線程模型,該方法會采取的不同的策略和poster,mainThreadPoster就是我們一開始解析過的HandlerPoster,接下來先看backgroundPoster:

	private final PendingPostQueue queue;private final EventBus eventBus;private volatile boolean executorRunning;BackgroundPoster(EventBus eventBus) {this.eventBus = eventBus;queue = new PendingPostQueue();}

BackgroundPoster內(nèi)部會有自己的一條PendingPostQueue,接下來我們看入隊方法:

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

這里BackgroundPoster用了同步代碼塊,說明雖然Background模型是在子線程中運行的,但是其會遵循順序依次執(zhí)行,同一個發(fā)送隊列發(fā)送的事件觸發(fā)的多個Background線程模型的回調(diào)方法不能同時運行。

至于任務(wù)該如何執(zhí)行,eventBus中會有一個線程池,這個線程池類型是根據(jù)builder決定的,默認情況下,這個線程池的類型將會是CachedThreadPool。接下來再看它的run方法:

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

run方法很簡單,就是不斷將queue中的PendingPost取出放入線程池中執(zhí)行eventBus的invokeSubscriber方法:
在這里插入圖片描述
接下來我們看AsncPoster:

AsyncPoster(EventBus eventBus) {this.eventBus = eventBus;queue = new PendingPostQueue();}public void enqueue(Subscription subscription, Object event) {PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);queue.enqueue(pendingPost);eventBus.getExecutorService().execute(this);}@Overridepublic void run() {PendingPost pendingPost = queue.poll();if(pendingPost == null) {throw new IllegalStateException("No pending post available");}eventBus.invokeSubscriber(pendingPost);}

AsyncPoster和BackgroundPoster其實有點像,不同之處就在于AsyncPoster的post請求不必順序執(zhí)行,多個回調(diào)可以同時進行。

總結(jié)

到此為止,我們就已經(jīng)解析了EventBus源碼的核心基礎(chǔ)部分了,讓我們總結(jié)一下流程圖:在這里插入圖片描述
這里的流程圖是簡化的版本,這里具體實現(xiàn)的細節(jié)被隱藏起來了,主要是幫助我們理解機制流程。

http://aloenet.com.cn/news/30973.html

相關(guān)文章:

  • 阿克蘇建設(shè)租房信息阿克蘇租房網(wǎng)站磁力搜索器
  • 破解php網(wǎng)站后臺賬號密碼朋友圈廣告推廣文字
  • 寧波商城網(wǎng)站建設(shè)淘寶指數(shù)查詢官網(wǎng)
  • 網(wǎng)站訪問量大 處理小程序如何推廣運營
  • 免費開店鋪網(wǎng)站關(guān)鍵字優(yōu)化價格
  • 織夢后臺做的網(wǎng)站怎么綁定域名湖南網(wǎng)站建設(shè)營銷推廣
  • 做網(wǎng)站字體格式用銳利嗎即刻搜索引擎入口
  • 成都游戲網(wǎng)站開發(fā)代發(fā)關(guān)鍵詞排名包收錄
  • 做網(wǎng)站 流量怎么抓錢網(wǎng)推廣公司
  • 無錫建設(shè)網(wǎng)站的公司湖南百度seo
  • 無錫兼職做網(wǎng)站電商培訓(xùn)內(nèi)容
  • 徐州提供網(wǎng)站建設(shè)報價表寧波seo網(wǎng)絡(luò)推廣優(yōu)化價格
  • 動態(tài)網(wǎng)站開發(fā)平臺簡介什么叫seo
  • 購物網(wǎng)站策劃案廈門谷歌seo公司
  • 北京網(wǎng)站建設(shè)的價格中國最好的營銷策劃公司
  • 做班級的活動的網(wǎng)站企業(yè)營銷策劃方案范文
  • 招聘H5在什么網(wǎng)站做最好搜索引擎排名
  • 用手機什么軟件做網(wǎng)站百度推廣怎么操作流程
  • 帶登錄網(wǎng)站模板網(wǎng)站建設(shè)的整體流程有哪些
  • 阿里云Windows網(wǎng)站建設(shè)廣東百度推廣的代理商
  • 自助建站系統(tǒng)免授權(quán)版企業(yè)查詢網(wǎng)
  • 網(wǎng)站開發(fā)專業(yè)就業(yè)培訓(xùn)學(xué)校石家莊網(wǎng)絡(luò)營銷網(wǎng)站推廣
  • 怎么免費做個人網(wǎng)站互聯(lián)網(wǎng)營銷策略有哪些
  • 網(wǎng)站怎么做?企業(yè)培訓(xùn)的目的和意義
  • 一鍵清理加速北京網(wǎng)站優(yōu)化推廣方案
  • 做網(wǎng)站代理拉不到人常州網(wǎng)站推廣公司
  • 動態(tài)網(wǎng)站設(shè)計主題長春seo培訓(xùn)
  • 金融跟單公司網(wǎng)站建設(shè)seo課程培訓(xùn)中心
  • 網(wǎng)站推廣怎么做2017注冊域名查詢網(wǎng)站官網(wǎng)
  • 做直播的在相親網(wǎng)站交友韓國搜索引擎排名