rciovati
rciovati

Reputation: 28063

Exception loading data with CursorLoader after screen rotation

I'm having a strange issue with my application running on a Nexus 7 tablet. I have two fragments that load data with CursorLoader from my custom ContentProvider.

When i startup the application it runs fine both in landscape and portait mode, loaders load data properly. The problem occurs when i rotate the screen: my Activity and both Fragments are correctly recreated and loaders are started again, as expected. They load data (debugging i saw that onLoadFinished is called for both loaders) but after that the application is FC with this exception:

08-30 13:10:03.310: E/AndroidRuntime(14964): FATAL EXCEPTION: ModernAsyncTask #4
08-30 13:10:03.310: E/AndroidRuntime(14964): java.lang.RuntimeException: An error occured while executing doInBackground()
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.ModernAsyncTask$3.done(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.lang.Thread.run(Thread.java:856)
08-30 13:10:03.310: E/AndroidRuntime(14964): Caused by: java.lang.NullPointerException
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1094)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.content.ContentResolver.query(ContentResolver.java:354)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.content.ContentResolver.query(ContentResolver.java:313)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.CursorLoader.loadInBackground(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.CursorLoader.loadInBackground(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at android.support.v4.content.ModernAsyncTask$2.call(Unknown Source)
08-30 13:10:03.310: E/AndroidRuntime(14964):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
08-30 13:10:03.310: E/AndroidRuntime(14964):    ... 4 more

Honestly, stacktrace doesn't help because it doesn't contains any reference to my code and also it's a pretty strange issue because, as a said above, both fragments work the first time i launch the application, work properly on the phone layout but crash on my tablet layout after screen rotation.

EDIT:

In both fragments i load data in the method onActivityCreated method simply calling getLoaderManager.initLoader(....);

This is how i created my loaders:

First fragment

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    CursorLoader cursorLoader = new CursorLoader(getActivity(), MyContract.CONTENT_URI,
            null, null, null, null);

    return cursorLoader;
}

second fragment:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    return new CursorLoader(getActivity(), MyContract.CONTENT_URI, null,
            null, null, null);
}

This is the declaration of my CP's content uri:

//MyContract class
public static final Uri CONTENT_URI = Uri.parse("content://" + MyProvider.AUTHORITY + "/items");

//In MyProvider class
public static final String AUTHORITY = "it.rciovati.mypackage.MyProvider"

Upvotes: 2

Views: 4090

Answers (3)

Carl Anderson
Carl Anderson

Reputation: 3476

There was a bug in android.content.ContentResolver.acquireUnstableProvider() that was only fixed in November of 2013. The details of that bug can be seen here:

https://bugzilla.mozilla.org/show_bug.cgi?id=904551

Essentially the function could not handle if the Uri is null and would cause a crash. In order to properly support older Android versions you'll need to either ensure you never pass in a null Uri, or catch the NPE that is thrown.

Upvotes: 3

Yaojin
Yaojin

Reputation: 351

I too have the same problem. Turns out it's a really easy bug to squash. Example based on my code

        Uri content_uri = null;

        if (bundle != null && bundle.containsKey("ID_BUNDLE_KEY")) {
            int word_id_to_query = bundle.getInt(ID_BUNDLE_KEY, 1);
            content_uri = ContentUris.withAppendedId(WordListContentProvider.CONTENT_URI, word_id_to_query);

        }

        cursorLoader = new CursorLoader(getActivity()
                .getApplicationContext(), content_uri,
                projections, SELECTION, SELECTION_ARGS, null);

My mistake was to use "ID_BUNDLE_KEY" as the argument rather than ID_BUNDLE_KEY. The latter being already a final static String variable. Thus bundle.containsKey will always return false. This led to the variable of content_uri containing "null" when passed into the new CurorLoader function. My advice is to verify in your own code that a "null" uri is NOT erringly passed in.

For extra info, line 1094 of the ContentResolver.java in Android 4.1.2 AOSP code is really when the condition of [uri.getscheme() != "content"] returns false, leading to a null provider. It's this null provider that the runtime library is complaining about.

Upvotes: 0

rciovati
rciovati

Reputation: 28063

I solved it. In my layout i have a third fragment that loads other data and there was a wrong check on it.

Upvotes: 0

Related Questions