Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid: CursorLoader crash on non-topmost Fragment
    primarykey
    data
    text
    <p>I have a few ListFragments that use a CursorLoader to retrieve their content. As the user drills down through the content, one Fragment replaces another (the Activity remains the same). But if the content changes on a non-topmost Fragment, the app crashes:</p> <pre><code>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 </code></pre> <p>This can happen when a background update changes the content, or when the user returns from the home screen and another Fragment is at the top of the back stack. The <code>onLoadFinished</code> handler tries to update the UI, which no longer exists. But why is the fragment still getting cursor updates anyway?</p> <p>I've worked around it by aborting the update if the fragment is not visible, but this seems like the wrong thing to do.</p> <p>Here's what my Fragment looks like, edited a little for brevity:</p> <pre><code>public class FirstListFragment extends ListFragment implements LoaderManager.LoaderCallbacks&lt;Cursor&gt; { @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&lt;Cursor&gt; onCreateLoader(int id, Bundle args) { return new CursorLoader(getActivity(), ExampleProvider.CONTENT_URI, PROJECTION, null, null, null); } @Override public void onLoadFinished(Loader&lt;Cursor&gt; 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&lt;Cursor&gt; 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); } } </code></pre> <p>The <code>isVisible()</code> check prevents the crash, but it's not in any of the example code I've seen. What's wrong here?</p> <hr> <p><strong>Edit:</strong> Sure enough, I wound up with a StaleDataException with an old view trying to use a closed cursor. So now I'm destroying the LoaderManager when the view is destroyed. Still not sure if this is the right thing to do, and I can't reproduce the StaleDataException.</p> <p>I removed the <code>isVisible()</code> hack from above and added this:</p> <pre><code>@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); } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload