IllegalArgumentException cuando se utiliza Otto con un fragmento retenido

Estoy usando Otto 1.3.3 y cuando IllegalArgumentException mi aplicación a veces consigo una IllegalArgumentException con el stacktrace siguiente:

 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 tiene su instancia retenida, SessionProviderFragment continuación la clase extendida:

 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; } } 

He intentado usar bus.register(this) en onAttach() o onCreate() , que no cambió el problema.

El lugar adecuado para registrarse en el bus es onResume() y el lugar adecuado para onPause() registro está en onPause() así:

 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); } } 

Tenga en cuenta que onDestroy() no está garantizado para ser llamado .

Usted podría estar a punto de comentar sobre esto y decir, hey Chris, si me registro en onResume() y los eventos se disparan antes de que me golpeó este método no voy a recibir los eventos! Usted tendría razón, pero esto significa que no está utilizando los productores como debe ser.

También tenga en cuenta, si utiliza roboguice-sherlock no tiene que inyectarse. Tampoco es necesario null el Bus cuando el fragmento sale del alcance del recolector de basura lo limpia para usted.

He utilizado Otto y EventBus principalmente para pasar actualizaciones de los servicios de fondo a las Activities y Fragments . No sé su caso de uso exacto, pero el uso más común para mí era actualizar la interfaz de usuario (por ejemplo, ProgressBar , status message , etc).

Dicho esto, lo que he encontrado como más eficiente, es registrar el bus en el método onViewCreated() del fragmento y anular su registro en el método onDestroyView() . Siempre que los mensajes de bus sean persistentes (a través de un provider de Otto o eventos sticky para EventBus ), no perderá ningún mensaje de esta manera.

Estoy usando un "Fragmento retenido" por actividad para guardar el estado de una solicitud de sesión HTTP. Mi problema era que no instancié mi "Fragmento retenido" de la manera correcta.

Antes de que tuviera en onCreate ():

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

Al parecer, el código anterior podría crear varios SessionProviderFragment al salir de la actividad se está reabriendo más tarde. Cosa que la manera correcta es:

 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(); } 

También moví el registro de bus / unregister en onResume / onPause en mi BaseFragment para estar seguro de que siempre tendré un SessionProviderFragment registrado en el bus a la vez.

No es realmente seguro tener un @Produce en un Fragment , porque más de una instancia del fragmento puede existir (y estar registrado en el bus) al mismo tiempo.

En mi opinión @Produce realmente sólo tiene sentido en un singleton .

  • RxAndroid, bus de eventos y ciclo de vida de la actividad
  • Cancelar el registro de los oyentes en onDestroy - ¿cuál es el daño / pueden tener fugas?
  • Informe Leakcanary de pérdida de memoria usando Otto
  • @Subscribe método llamado tiempo múltiple para el mismo evento
  • Otto vs LocalBroadcast:
  • Utilizar Otto para actualizar un listadapter de un GcmListenerService
  • NullPointerException con Otto y Dagger
  • Otto (Bus de eventos), enviar fragmento de evento a fragmento pero no recibir
  • RuntimeException: No se pudo enviar evento @ otto
  • Mejores Prácticas para el Manejo de la Búsqueda
  • ¿Cómo uso eficazmente un bus de eventos?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.