Android EventBus粘性事件实现机制探究
什么是EventBus粘性事件?
在使用EventBus时,除了普通的事件外,还有一种特殊的事件——粘性事件。
所谓的粘性事件,是指在post一个事件时,如果当前并没有订阅者,那么这个事件会被保存下来(以队列的形式),等这个订阅者再次订阅时,这个事件会再次被发送出去,这就是EventBus的粘性事件实现机制。
EventBus粘性事件的实现
在EventBus的源码中,其实现机制就是优先调用订阅者的onEvent方法来订阅事件,如果没有找到该方法,则再判断是否有onEventSticky方法,如果有则将事件添加到一个类似于队列的结构中,并在后续的订阅操作中将该事件发送出去。
/**
* Posts the given event to the event bus.
*/
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;
}
}
}
同时,在移除粘性事件队列中的内容时,也需要对事件进行特判处理,以保证EventBus的粘性事件实现机制能够顺利执行。
/**
* Dispatches the given {@code event} to the currently registered subscribers according to its
* type. This method may be called from any thread, but the event will be dispatched on the main
* thread using the {@link Handler} specified during construction.
*/
protected void dispatch(Object event, Handler handler) throws EventBusException {
if (event == null) {
throw new EventBusException("Event to dispatch must not be null.");
}
try {
Class<?> eventType = event.getClass();
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventType);
boolean dispatched = false;
for (int i = 0, count = eventTypes.size(); i < count; i++) {
Class<?> clazz = eventTypes.get(i);
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(clazz);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
enqueueEvent(event, subscription);
}
dispatched = true;
}
}
if (!dispatched) {
Log.d(TAG, "No subscribers registered for event " + eventType);
if (eventClassBySubscriber.containsKey(eventType)) {
// For efficiency, we will avoid generating a stack trace since we should't
// have an error, this is just a warning!
Log.d(TAG, "(OK, don't worry, you are not using ProGuard.)");
} else {
Log.d(TAG, "The eventClass " + eventType + " is not a subclass of any registered events");
}
}
} else {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventType);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
enqueueEvent(event, subscription);
}
} else {
Log.d(TAG, "No subscribers registered for event " + eventType);
if (eventClassBySubscriber.containsKey(eventType)) {
// For efficiency, we will avoid generating a stack trace since we should't
// have an error, this is just a warning!
Log.d(TAG, "(OK, don't worry, you are not using ProGuard.)");
} else {
Log.d(TAG, "The eventClass " + eventType + " is not registered");
}
}
}
} finally {
eventQueue.clear(); // 清空粘性事件队列
}
}
EventBus粘性事件的使用
使用EventBus的粘性事件也非常简单,在EventBus.post()方法中,直接传入粘性事件即可。
EventBus.getDefault().postSticky(new TestEvent("sticky event"));
同时,在订阅的时候,和普通事件的订阅方式一致。
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(TestEvent event) {
// do something
}
需要注意的是,在使用粘性事件的时候,需要使用sticky属性将事件标记为粘性事件,同时订阅者的onEvent方法也需要用sticky属性进行标记,以确保能够正确地接收到粘性事件。
示例
示例一
在第一个示例中,我们可以定义一个事件类TestEvent,以及在MainActivity中订阅这个事件,并且在Fragment中进行post,代码如下:
public class TestEvent {
public String message;
public TestEvent(String message) {
this.message = message;
}
}
public class MainActivity extends AppCompatActivity {
private MyFragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFragment = new MyFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, mFragment).commit();
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(TestEvent event) {
Toast.makeText(this, "sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
}
}
public class MyFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_my, container, false);
rootView.findViewById(R.id.btn_send_sticky).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().postSticky(new TestEvent("sticky event"));
}
});
return rootView;
}
@Override
public void onDestroyView() {
super.onDestroyView();
EventBus.getDefault().removeAllStickyEvents();
}
}
在MainActivity中的onStickyEvent()方法中订阅了TestEvent这个事件,并且在MyFragment的点击事件中post了这个事件,这时候事件并没有被订阅者接收到,但是当我们返回MainActivity时,这个事件会被后续的订阅者接收到。
示例二
在第二个示例中,我们可以定义一个粘性事件,然后在两个不同的Activity中订阅和post这个事件,来证明EventBus粘性事件的机制是全局的。
代码如下:
public class StickyEvent {
public String message;
public StickyEvent(String message) {
this.message = message;
}
}
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(StickyEvent event) {
Toast.makeText(this, "FirstActivity sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
}
public void startSecondActivity(View view) {
startActivity(new Intent(this, SecondActivity.class));
}
}
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
// post sticky event
EventBus.getDefault().postSticky(new StickyEvent("sticky event"));
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().removeAllStickyEvents();
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(StickyEvent event) {
Toast.makeText(this, "SecondActivity sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
}
public void startFirstActivity(View view) {
startActivity(new Intent(this, FirstActivity.class));
}
}
在FirstActivity和SecondActivity中都订阅了StickyEvent这个粘性事件,并且在SecondActivity中post了这个事件,随后在FirstActivity中启动SecondActivity,返回FirstActivity后,我们可以看到之前post的事件被接收到了。
总结
EventBus的粘性事件机制,可以帮助我们在事件分发时,使得那些尚未订阅的订阅者能够在之后订阅时被及时地接收到,不错过任何一个事件。使用起来也十分简单,并且支持全局的粘性事件分发。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android EventBus粘性事件实现机制探究 - Python技术站