EventBus 发布给未注册的订阅者
EventBus posts to unregistered subscriber
我在使用EventBus的时候遇到了一个Fragment注销后订阅方法被调用一次的意外情况
场景是这样的。我有一个 Activity 包含可以放置任何片段的布局。 Activity 以某个 Fragment 开头。
Activity 和片段在 onResume()
上注册,在 onPause()
上注销。对于相同类型的事件,它们每个都有自己的处理程序。
通过调度的事件,Activity 根据某种条件用另一个替换片段。然后,片段的 onPause()
在删除过程中被调用, EventBus.getDefault().unregister(this)
也被执行。
那么,我希望从现在开始不要调用 Fragment 的处理程序。但是在取消注册片段后立即调用一次。
EventBus 似乎无法处理在事件发布过程中取消注册任何订阅者的情况。有人知道这个问题吗?
已编辑以获取更多详细信息
Fragment中的相关方法
@Override
public void onResume() {
Log.d(LOG_TAG, "onResume()");
super.onResume();
EventBus.getDefault().register(this);
Communicator.registerListener(listener);
}
@Override
public void onPause() {
Log.d(LOG_TAG, "onPause()");
Communicator.unregisterListener(listener);
EventBus.getDefault().unregister(this);
super.onPause();
}
@Subscribe(sticky = true)
public void handleEvent(DeviceConnectionSelectEvent event) {
if (event.container != null) {
setDevice(event.container.getRapaelDevice());
}
}
onPause()
的调用堆栈
Fragment.onPause()
at com.neofect.rapael.client.bridge.app.device.kids.SmartKidsSensorDataFragment.onPause(SmartKidsSensorDataFragment.java:105)
at android.support.v4.app.Fragment.performPause(Fragment.java:2139)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1117)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1252)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1234)
at android.support.v4.app.FragmentManagerImpl.dispatchPause(FragmentManager.java:2060)
at android.support.v4.app.Fragment.performPause(Fragment.java:2135)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1117)
at android.support.v4.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1349)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:695)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:570)
at com.neofect.rapael.client.bridge.app.MainActivity.changeDeviceDetailFragment(MainActivity.java:111)
at com.neofect.rapael.client.bridge.app.MainActivity.handleEvent(MainActivity.java:100)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:485)
at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:416)
at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:397)
at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:370)
at org.greenrobot.eventbus.EventBus.post(EventBus.java:251)
at org.greenrobot.eventbus.EventBus.postSticky(EventBus.java:292)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.selectConnectionItem(DeviceConnectionListPresenter.java:114)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.access0(DeviceConnectionListPresenter.java:28)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:179)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:122)
at com.neofect.communicator.CommunicationHandler.run(CommunicationHandler.java:90)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
handleEvent()
的调用堆栈 - 这是在上面 onPause()
之后立即调用的
Fragment.handleEvent()
at com.neofect.rapael.client.bridge.app.device.kids.SmartKidsSensorDataFragment.handleEvent(SmartKidsSensorDataFragment.java:121)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:485)
at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:416)
at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:397)
at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:370)
at org.greenrobot.eventbus.EventBus.post(EventBus.java:251)
at org.greenrobot.eventbus.EventBus.postSticky(EventBus.java:292)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.selectConnectionItem(DeviceConnectionListPresenter.java:114)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.access0(DeviceConnectionListPresenter.java:28)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:179)
at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:122)
at com.neofect.communicator.CommunicationHandler.run(CommunicationHandler.java:90)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
handleEvent()
根据上述在与 onPause()
相同的调用堆栈上调用。他们在一个事件发布中被调用。
我猜测事件调度循环(消息队列循环)在开始发布之前保留订阅列表,因此它无法处理在发布过程中完成的订阅删除。
你是对的。
如果您查看 Event Bus 的源代码,您会发现对于 相同的线程(在您的情况下为 MAIN)Event Bus 不会重新检查订阅是否在 posting 过程中活跃:
post()
在此方法中,事件总线到达 postSingleEventForEventType()
,它获取当前可用的所有订阅并通知它们。那些来自同一个线程的,没有检查就被执行:
EventBus#invokeSubscriber(subscription, event)
其他的已安排并将使用执行检查的方法执行:
EventBus#invokeSubscriber(pendingPost)
注销()
在此方法中,事件总线到达 unsubscribeByEventType()
,它获得相同的订阅,删除并停用相应的订阅:
List<Subscription> subscriptions = subscriptionsByEventType.get(eventClass);
for (...) {
// ...
subscription.active = false;
subscriptions.remove(i);
}
我在使用EventBus的时候遇到了一个Fragment注销后订阅方法被调用一次的意外情况
场景是这样的。我有一个 Activity 包含可以放置任何片段的布局。 Activity 以某个 Fragment 开头。
Activity 和片段在 onResume()
上注册,在 onPause()
上注销。对于相同类型的事件,它们每个都有自己的处理程序。
通过调度的事件,Activity 根据某种条件用另一个替换片段。然后,片段的 onPause()
在删除过程中被调用, EventBus.getDefault().unregister(this)
也被执行。
那么,我希望从现在开始不要调用 Fragment 的处理程序。但是在取消注册片段后立即调用一次。
EventBus 似乎无法处理在事件发布过程中取消注册任何订阅者的情况。有人知道这个问题吗?
已编辑以获取更多详细信息
Fragment中的相关方法
@Override public void onResume() { Log.d(LOG_TAG, "onResume()"); super.onResume(); EventBus.getDefault().register(this); Communicator.registerListener(listener); } @Override public void onPause() { Log.d(LOG_TAG, "onPause()"); Communicator.unregisterListener(listener); EventBus.getDefault().unregister(this); super.onPause(); } @Subscribe(sticky = true) public void handleEvent(DeviceConnectionSelectEvent event) { if (event.container != null) { setDevice(event.container.getRapaelDevice()); } }
的调用堆栈onPause()
Fragment.onPause() at com.neofect.rapael.client.bridge.app.device.kids.SmartKidsSensorDataFragment.onPause(SmartKidsSensorDataFragment.java:105) at android.support.v4.app.Fragment.performPause(Fragment.java:2139) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1117) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1252) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1234) at android.support.v4.app.FragmentManagerImpl.dispatchPause(FragmentManager.java:2060) at android.support.v4.app.Fragment.performPause(Fragment.java:2135) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1117) at android.support.v4.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1349) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:695) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617) at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:570) at com.neofect.rapael.client.bridge.app.MainActivity.changeDeviceDetailFragment(MainActivity.java:111) at com.neofect.rapael.client.bridge.app.MainActivity.handleEvent(MainActivity.java:100) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:485) at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:416) at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:397) at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:370) at org.greenrobot.eventbus.EventBus.post(EventBus.java:251) at org.greenrobot.eventbus.EventBus.postSticky(EventBus.java:292) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.selectConnectionItem(DeviceConnectionListPresenter.java:114) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.access0(DeviceConnectionListPresenter.java:28) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:179) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:122) at com.neofect.communicator.CommunicationHandler.run(CommunicationHandler.java:90) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5017) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method)
之后立即调用的handleEvent()
的调用堆栈 - 这是在上面onPause()
Fragment.handleEvent() at com.neofect.rapael.client.bridge.app.device.kids.SmartKidsSensorDataFragment.handleEvent(SmartKidsSensorDataFragment.java:121) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:485) at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:416) at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:397) at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:370) at org.greenrobot.eventbus.EventBus.post(EventBus.java:251) at org.greenrobot.eventbus.EventBus.postSticky(EventBus.java:292) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.selectConnectionItem(DeviceConnectionListPresenter.java:114) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.access0(DeviceConnectionListPresenter.java:28) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:179) at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter.onDeviceReady(DeviceConnectionListPresenter.java:122) at com.neofect.communicator.CommunicationHandler.run(CommunicationHandler.java:90) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5017) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method)
handleEvent()
根据上述在与 onPause()
相同的调用堆栈上调用。他们在一个事件发布中被调用。
我猜测事件调度循环(消息队列循环)在开始发布之前保留订阅列表,因此它无法处理在发布过程中完成的订阅删除。
你是对的。
如果您查看 Event Bus 的源代码,您会发现对于 相同的线程(在您的情况下为 MAIN)Event Bus 不会重新检查订阅是否在 posting 过程中活跃:
post()
在此方法中,事件总线到达 postSingleEventForEventType()
,它获取当前可用的所有订阅并通知它们。那些来自同一个线程的,没有检查就被执行:
EventBus#invokeSubscriber(subscription, event)
其他的已安排并将使用执行检查的方法执行:
EventBus#invokeSubscriber(pendingPost)
注销()
在此方法中,事件总线到达 unsubscribeByEventType()
,它获得相同的订阅,删除并停用相应的订阅:
List<Subscription> subscriptions = subscriptionsByEventType.get(eventClass);
for (...) {
// ...
subscription.active = false;
subscriptions.remove(i);
}