2011-07-20 19 views
14

Tengo algunos ListFragments que usan un CursorLoader para recuperar su contenido. A medida que el usuario profundiza en el contenido, un Fragmento reemplaza a otro (la Actividad sigue siendo la misma). Pero si los cambios en el contenido en un fragmento no superior, la aplicación se bloquea:Android: CursorLoader se bloquea en el Fragmento no superior

E/AndroidRuntime(18830): FATAL EXCEPTION: main 
E/AndroidRuntime(18830): java.lang.RuntimeException: Unable to resume activity {com.example.ExampleApp/com.example.ExampleApp.ExampleActivity}: java.lang.IllegalStateException: Content view not yet created 
E/AndroidRuntime(18830):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2120) 
E/AndroidRuntime(18830):  at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2135) 
E/AndroidRuntime(18830):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:957) 
E/AndroidRuntime(18830):  at android.os.Handler.dispatchMessage(Handler.java:99) 
E/AndroidRuntime(18830):  at android.os.Looper.loop(Looper.java:130) 
E/AndroidRuntime(18830):  at android.app.ActivityThread.main(ActivityThread.java:3683) 
E/AndroidRuntime(18830):  at java.lang.reflect.Method.invokeNative(Native Method) 
E/AndroidRuntime(18830):  at java.lang.reflect.Method.invoke(Method.java:507) 
E/AndroidRuntime(18830):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 
E/AndroidRuntime(18830):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 
E/AndroidRuntime(18830):  at dalvik.system.NativeStart.main(Native Method) 
E/AndroidRuntime(18830): Caused by: java.lang.IllegalStateException: Content view not yet created 
E/AndroidRuntime(18830):  at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328) 
E/AndroidRuntime(18830):  at android.support.v4.app.ListFragment.setListShown(ListFragment.java:280) 
E/AndroidRuntime(18830):  at android.support.v4.app.ListFragment.setListShownNoAnimation(ListFragment.java:266) 
E/AndroidRuntime(18830):  at com.example.ExampleApp.FirstListFragment.onLoadFinished(FirstListFragment.java:102) 
E/AndroidRuntime(18830):  at com.example.ExampleApp.FristListFragment.onLoadFinished(FirstListFragment.java:20) 
E/AndroidRuntime(18830):  at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:414) 
E/AndroidRuntime(18830):  at android.support.v4.app.LoaderManagerImpl$LoaderInfo.reportStart(LoaderManager.java:298) 
E/AndroidRuntime(18830):  at android.support.v4.app.LoaderManagerImpl.doReportStart(LoaderManager.java:751) 
E/AndroidRuntime(18830):  at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:512) 
E/AndroidRuntime(18830):  at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129) 
E/AndroidRuntime(18830):  at android.app.Activity.performStart(Activity.java:3791) 
E/AndroidRuntime(18830):  at android.app.Activity.performRestart(Activity.java:3821) 
E/AndroidRuntime(18830):  at android.app.Activity.performResume(Activity.java:3826) 
E/AndroidRuntime(18830):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2110) 
E/AndroidRuntime(18830):  ... 10 more 

Esto puede suceder cuando una actualización de fondo cambia el contenido, o cuando el usuario vuelve desde la pantalla de inicio y otro fragmento está en la cima de la pila posterior. El controlador onLoadFinished intenta actualizar la interfaz de usuario, que ya no existe. Pero ¿por qué el fragmento sigue obteniendo actualizaciones de cursor de todos modos?

He solucionado el problema cancelando la actualización si el fragmento no está visible, pero parece una acción incorrecta.

Esto es lo que se ve mi fragmento tiene gusto, editado un poco por razones de brevedad:

public class FirstListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> { 
    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 

     // Initialize empty cursor adapter. 
     mAdapter = new ExampleCursorAdapter(getActivity(), null, 0); 
     setListAdapter(mAdapter); 

     // Start with a progress indicator 
     setListShown(false); 

     // Prepare the loader. Either re-connect with an existing one, or start a new one. 
     getLoaderManager().initLoader(EXAMPLE_LOADER_ID, null, this); 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     return new CursorLoader(getActivity(), ExampleProvider.CONTENT_URI, PROJECTION, null, null, null); 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
     // Do nothing if we're not visible. 
     if(!isVisible()) { 
      return; 
     } 

     // Swap the new cursor in. (The framework will take care of closing the 
     // old cursor once we return.) 
     mAdapter.swapCursor(data); 

     // The list should now be shown. 
     if(isResumed()) { 
      setListShown(true); 
     } else { 
      setListShownNoAnimation(true); 
     } 
    } 

    @Override 
    public void onLoaderReset(Loader<Cursor> loader) { 
     // This is called when the last Cursor provided to onLoadFinished() above is about to be 
     // closed. We need to make sure we are no longer using it. 
     mAdapter.swapCursor(null); 
    } 
} 

El cheque isVisible() evita el choque, pero no lo es en cualquier parte del código ejemplo que he visto. ¿Qué pasa aquí?


Editar: Efectivamente, me terminó con un StaleDataException con una antigua vista de tratar de utilizar un cursor cerrado. Así que ahora estoy destruyendo el LoaderManager cuando se destruye la vista. Todavía no estoy seguro de si esto es lo correcto y no puedo reproducir StaleDataException.

Quité el truco isVisible() desde arriba y añade esto:

@Override 
public void onDestroyView() { 
    super.onDestroyView(); 

    // The CursorLoader example doesn't do this, but if we get an update while the UI is 
    // destroyed, it will crash. Why is this necessary? 
    getLoaderManager().destroyLoader(EXAMPLE_LOADER_ID); 
} 

Respuesta

2

Esto parece ser un problema con los fragmentos Lista conseguir la vista de lista. Por algún motivo getListView() no devolverá una vista válida a menos que se muestre el fragmento. Lo he visto arrojar una excepción de estado ilegal en cualquier cosa en el ciclo de vida anterior a Start().

La manera en que resuelvo el problema es que llamo al cargador en el método onCreateView(). Cuando el cargador devuelve datos, compruebo si la actividad se inició. Si es así, lo cargo en la vista de lista, si no lo guardo en una variable local, y luego cargo los datos en el método onStart().

Esto parece un super hack, pero funciona.

+0

Sí, parece que hay algún error con el cargador que regresa demasiado pronto y getActivity() sigue siendo nulo (el fragmento no está completamente conectado). El cargador parece ser reiniciado por el framework a veces también. lo que hace que vuelva demasiado temprano. – pjco

Cuestiones relacionadas