Source code-Analysis of the realization principle of EventBus sticky events

1. Analysis of the realization principle of EventBus sticky events

1.1 Introduction and architecture of EventBus

When it comes to EventBus, I believe that most Android developers have used it. It is a publish/subscribe event bus framework designed for Android based on the observer pattern. It separates the receiver and sender of the event and simplifies the communication between components. The communication operation is very simple to use, and the source code is only one or two thousand lines, which is also very suitable for learning.

Insert picture description here

EventBus internally uses the handler mechanism to achieve cross-thread communication, so we don't need to use the handler mechanism to switch data between threads (note that EventBus cannot achieve cross-process communication because the singleton of EventBus is only valid in one process).

1.2 What is EventBus sticky event

Sticky events are a more distinctive feature in EventBus. What are sticky events? To answer this question, we must start with ordinary events.

public class EventBusTestActivity extends AppCompatActivity {
    
    

    private ActivityEventBusTestBinding bind;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        bind=ActivityEventBusTestBinding.inflate(getLayoutInflater());
        setContentView(bind.getRoot());
        //注册总线
        EventBus.getDefault().register(this);

        bind.btnSendMsg.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
            	//发送数据
                EventBus.getDefault().post(new MyMessage("普通消息"));
            }
        });
    }
    class MyMessage{
    
    
        final String msg;
        public MyMessage(String msg) {
    
    
            this.msg = msg;
        }
    }
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        //取消总线注册
        EventBus.getDefault().unregister(this);
    }
}

In general, the complete cycle of an Activity's common EventBus event is as follows:

注册到总线 -> 发送消息 ->销毁消息

It looks perfect, but what if there are two activities? ActivityA sends a message, and ActivityB also wants to receive it, but when ActivityA sends a message, ActivityB has not been created, it is very embarrassing, sticky events are to solve such problems.

The use of sticky events can be sent before the subscribed activity is created, and received after the creation is complete.

ActivityA创建,注册总线->点击按钮,发送事件->ActivityB创建,消费事件

1.3 Implementation principle of sticky events

How does it work? First, let's look at the sending function of sticky events

	private final Map<Class<?>, Object> stickyEvents;
	...
	EventBus(EventBusBuilder builder) {
    
    
        ...
        stickyEvents = new ConcurrentHashMap<>();
        ...
    }
	...
    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 immediately
        post(event);
    }
    
    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 {
    
    
                while (!eventQueue.isEmpty()) {
    
    
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
    
    
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

As you can see, here is only the sticky event stored in a ConcurrentHashMap, and then post is called to send the message, which can ensure the normal delivery of the message. But here we do not see how eventbus sends messages after the target activity is created. In fact, the mystery is in the part where the activity registers EventBus:

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

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    
    
       	...
        if (subscriberMethod.sticky) {
    
    
            if (eventInheritance) {
    
    
                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);
            }
        }
    }
    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 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);
        }
    }

After reading this code, it is estimated that everyone suddenly realized that when registering, when a sticky event was detected (the specific storage location is the sticky event pool that was added when the sticky event was released), the message was published directly. This is The principle of why sticky events can be posted before activity is created, and messages can be received when created.

register->subscribe->checkPostStickyEventToSubscription->postToSubscription

1.4 Other small questions about sticky events

You may also want to ask, when the sticky event is sent, the target activity has been created, what will happen? Will you receive two duplicate messages? The answer is obviously no, because after the target activity has been created, the register function is no longer executed. Naturally, two duplicate messages cannot appear. If you accidentally write EventBus.getDefault().register(this) twice, It doesn't matter, because EventBus does not register repeatedly after the class is registered.

The above conclusions can be seen in the source code of EventBus.

Guess you like

Origin blog.csdn.net/qq_23594799/article/details/108028917