ソースコード分析-EventBusワークフローを理解する

EventBusソースコード分析

EventBusは、私たちが頻繁に使用するオープンソースライブラリです。比較的簡単に使用でき、強力です。このような強力なライブラリの内部実装を検討できないのはなぜですか。
## EventBusの作成と変数
### EventBusオブジェクトの作成

EventBusを使用するときは、EventBus.getDefault()を使用し、オブジェクトに対して静的メソッドgetDefault()を使用してから、登録や投稿などの操作を実行することがよくあります。EventBusクラスの構築メソッドには、外部ではパラメーター以外の構築のみがあり、EventBus(EventBusBuilderビルダー)メソッドは内部で呼び出されます。EventBusオブジェクトを取得するには、実際には4つの方法があります。

  • ** EventBus.getDefault():**最も一般的に使用されるメソッドであるシングルトンモードは、デフォルトの構成を使用します。このメソッドのすべての呼び出しは、取得したオブジェクトが一意であることを確認するためにアプリで使用され、同じEventBusオブジェクトなしでサブスクリプションイベントと送信イベントが送信されることはありません。
  • new EventBus():あまり使用されず、デフォルト構成も使用されます
  • EventBus.builder()。build(): Builderメソッドを使用してオブジェクトをビルドしますこれは、ビルダーモードとして理解でき、手動で構成できます(ログの印刷、例外イベントの送信、例外のスローなど)。
  • ** EventBus.builder()。installDefaultEventBus():**これはBuilderの方法で構築されたオブジェクトでもあり、手動で構成できます。build()との違いは、EventBus.getDefault(()と同じであるEventBusのデフォルトオブジェクトを構築することです。 )同じオブジェクトを取得します。注:installDefaultEventBus()メソッドは、EventBus.getDefault()の前に呼び出す必要があります。そうしないと、オブジェクトが作成され、構成が無効になり、例外がスローされます。

EventBusメンバー変数

いくつかの重要な変数を紹介します。

  • デフォルトのEventBuilderオブジェクト、デフォルトの構成
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
  • キー変数、サブスクリプションイベントタイプによって区別されるすべてのサブスクリプションイベントオブジェクト(サブスクライバーとサブスクリプションメソッドを含む)のコレクション、および特定のイベントをサブスクライブするすべてのメソッドを見つけることができます
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
  • キー変数であるサブスクライバーとそれに対応するすべてのサブスクライブされたイベントのコレクションは、サブスクライバーに応じてサブスクライバーによってサブスクライブされたすべてのイベントを見つけることができます。
private final Map<Object, List<Class<?>>> typesBySubscriber
  • より重要な変数であるPostingThreadStateクラスは、現在の投稿の状態を記述します。ostメソッドを介して送信されたすべてのイベントは、最初にこのクラスのキューに格納され、次に配布操作のためにキューから取り出されますさらに、このクラスには、配布されているかどうか、ポスト操作がメインスレッドにあるかどうか、操作がキャンセルされているかどうかなどの変数が含まれています。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};
  • メインスレッドでの配布、バックグラウンドスレッドの配布、および非同期スレッドの配布操作を個別に制御します
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
  • このオブジェクトを介して特定のサブスクライバーのすべてのサブスクリプションメソッドを検索します
private final SubscriberMethodFinder subscriberMethodFinder;

EventBusサブスクリプション

登録()

サブスクリプション、つまりregister()メソッドは、クラスでEventBus.getDefault()。register(this)を呼び出した後、登録されたサブスクリプションイベントをそのクラスで受信できることを認識しています。

その前に、最初に2つのクラスを理解します。

** SubscriberMethod:** Subscriberメソッドクラス。これは、コードで注釈が付けられた@Subscribeを使用して、イベントを処理するためのメソッドを監視および受信するメソッドです。

final Method method;         //方法名、参数等信息,android原生反射类
final ThreadMode threadMode; //在哪一个线程中接收此事件
final Class<?> eventType;    //事件类型,如String.class等
final int priority;          //优先级,
final boolean sticky;        //是否粘性传输
String methodString;         //用来检测是否是同一个监听方法

**サブスクリプション:**サブスクライバーに対応するオブジェクトとその中のイベントメソッド

final Object subscriber;                //订阅者(例如某一个Activity对象)
final SubscriberMethod subscriberMethod;//订阅者内的一个订阅方法

登録プロセスを続行します。

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //找出这个类监听的所有事件,遍历列表进行订阅相关操作
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

このメソッドには多くのことが書かれていないことがわかります。1つ目は、前述のsubscriberMethodFinderを介してすべてのサブスクリプションメソッドを見つけることです。具体的な実装は、リフレクションを介して見つけることです。次に、これらのメソッドをそれぞれ登録してバインドするので、subscribe()メソッドを引き続き確認します。

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
	    //获取该订阅方法的事件类型
        Class<?> eventType = subscriberMethod.eventType;
        //创建一个新的订阅者和订阅方法组合对象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //根据事件类型,获取当前所有监听该类型的订阅操作(订阅者和订阅方法)
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //如果找不到,新建列表,以当前事件类型为key塞入map集合中
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //同一个订阅者对象中,如果有该订阅方法,不能重复订阅,也就是一个类中的onEvent方法不能是相同的参数(事件类型)
            if (subscriptions.contains(newSubscription)) {
                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;
            }
        }

        //获取该订阅者订阅的所有事件,如果没有,新插入一个
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //将订阅的事件插入到该订阅者订阅的所有事件的集合中
        subscribedEvents.add(eventType);

        //粘性事件处理
        if (subscriberMethod.sticky) {
            ...
        }
    }

このメソッドは登録プロセス全体の鍵ですが、簡単に言えば、2つのポイントに要約できます
。1。登録されたすべてのイベントタイプのリストを取得し、新しく見つかったメソッドをリストに挿入し、最後にイベントタイプをキーとして使用し、リストを次のように使用します。値が更新されるか、マップコレクションに挿入されます。
2.サブスクライバーをキー、リストを値として、このサブスクライバーに関連するすべてのサブスクリプション操作のリストを取得し、それらをマップコレクションに詰め込みます。

unRegister()

通常、onDestroyメソッドでは登録がキャンセルされ、登録をキャンセルすると受信メッセージは監視されなくなります。登録プロセスから、unRegisterでは、元に戻すだけでよいことがわかります。元に戻すにはどうすればよいですか。削除、削除、削除!

    public synchronized void unregister(Object subscriber) {
        //找出这个订阅者订阅的所有事件
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
	        //遍历这些事件方法,在事件类型-订阅操作map集合中找出这些方法并移除
            for (Class<?> eventType : subscribedTypes) {
	            //方法实现为:找出这个事件类型的所有订阅操作,循环匹配如果是当前这个订阅者订阅的,从列表中移除
                unsubscribeByEventType(subscriber, eventType);
            }
            //最后从map集合中移除这个订阅者
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

##イベントモニタリング

コメントを購読する

@Subscribeアノテーションを使用すると、threadMode = ThreadMode.MAINのようなコードが追加されることがあります。これが使用されるのはなぜですか?リスナーがイベントを受信するスレッドを設定してください!

それだけでなく、イベントがスティッキーかどうかを設定したり、優先度を設定したりすることもできます。購読メモ:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
	//可设置线程模式,默认为POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
	//是否粘性传输,默认false
    boolean sticky() default false;
	//优先级,默认为0,排到队尾
    int priority() default 0;
}

ThreadMode

これはEventBusのかなり印象的な設定であり、操作を実行するスレッドを指定できます。

#### ThreadMode.POSTING
デフォルトモード。イベントは、投稿後にサブスクライバーに直接配布され、送信イベントと同じスレッドで実行されます。スレッドの切り替え回避されるため、オーバーヘッドが最小限に抑えられます。プロジェクトでスレッド間の切り替えが多すぎない場合、これは公式に推奨されるモードでもあります。ただし、送信スレッドがメインスレッドである場合のブロックを回避するために、メソッドで時間のかかる操作を実行しないようにしてください

#### ThreadMode.MAIN
イベントの監視と実行は、メイン(UI)スレッドで実行されます送信イベントがメインスレッドにある場合は直接実行されます。それ以外の場合は、メインスレッドがキューに入れられ、ポスターを介して実行するために配布されます。また、メインスレッドのブロックを回避するために、メソッドで時間のかかる操作を実行しないでください
#### ThreadMode.MAIN_ORDEREDは
基本的にMAINと同じです。違いは、イベントの送信元のスレッドに関係なく、メインスレッドのPosterを介してキューに入れて均一に実行する必要があることです。
#### ThreadMode.BACKGROUND
バックグラウンドスレッドの実行。送信イベントがメインスレッドにない場合は、現在のスレッドで直接実行されます。送信イベントがメインスレッドにある場合、EventBusは一意のバックグラウンドスレッドを開始し、すべてのイベントはこのスレッドで実行するためにキューに入れられます。また、メソッドで時間のかかる操作を実行しないことをお勧めします。
#### ThreadMode.ASYNC
は非同期スレッドで実行されます。イベントを送信するスレッドに関係なく、このスレッドでは実行されません。操作を実行するために新しい非同期スレッドが開かれます。このモードは通常、モニターで時間のかかる操作を実行する必要がある場合に使用されます。EventBusは、スレッドプールを使用して、あまりにも多くのスレッドを開かないように管理します。

イベントを送信する

これはもっと重要です。使用するときに簡単な投稿を投稿するだけで済みます。それでは、サブスクライバーはこれらのイベントをどのように受信しますか?そして、それでもどのスレッドになりたいですか?同様に、最初にいくつかのカテゴリを理解しましょう。


送信するイベントを記述するためにPendingPostクラスが使用され、キューは再利用のために内部的に維持されます。
オブジェクトを取得し、リストを再利用します。

    static PendingPost obtainPendingPost(Subscription subscription, Object event) {
        /**
         * 如果当前正在从队列中复用,则直接return一个new出来的对象
         * 如果队列为空,同样返回新对象
         * 如果能够复用,则取队列最后一个对象,将其变量置为传入的参数,最后return这个对象
         */
        synchronized (pendingPostPool) {
            int size = pendingPostPool.size();
            if (size > 0) {
                //取对象使用的是remove方法,从队列中移出,这样保证队列不会越来越长
                PendingPost pendingPost = pendingPostPool.remove(size - 1);
                pendingPost.event = event;
                pendingPost.subscription = subscription;
                pendingPost.next = null;
                return pendingPost;
            }
        }
        return new PendingPost(event, subscription);
    }

使用後にオブジェクトを解放します。

    static void releasePendingPost(PendingPost pendingPost) {
        pendingPost.event = null;
        pendingPost.subscription = null;
        pendingPost.next = null;
        synchronized (pendingPostPool) {
            // Don't let the pool grow indefinitely
            /**
             * 当前队列不能超过10000,因为每一次obtain要么获取一个新对象,要么从队列中移除最后一个对象,
             * 所以即是保证当前同一个线程同一时间内需要发送的事件操作不超过10000
             */
            if (pendingPostPool.size() < 10000) {
                pendingPostPool.add(pendingPost);
            }
        }
    }

つまり、イベントを送信するすべての投稿で、送信済みのイベントオブジェクト、イベントタイプ、サブスクライバー情報、サブスクリプションメソッド、およびその他の情報を含む、送信可能なオブジェクトを最終的に作成します。

PendingPostQueue
这个类可以看成是一个链表,每一个节点就是一个PendingPost对象,通过PendingPost的next变量连接,取值、移除可参照单链表用法。

###开始Post
1、主动调用post()方法发送事件,传入一个Object对象
2、获取我们开始提到的PostingThreadState 对象,将该事件添加到里面的列表,判断当前状态。

    public void post(Object event) {
        /**
         * 获取当前发送对象,添加到队列中,如果正在发送其他事件,则在发送后会继续发送新加入的事件(while循环)
         * 如果当前空闲,则立即发送事件
         * 最后重置状态
         */
        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 {
                //从队列中获取事件对象使用remove方法获取,省去发送后从队列删除这一操作
                //列表为空时退出循环,因为前面的逻辑,是有可能正在post操作的时候,新的post发过来了,添加到了列表中,
                //但是还没有没有真正发送,所以发送完一个之后需要判断是否还有需要发送的事件
                while (!eventQueue.isEmpty()) {
		            //真正发送在这里
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

3、根据事件类型,在集合中查找是否有订阅这个事件类型的订阅方法,如果没有找到,抛出异常;如果找到,循环处理每一个订阅操作

            for (Subscription subscription : subscriptions) {
	            //当前发送状态更新
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
	                //给订阅者发送事件
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }

5、最后的发送,根据订阅方法选择的线程,选择相应的poster分发

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        //当初注册@Subscribe注解时设置的在哪个线程中接收事件,默认Posting,即当前线程(和发送者同一线程)
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
	            //直接当前线程执行
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
	            //如果当前是主线程,直接执行
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                //否则使用主线程Poster统一排队分发
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
	            //不管处于何线程,统一使用主线程Poster排队分发
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
	            //如果不在主线程,直接执行,否则,使用后台Poster排队分发
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
	            //不管处于何线程,统一使用异步Poster排队分发
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

执行

执行比较简单了,使用java反射机制

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

几个Poster

在哪一个线程中接收到事件,取决于用哪一个Poster发送,默认的话是直接执行,主线程执行是使用HandlerPoster,后台执行是使用BackgroundPoster,异步执行使用AsyncPoster。
首先来看Poster,公共实现的一个借口类:

interface Poster {
    void enqueue(Subscription subscription, Object event);
}

里面就一个执行方法,也就是发送事件

AsyncPoster

异步Poster实现了Runnable和Poster接口,利用Runnable新开一个线程执行。
执行enqueue方法时,内部执行了Runnable的必须实现方法run(),这个就是新开一个线程执行啦,线程里面调用EventBus里的**invokeSubscriber()**反射方法。

BackgroundPoster
とAsyncPosterの違いは、1つのスレッドのみを開くことです。イベントが発生して配信する必要がある場合、最初に他の操作が現在実行されているかどうかを判断します。空きがある場合はすぐに実行し、そうでない場合は最初にリストとキューを挿入し、前のイベントが処理されるまで待ちます。次に、次のイベントを発行します。

HandlerPoster
は最後のメインスレッドPosterです。このPosterが作成されると、Looper.getMainLooper()が渡されるため、メインスレッドでの監視が実現されます。このポスターはハンドラーモードを使用します。メッセージの送信要求を受信すると、独自のsendMessage()メソッドを呼び出し、handleMessageで要求を処理します。

    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            //开始处理时间
            long started = SystemClock.uptimeMillis();
            while (true) {
	            //从队列中取出一个PendingPost操作
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                //最终的使用反射执行订阅方法
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                //如果循环执行时间大于10毫秒,退出循环,避免一直从队列中取数据,
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }

終わり

この時点で、EventBusプロセス全体は基本的に終了しています。この記事は、プロセスの分析から始まり、基本的にEventBusフレームワーク全体と内部実装の原則について説明します。これには多くの欠点があります。遠慮なく教えてください。
感謝!

おすすめ

転載: blog.csdn.net/lizebin_bin/article/details/80409169