blog

EventBusソースコード解析

4.5 ASYNC : 非同期スレッドで実行されます。どのような場合でも、サブスクリプションメソッドを実行するスレッドは、ポスト送信者のスレッドとは異なり、メインスレッドではない別のスレッドですが、非...

Aug 10, 2020 · 17 min. read
シェア

まとめ

  1. サブスクリプション・メソッドが有効になる前に、register インターフェースを通してサブスクライバーとして登録し、メッセージを受け取らなくなったら unregister を呼び出して登録を解除し、メモリ・リークを回避する必要があります;

  2. EventBus インタフェースからメッセージを受信する唯一の方法は、@Subscribe アノテーションを付けてサブスクライバ・メソッドとして宣言することです。

  3. Subscribeアノテーションは、3つのプロパティ・フィールドを提供します:

    3.1 threadMode: サブスクリプションメソッドが許可されるスレッドを指定します;

    3.2 sticky: イベントがスティッキーかどうかを示します;

    3.3 priority: 購読方法の優先順位を指定します。優先順位が大きいほど、優先順位は高くなります;

  4. threadMode は現在サポートされています:

    4.1 POSTING : イベント送信者と同じスレッドで実行され、実行はブロックされます;

    4.2 MAIN :メインスレッドで実行し、現在の送信メソッドのメインスレッドは、すぐにブロックされ、実行された場合、それ以外の場合は、メインスレッドハンドラの実行を待ってキューに入ります;

    4.3 MAIN_ORDERED : メインスレッドで実行され、MAIN_ORDEREDとmainの違いは、MAIN_ORDEREDは実行のためにキューに入れられ、送信者が現在メインスレッドにいる場合は現在のスレッドをブロックしませんが、MAINは現在メインスレッドにいる場合はサブスクリプションメソッドが実行されるまでメインスレッドをブロックします;

    4.4 BACKGROUND : バックグラウンドスレッドで実行され、現在の投稿送信者がメインスレッドの場合はバックグラウンドスレッドのキューに入り、現在の投稿送信者がバックグラウンドスレッドの場合はスレッドを直接ブロックして実行します;

    4.5 ASYNC : 非同期スレッドで実行され、どのような場合でもサブスクリプションメソッドを実行するために別のスレッドを起動します。投稿の送信者がいるスレッドとは異なり、BACKGROUNDは非メインスレッドですが、非同期は常に別のスレッドで実行されます。

  5. 送信されたスティッキー・イベントはデフォルトでキャッシュされ、同じタイプのイベントでは現在の最新のものだけがキャッシュされます;

  6. ベタなイベントと普通のイベントの違い:

    6.1 通常のイベント 通常のイベントが送られたとき、それは同じイベントタイプで登録に 成功したサブスクライバーだけが受け取ることができます。イベントが送られた後はキャッシュされないので、イベントが送られた後に 登録された同じタイプのサブスクライバーメソッドはイベントを受け取ることが できない;

    6.2 スティッキーイベント: スティッキーイベントを送信するとき、同じイベントタイプを登録したサブスクライバーメソッドは、イベントを受信することができます。つまり、送信の終了後、同じイベント・タイプのスティッキー・サブスクライバ・メソッドを登録すると、すぐに最新のスティッキー・イベントを再び受信します;

  7. そうでなければ、最後のスティッキーイベントは常にキャッシュされ、同じイベントタイプでスティッキーサブスクライバメソッドを再登録するたびに再度実行されます;

  8. メッセージ・イベント・クラス A を送信した後、そのイベント・タイプ A のサブスクリプション・メソッドを受信すること、A の親クラスのサブスクリプション・メソッドを受信すること、A が実装するインタフェースのサブスクリプション・メソッドを受信すること、などが実行のトリガーとなります;

  9. サブクラスは親クラスからサブスクライバー・メソッドを継承します。

  10. サブクラスと親クラスを同時に登録することはできません。登録しないと例外がスローされます。

ソースコード解析

EventBusのソースコードは、最も重要なキャッシュに使用されるいくつかのコレクションであり、その左右を理解する基本的にアイデアの一般的な実装を理解しています:

public class EventBus {
 
 /**
 * イベント・タイプの継承システムと実装されたインターフェースのリストをキャッシュし、毎回重複した解析と検索を避ける。
 *
 * key : 送られるイベントはクラスに対応する。 post("123")重要なのはString.class
 * value : イベントクラス、イベントが実装するインターフェース、イベントの親クラス、親クラスが実装するインターフェース、親クラスの親クラス......この継承とインターフェイス実装の関係を持つクラスの集まりは、以下のようになる。
 *
 * イベントを送信するとき、イベントタイプがその親クラスまたはインターフェイスであるサブスクライバー・メソッドも、例えば、トリガーすることができる:
 *
 *  A.class , B extends A , B implements C 
 * @Subscribe()
 * public void subsA(A a) {}
 * @Subscribe()
 * public void subsC(C c) {}
 * @Subscribe()
 * public void subsB(B b) {}
 *
 * そして、Postが実行されると、上記の3つのサブスクライバーメソッドすべてが実行される。
 */
 private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
 /**
 * サブスクライブされたメソッドの受信メッセージ入力を保存するclass --> 対応するSubscriptionSubscriptionオブジェクトのリスト
 *
 *
 * 役割.class そして、サブスクリプションオブジェクトのリストを取り出し、その中のサブスクライバーメソッドを1つずつ実行することができる。
 */
 private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
 /**
 * サブスクライバーを保存する -> 所有するサブスクリプション・メソッドの受信クラス一覧
 *
 *  : 渡されたオブジェクトに対してregisterを呼び出す。 FirstActivity.this
 * サブスクリプション・メソッドのクラス入力のリスト:
 * FirstActivityに2つのサブスクライバー・メソッドがあるとする。
 * @Subscribe
 * public void test(String msg)
 * @Subscribe
 * public void test(int type)
 * 次に、対応するリスト[String.class, Integer.class]
 *
 * 効果:次回、unregisterで渡されたオブジェクトにしたがって、次のようになる。 FirstActivity.thisそうすると、リスト [String.class, Integer.class]
 * そして、サブスクリプションByEventTypeをString.class   Integer.class対応するサブスクリプション・メソッド・オブジェクトのリスト
 * サブスクリプションオブジェクトを一貫して繰り返し、それからFirstActivity.thisすべてのサブスクライブされたメソッドには
 */
 private final Map<Object, List<Class<?>>> typesBySubscriber;
 /**
 * スティッキーイベントのキャッシュは、例えばpostSticky("123")
 *  String.class --> "123" つまり、各イベント・タイプについて、最新のスティッキー・イベントのみがキャッシュされる。
 */
 private final Map<Class<?>, Object> stickyEvents; 
 
}

登録関連 register vs.unregister

register

登録が必要なオブジェクトを渡すと、eventBus はそのオブジェクトに存在するサブスクライバ・メソッドを解析し、そのオブジェクトとの関係を確立します:

 //subscriber :次のようなサブスクライバーがある。FirstActivity.this
 public void register(Object subscriber) {
 //サブスクライバのタイプを取得する。FirstActivity.class
 Class<?> subscriberClass = subscriber.getClass(); 
 //サブスクライバーの資格を取得する@Subscribeラベル付きサブスクライバメソッドのリスト
 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
 
 //このサイドトラバーサルの結果は、サブスクリプションの優先順位に従ってsubscriptionsByEventTypeリストに解析され、キャッシュされる。
 //そして、スティッキー・サブスクリプション・メソッドかどうかを判断し、スティッキー・サブスクリプション・メソッドであり、合致するスティッキー・イベントがあれば、その処理をトリガーする。
 synchronized (this) {
 for (SubscriberMethod subscriberMethod : subscriberMethods) {
 subscribe(subscriber, subscriberMethod);
 }
 }
 }
findSubscriberMethods

サブスクライバの @Subscribe アノテーションに一致する、サブスクライバ内のサブスクライバ・メソッドのリストを取得します:

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
 //このクラスの関連するサブスクリプションメソッドが解析されたかどうかを判断し、解析された場合はキャッシュから読み出す。
 // 例えば、これによりFirstActivityに入るたびにレジスタを再パースする必要がなくなる。
 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
 if (subscriberMethods != null) {
 return subscriberMethods;
 }
 //キャッシュなし、re-parse、ignoreGeneratedIndexのデフォルトは以下である。false
 //この属性は、アノテーターが生成したMyEventBusIndex
 if (ignoreGeneratedIndex) {
 subscriberMethods = findUsingReflection(subscriberClass);
 } else {
 //サブスクライバメソッドのリストを取得するための構文解析
 subscriberMethods = findUsingInfo(subscriberClass);
 }
 if (subscriberMethods.isEmpty()) {
 //サブスクライバを登録するために register を呼び出すと、適格なサブスクライバ・メソッドが存在しない場合、例外がスローされる。
 throw new EventBusException("Subscriber " + subscriberClass
 + " and its super classes have no public methods with the @Subscribe annotation");
 } else {
 //解析されたサブスクライバクラスに対応するサブスクライバメソッドのリストをキャッシュする。 FirstActivity - List<subscriberMethod>
 METHOD_CACHE.put(subscriberClass, subscriberMethods);
 return subscriberMethods;
 }
 }
findUsingInfo

実際にサブスクライブされたメソッドのリストをパースして取得する実行ステップ

 //subscriberClass:サブスクライバに対応するクラス、例えばFirstActivity.class
 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
 FindState findState = prepareFindState();
 findState.initForSubscriber(subscriberClass);
 //  findState.clazz = subscriberClass
 while (findState.clazz != null) {
 //サブスクライバーの情報を取得する
 findState.subscriberInfo = getSubscriberInfo(findState);
 //最初はnull
 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);
 }
 //親クラス内のサブスクライバ・メソッドの再帰的検索は、デフォルトで実行される
 //そのため、親クラスのサブスクライバー・メソッドも継承され、有効になる。
 findState.moveToSuperclass();
 }
 //解析されたサブスクライバ・メソッドのリストを返し、リソースを解放する。
 return getMethodsAndRelease(findState);
 }
findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) {
 Method[] methods;
 try {
 //サブスクライバ・クラスのすべてのメソッドを取得する
 methods = findState.clazz.getDeclaredMethods();
 } catch (Throwable th) {
 // Workaround for java.lang.NoClassDefFoundError, see https://.////49
 try {
 methods = findState.clazz.getMethods();
 } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
 String msg = "Could not inspect methods of " + findState.clazz.getName();
 if (ignoreGeneratedIndex) {
 msg += ". Please consider using EventBus annotation processor to avoid reflection.";
 } else {
 msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
 }
 throw new EventBusException(msg, error);
 }
 findState.skipSuperClasses = true;
 }
 //取得したメソッドを繰り返し実行する
 for (Method method : methods) {
 int modifiers = method.getModifiers();
 //methodサブスクライバーメソッドの最初の制限である、static、abstract、ブリッジメソッドは使用できない。
 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
 //メソッド入力のリストに対応するクラス配列を、以下の順序で取得する。
 Class<?>[] parameterTypes = method.getParameterTypes();
 //サブスクライバー・メソッドの2つ目の制限は、パラメータの数が1つに制限されていることである。
 if (parameterTypes.length == 1) {
 //を取得する。@Subscribeアノテーションの3つ目の制限事項であるサブスクライバー・メソッドは、必ず@Subscribeアノテーション宣言
 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
 if (subscribeAnnotation != null) {
 //受信するパラメーターに対応するクラス・クラスを取得する。String.class
 Class<?> eventType = parameterTypes[0];
 if (findState.checkAdd(method, eventType)) {
 //サブスクライバのメソッドが実行されているスレッドを指定するために、アノテーションで threadMode の値を取得する。
 ThreadMode threadMode = subscribeAnnotation.threadMode();
 //サブスクライバ・メソッドをこのリストに格納する; SubscriberMethodサブスクライバ・メソッドに対応するメソッドを構築し、参照クラスに、アノテーションで3つのパラメータを指定する。
 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
 subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
 }
 }
 } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
 String methodName = method.getDeclaringClass().getName() + "." + method.getName();
 throw new EventBusException("@Subscribe method " + methodName +
 "must have exactly 1 parameter but has " + parameterTypes.length);
 }
 } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
 String methodName = method.getDeclaringClass().getName() + "." + method.getName();
 throw new EventBusException(methodName +
 " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
 }
 }
}

subscribe

このトラバースの結果は解析され、優先順位に従ってsubscriptionsByEventTypeリストにキャッシュされ、スティッキー・サブスクリプション・メソッドであるかどうかを判断し、スティッキー・サブスクリプション・メソッドであり、マッチするスティッキー・イベントがある場合に処理をトリガーします。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
 Class<?> eventType = subscriberMethod.eventType; //method例えば、入力パラメーターであるString.class
 //サブスクリプションオブジェクトは、サブスクライバーオブジェクトとサブスクリプションメソッドオブジェクトを保持する。 , FirstActivity.this   SubscriberMethod
 Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
 /***
 * CopyOnWriteArrayListスレッドセーフだ。List
 *
 * 入力パラメータ型の購読リストを取得し、投稿時に、メッセージのパラメータに従って直接メッセージを送信できるようにする。.class
 * 実行する必要のあるサブスクライバー・メソッドが揃ったので、それらを1つずつ呼び出す。
 * ***/
 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
 if (subscriptions == null) {
 subscriptions = new CopyOnWriteArrayList<>();
 subscriptionsByEventType.put(eventType, subscriptions);
 } else {
 //このサブスクライバ・メソッドが既に追加されている場合、それを繰り返すと例外がスローされる;
 //1.registerを2回呼び出すと、このような現象が起こる;
 //2.親クラスがregisterを呼び出し、子クラスがregisterを呼び出すことで、子クラスでもこの現象が発生する;
 if (subscriptions.contains(newSubscription)) {
 throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
 + eventType);
 }
 }
 // @Subscribeパラメータ priority で指定された優先順位は、サブスクリプションオブジェクトに格納される。
 int size = subscriptions.size();
 for (int i = 0; i <= size; i++) {
 //priority大きいほど優先順位が高く、早く配置され、早く呼び出される
 if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
 subscriptions.add(i, newSubscription);
 break;
 }
 }
 //このサブスクライバ・オブジェクトをキャッシュする。FirstActivity.thisサブスクリプション・メソッド・エントリ・クラスのリスト
 //Role can typesBySubscriberプロパティ・アノテーション
 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
 if (subscribedEvents == null) {
 //TODO ArrayList不適切な点がある。Class
 //しかし、一般的にサブスクライバは、同じ入力パラメータ・タイプを持つサブスクライバ・メソッドを持たない。
 subscribedEvents = new ArrayList<>();
 typesBySubscriber.put(subscriber, subscribedEvents);
 }
 subscribedEvents.add(eventType);
 //イベント・タイプが一致するスティッキー・イベントを処理すると
 //スティッキーイベントが実行された場合、キャッシュからクリアされない、つまり、次に再び登録されたとき、または関連するイベントタイプの定義されたメソッドが登録されたときに、再び実行されることに注意すること。
 //なぜなら、結局のところ、フレームワークはスティッキーイベントをどれだけ実行する必要があるか正確に知らないからである。
 if (subscriberMethod.sticky) {
 if (eventInheritance) {// 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();
 //eventTypecandidateEventTypeの親である場合、または2つが同じ型である場合は、以下のように返される。true
 if (eventType.isAssignableFrom(candidateEventType)) {
 Object stickyEvent = entry.getValue();
 checkPostStickyEventToSubscription(newSubscription, stickyEvent);
 }
 }
 } else {
 Object stickyEvent = stickyEvents.get(eventType);
 checkPostStickyEventToSubscription(newSubscription, stickyEvent);
 }
 }
 }

unregister

例えば、String.classとMan.classの2つのタイプがあります。

次に、subscriptionsByEventType からそれぞれ String.class と Man.class に対応するすべてのサブスクライバ・メソッドのリストを取得し、それに関連付けられたサブスクリプション・オブジェクトがサブスクライバであるかどうかを判断し、サブスクライバであればそれを削除します。


 /**
 * 登録をキャンセルし、登録されているメソッドのリストを消去する。
 * オブジェクトを常にキャッシュして、メモリー・リークにつながるのを避ける。
 *
 * @param subscriber
 */
 public synchronized void unregister(Object subscriber) {
 //オブジェクトを取得する。FirstActivity.this所有するサブスクリプション・メソッド・メッセージ・エントリー・クラス一覧
 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
 if (subscribedTypes != null) {
 //クラスリストを反復する
 for (Class<?> eventType : subscribedTypes) {
 //サブスクライバ・オブジェクトを渡すことはclass
 unsubscribeByEventType(subscriber, eventType);
 }
 //このオブジェクトのキャッシュを削除するには、リスト
 typesBySubscriber.remove(subscriber);
 } else {
 logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
 }
 }
unsubscribeByEventType

eventType に対応するイベント・リストのサブスクライバ・メソッドを決定し、それに関連付けられ ているサブスクライバ・オブジェクトが subscriber である場合、それを削除します;

 //subscriberのように、渡されたオブジェクトに対してunregisterをコールする。FirstActivity.this
 
 private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
 //エントリークラスに対応するサブスクライブされたメソッドオブジェクトのリストを取得する。
 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
 if (subscriptions != null) {
 int size = subscriptions.size();
 //リスト走査削除の実装
 for (int i = 0; i < size; i++) {
 Subscription subscription = subscriptions.get(i);
 //このサブスクライバ・メソッドがこのサブスクライバによって所有されている場合、remove を実行する。
 if (subscription.subscriber == subscriber) {
 subscription.active = false;
 subscriptions.remove(i);
 i--;
 size--;
 }
 }
 }
 }

メッセージの送信: post または postSticky

post

指定されたイベントをメッセージ・イベント・バスに送信します。

 public void post(Object event) {
 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 {
 //FIFO、イベントを順番に処理する
 while (!eventQueue.isEmpty()) {
 postSingleEvent(eventQueue.remove(0), postingState);
 }
 } finally {
 postingState.isPosting = false;
 postingState.isMainThread = false;
 }
 }
 }
postSingleEvent
 /**
 * イベントを送信する
 *
 * @param event 送信されるイベント,EventBus.getDefault().post(event);
 * @param postingState
 * @throws Error
 */
 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
 //event送信するイベント・オブジェクトは、以下のようにイベント・クラス・タイプを取得する。String.class
 Class<?> eventClass = event.getClass();
 boolean subscriptionFound = false;
 // true
 if (eventInheritance) {
 //イベント・クラス・タイプの関連する親クラス、親クラスによって実装されたインターフェース、親クラスによって実装されたインターフェースを再帰的に取得する。
 //つまり、親クラスのサブスクリプション・メソッドと実装されたインターフェイスを呼び出すことができる。
 //呼び出し順は、イベントclass >   >   > 親クラスのインターフェース > 親クラスの親 > 親クラスの親インターフェース.....
 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));
 }
 }
 }
postSingleEventForEventType
 /**
 * イベントの送信
 *
 * @param event イベント・オブジェクト、例えばpost("123") "123"
 * @param postingState
 * @param eventClass イベントタイプ "123" String.class そのメソッドがスティッキーメソッドでない場合、呼び出される場所によって、親クラスから呼び出されることもあれば、実装されたインターフェースから呼び出されることもある。
 * @return
 */
 private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
 CopyOnWriteArrayList<Subscription> subscriptions;
 synchronized (this) {
 //このイベント・タイプに対応するサブスクライブされたメソッド・オブジェクトのリストを取得する
 subscriptions = subscriptionsByEventType.get(eventClass);
 }
 if (subscriptions != null && !subscriptions.isEmpty()) {
 //Subscription各サブスクライバ・メソッドをサブスクライバ・オブジェクトに格納する
 for (Subscription subscription : subscriptions) {
 postingState.event = event;
 postingState.subscription = subscription;
 boolean aborted;
 try {
 //サブスクリプションメソッドにイベントを渡す@Subscribe
 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;
 }
postToSubscription

指定されたスレッドタイプに基づいてスケジューリング実行を開始します。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
 // @Subscribe内部のthreadModeで指定されたスレッドが処理を行う。
 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://メインスレッドで実行する
 //mainとの違いはMAIN_ORDEREDそれらはすべて実行のためにキューに入れられ、送信者が現在メイン・スレッドであれば、現在のスレッドをブロックすることはない。
 //mainは、サブスクリプション・メソッドが実行されるまで、現在メイン・スレッドであればメイン・スレッドをブロックする。
 if (mainThreadPoster != null) {
 mainThreadPoster.enqueue(subscription, event);
 } else {
 // temporary: technically not correct as poster not decoupled from subscriber
 invokeSubscriber(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);
 }
 }

postSticky

同じイベントタイプクラスのスティッキーイベントを3回送信した後、スティッキーメソッドを登録すると、3回すべてではなく、最後の最新のスティッキーイベントだけが受信されます。

登録された同じイベント・タイプのメソッドは、スティッキー・メソッドであろうとなかろうと、受信して実行することができます。

 private final Map<Class<?>, Object> stickyEvents = new ConcurrentHashMap<>();
 
 public void postSticky(Object event) {
 //スティッキーイベントをキャッシュすると、各イベントタイプの最新のものだけがキャッシュされる。
 synchronized (stickyEvents) {
 stickyEvents.put(event.getClass(), event);
 }
 //通常のイベント送信を行えば、登録された同じイベント・タイプのメソッドは、スティッキー・メソッドであるかどうかに関係なく受信・実行できる。
 post(event);
 }

Read next

Vuexの使用経験

vuexの使用経験まえがきプロジェクトでvuexを使用するため、最近Mootools.comのコースを勉強し直しました。vuexは、vueプロジェクトの同じページのすべてのコンポーネントで使用できる状態管理リポジトリです。

Aug 10, 2020 · 7 min read