使用带有保留片段的Otto时出现IllegalArgumentException

时间:2013-05-15 04:24:05

标签: android android-lifecycle otto

我正在使用Otto 1.3.3,当我恢复申请时,有时会得到IllegalArgumentException以下的堆栈跟踪:

Caused by: java.lang.IllegalArgumentException: Producer method for type class 
com.couchsurfing.mobile.ui.setup
        .SessionProviderFragment$SessionConnectionStateChangeEvent found on 
        type class com.couchsurfing.mobile.ui.setup.SessionProviderFragment, 
        but already registered by type class 
        com.couchsurfing.mobile.ui.setup.SessionProviderFragment.
    at com.squareup.otto.Bus.register(Bus.java:194)
    at com.couchsurfing.mobile.ui.BaseRetainedFragment
       .onCreate(BaseRetainedFragment.java:20)

SessionProviderFragment保留了其实例,请在扩展类下面找到:

public abstract class BaseRetainedFragment extends SherlockFragment {

    @Inject
    Bus bus;

    @Override
    public void onCreate(final Bundle state) {
        super.onCreate(state);
        ((CouchsurfingApplication) getActivity().getApplication()).inject(this);
        setRetainInstance(true);
        bus.register(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        bus.unregister(this);
        bus = null;
    }
}

我尝试使用bus.register(this)onAttach()中的onCreate(),但未改变此问题。

4 个答案:

答案 0 :(得分:7)

在公交车上注册的正确位置在onResume(),取消注册的正确位置在onPause()中,如下所示:

public abstract class BaseRetainedFragment extends RoboSherlockFragment {
    @Inject private Bus bus;

    @Override
    public void onCreate(final Bundle state) {
        super.onCreate(state);
        setRetainInstance(true);
    }

    @Override
    public void onResume() {
        super.onResume();
        bus.register(this);
    }

    @Override
    public void onPause() {
        super.onDestroy();
        bus.unregister(this);
    }
}

请注意,onDestroy()not guaranteed to be called

你可能会对此发表评论,然后说,嘿Chris,如果我在onResume()注册并且事件在我点击此方法之前被解雇我将不会收到这些事件!你是对的,但这意味着你没有像你应该那样使用Producers

另请注意,如果您使用roboguice-sherlock,则无需自己注射。当Fragment超出范围时,您也不需要null Bus垃圾收集器将为您清理它。

答案 1 :(得分:1)

我主要使用OttoEventBus将更新从后台服务传递到ActivitiesFragments。我不知道您的确切用例,但最常见的用途是更新用户界面(例如ProgressBarstatus message等)。

尽管如此,我发现最有效的方法是在片段的onViewCreated()方法中注册总线,并在onDestroyView()方法中取消注册。如果总线消息是持久性的(通过provider用于Otto或sticky EventBus事件,则不会以任何方式丢失任何消息。

答案 2 :(得分:1)

我在每个活动中使用一个“保留片段”来保存HTTP会话请求的状态。我的问题是我没有以正确的方式实例化我的“保留碎片”。

在进入onCreate()之前:

if (savedInstanceState == null) {
    sessionProviderFragment = new SessionProviderFragment();
    getSupportFragmentManager().beginTransaction().add(sessionProviderFragment,
        SessionProviderFragment.TAG).commit();
}

显然,当退出活动后再重新打开时,上面的代码可能会创建多个SessionProviderFragment。 它接缝的正确方法是:

sessionProviderFragment = (SessionProviderFragment) getSupportFragmentManager()
    .findFragmentByTag(SessionProviderFragment.TAG);

// If not retained (or first time running), we need to create it.
if (sessionProviderFragment == null) {
    sessionProviderFragment = new SessionProviderFragment();
    getSupportFragmentManager().beginTransaction().add(sessionProviderFragment,
            SessionProviderFragment.TAG).commit();
}
if (savedInstanceState == null) {
    initUiFragment();
}

我还在我的BaseFragment中的onResume / onPause中移动了总线注册/取消注册,以确保我一次总是在总线上注册一个SessionProviderFragment

答案 3 :(得分:0)

@Produce上设置Fragment并不是真的安全,因为片段的多个实例可以存在(并在总线上注册)同一时间。

在我看来@Produce实际上只对singleton有意义。