¿Por qué onLoadFinished se llama de nuevo después de que se reanude el fragmento?
Tengo un problema peculiar con los cargadores. Actualmente no estoy seguro de si se trata de un error en mi código o malentendido cargadores.
La aplicación
- ¿Se puede utilizar un LoaderManager de un servicio?
- GetLoaderManager en ListActivity
- IllegalStateException - Apoyo LoaderManager con AutocompleteTextView
- ¿Está bien tener una instancia de SQLiteOpenHelper compartida por todas las actividades en una aplicación de Android?
- Uso del patrón de diseño Singleton para SQLiteDatabase
El problema surge con las conversaciones (imagine algo similar a Whatsapp). Los cargadores que utilizo se implementan basados en el ejemplo de AsyncTaskLoader . Estoy usando la biblioteca de soporte.
- En OnCreate, inicio un cargador para recuperar los mensajes en caché.
- Cuando CachedMessageLoader finaliza, inicia un RefreshLoader para recuperar (en línea) los mensajes más recientes.
- Cada tipo de cargador como un ID distinto (por ejemplo, sin conexión: 1 en línea: 2)
Esto funciona muy bien, con la siguiente excepción.
Problema
Cuando abro otro fragmento (y agrego la transacción a la backstack) y luego utilizo la Back-Key para volver al onLoadFinished
conversación, onLoadFinished
se vuelve a llamar con ambos resultados anteriores. Esta llamada sucede antes de que el fragmento haya tenido alguna oportunidad de iniciar un cargador de nuevo …
Esta entrega de "antiguos" resultados que he obtenido antes de los resultados en los mensajes duplicados.
Pregunta
- ¿Por qué se entregan nuevamente esos resultados?
- ¿Puedo usar estos cargadores mal?
- ¿Puedo "invalidar" los resultados para asegurarme de que solo los entregue una vez o debo eliminar los duplicados?
Traza de la pila de la llamada
MyFragment.onLoadFinished(Loader, Result) line: 369 MyFragment.onLoadFinished(Loader, Object) line: 1 LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 427 LoaderManagerImpl$LoaderInfo.reportStart() line: 307 LoaderManagerImpl.doReportStart() line: 768 MyFragment(Fragment).performStart() line: 1511 FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 957 FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1104 BackStackRecord.popFromBackStack(boolean) line: 764 ...
Actualización 1 Los cargadores mencionados aquí son iniciados por el fragmento de conversación:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); Bundle args = getArguments(); m_profileId = args.getString(ArgumentConstants.ARG_USERID); m_adapter = new MessageAdapter(this); if (savedInstanceState != null) { restoreInstanceState(savedInstanceState); } if (m_adapter.isEmpty()) { Bundle bundle = new Bundle(); bundle.putString(ArgumentConstants.ARG_USERID, m_profileId); getLoaderManager().restartLoader(R.id.loader_message_initial, bundle, this); } else { // Omitted: Some arguments passed in Bundle Bundle b = new Bundle(). getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this); } } @Override public void onResume() { super.onResume(); // Omitted: setting up UI state / initiating other loaders that work fine } @Override public AbstractMessageLoader onCreateLoader(final int type, final Bundle bundle) { final SherlockFragmentActivity context = getSherlockActivity(); context.setProgressBarIndeterminateVisibility(true); switch (type) { case R.id.loader_message_empty: return new EmptyOnlineLoader(context, bundle); case R.id.loader_message_initial: return new InitialDBMessageLoader(context, bundle); case R.id.loader_message_moreoldDB: return new OlderMessageDBLoader(context, bundle); case R.id.loader_message_moreoldOnline: return new OlderMessageOnlineLoader(context, bundle); case R.id.loader_message_send: sendPreActions(); return new SendMessageLoader(context, bundle); case R.id.loader_message_refresh: return new RefreshMessageLoader(context, bundle); default: throw new UnsupportedOperationException("Unknown loader"); } } @Override public void onLoadFinished(Loader<Holder<MessageResult>> loader, Holder<MessageResult> holder) { if (getSherlockActivity() != null) { getSherlockActivity().setProgressBarIndeterminateVisibility(false); } // Omitted: Error handling of result (can contain exception) List<PrivateMessage> unreadMessages = res.getUnreadMessages(); switch (type) { case R.id.loader_message_moreoldDB: { // Omitted error handling (no data) if (unreadMessages.isEmpty()) { m_hasNoMoreCached = true; // Launch an online loader Bundle b = new Bundle(); // Arguments omitted getLoaderManager().restartLoader(R.id.loader_message_moreoldOnline, b, ConversationFragment.this); } // Omitted: Inserting results into adapter } case R.id.loader_message_empty: { // Online load when nothing in DB // Omitted: error/result handling handling break; } case R.id.loader_message_initial: { // Latest from DB, when opening // Omitted: Error/result handling // If we found nothing, request online if (unreadMessages.isEmpty()) { Bundle b = new Bundle(); // Omitted: arguments getLoaderManager().restartLoader(R.id.loader_message_empty, b, this); } else { // Just get new stuff Bundle b = new Bundle(); // Omitted: Arguments getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this); } break; } // Omitted: Loaders that do not start other loaders, but only add returned data to the adapter default: throw new IllegalArgumentException("Unknown loader type " + type); } // Omitted: Refreshing UI elements } @Override public void onLoaderReset(Loader<Holder<MessageResult>> arg0) { }
Actualizar 2 My MainActivity (que ultimatively alberga todos los fragmentos) subclases SherlockFragmentActivity y básicamente lanza fragmentos como este:
Fragment f = new ConversationFragment(); // Setup omitted f.setRetainInstance(false); // Omitted: Code related to navigation drawer FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction().replace(R.id.fragment_container_frame, f).commit();
El fragmento de conversación inicia el fragmento de "perfil de visualización" de la siguiente manera:
DisplayProfileFragment f = new DisplayProfileFragment(); // Arguments omitted FragmentManager manager = getSherlockActivity().getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit();
- ¿Qué hace LoaderManager?
- Android getContentResolver (). NotifyChange () no reinicia mi cargador
- Cuando una actividad se destruye debido a un cambio de configuración, ¿también se destruyen sus cargadores?
- Inicialización de un cargador en una actividad
- AlphabetIndexer con adaptador personalizado administrado por LoaderManager
- Paquete de Compatibilidad con Android y getLoaderManager ()
- ¿Cómo puedo actualizar el cursor desde un CursorLoader?
- ¿Cómo CursorLoader actualiza automáticamente la vista incluso si la aplicación está inactiva?
Hay otras preguntas similares, como Android: LoaderCallbacks.OnLoadFinished llamado dos veces Sin embargo, el comportamiento de los ganchos gestor de cargador son lo que son. Puede destruir el cargador después de obtener el primer conjunto de resultados
public abstract void destroyLoader (int id)
O puede manejar el onLoaderReset y vincular sus datos de interfaz de usuario más de cerca a los datos del cargador
public abstract void onLoaderReset (Loader<D> loader)
Se llama cuando un cargador creado previamente se está restableciendo y, por lo tanto, hace que sus datos no estén disponibles. La aplicación debe en este momento eliminar cualquier referencia que tenga a los datos del cargador.
Personalmente, utilizaría un ContentProvider y un CursorLoader para esto (cada fila de datos necesitaría tener un _ID único pero para los mensajes que no deben ser un problema).
- Me sale abierto: Error de EACCES (Permiso denegado) cuando intento escribir datos en la tarjeta SD en Android 5.1, ¿por qué?
- Android rxJava Manejo de errores con retroadaptación