EventBus发布到未注册的订阅者

时间:2016-05-24 08:43:34

标签: android greenrobot-eventbus

在使用EventBus时,我遇到了意外情况,即片段​​取消注册后,片段的订阅方法也会被调用一次。

情景是这样的。我有一个Activity,其中包含可以放置任何Fragment的布局。活动以某个片段开头。

活动和片段在onResume()上注册,并在onPause()上取消注册。对于同一类型的事件,他们都有自己的处理程序。

通过调度事件,Activity会根据某种情况将Fragment替换为另一个。然后,在删除过程中调用片段onPause(),同时执行EventBus.getDefault().unregister(this)

然后,我希望现在不会调用Fragment的处理程序。但是在片段未注册后立即调用它。

似乎EventBus没有处理任何订阅者在事件发布过程中未注册的情况。有谁知道这个问题?

编辑了更多详情

  • 片段中的相关方法

    @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.access$400(DeviceConnectionListPresenter.java:28)
              at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter$1.onDeviceReady(DeviceConnectionListPresenter.java:179)
              at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter$1.onDeviceReady(DeviceConnectionListPresenter.java:122)
              at com.neofect.communicator.CommunicationHandler$5.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.access$400(DeviceConnectionListPresenter.java:28)
              at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter$1.onDeviceReady(DeviceConnectionListPresenter.java:179)
              at com.neofect.rapael.client.bridge.app.ui.device_connection_list.DeviceConnectionListPresenter$1.onDeviceReady(DeviceConnectionListPresenter.java:122)
              at com.neofect.communicator.CommunicationHandler$5.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()相同的调用堆栈上调用。它们在一个事件发布中被调用。

我猜测事件调度循环(消息队列循环)在开始发布之前保留了订阅列表,因此它无法处理在发布过程中完成的订阅删除。

1 个答案:

答案 0 :(得分:2)

你是对的。

如果查看事件总线的源代码,您将看到相同的线程(在您的情况下为MAIN)如果订阅在发布过程中处于活动状态,则事件总线不会重新检查:

交的()

在此方法中,事件总线到达postSingleEventForEventType(),此时它将获取所有可用的订阅并通知它们。那些来自同一个线程的,没有检查执行:

EventBus#invokeSubscriber(subscription, event)

其他人已安排,并将使用执行检查的方法执行:

EventBus#invokeSubscriber(pendingPost)

取消注册()

在此方法中,事件总线到达unsubscribeByEventType(),获取相同的订阅,删除和停用相应的订阅:

List<Subscription> subscriptions = subscriptionsByEventType.get(eventClass);
for (...) {
    // ...
    subscription.active = false;
    subscriptions.remove(i);
}