What is the preferred method of loading dependant queries with the LoaderManager API in Android? As of now the best I could come up with is something along the lines of:
@Override
public void onCreate( Bundle savedInstanceState ) {
getLoaderManager().initLoader( FIRST, null, this );
}
@Override
public void onLoadFinished( Loader<Cursor> loader, Cursor data ) {
switch ( loader.getId() ) {
case FIRST:
Bundle args = new Bundle();
args.putInt( ID, somethingFromData( data ) );
getLoaderManager().restartLoader( SECOND, args, this );
break;
case SECOND:
somethingElseFromData( data );
break;
}
}
This works fine most of the time, but it crashes horribly under one special case. Say I launch a second activity or push a fragment on top of this that modifies the data of FIRST
. Now when I navigate back to the activity/fragment with the code above it first refreshes me with the old data of FIRST
and SECOND
, and since FIRST
initiates SECOND
, SECOND
is reloaded with the new data. Now since FIRST
is changed it is loaded again, which causes yet another load of SECOND
to initiate.
First of all if you count that that sums up to two loads of FIRST
(one old and one new) and three loads of SECOND
(two old and one new), which seams at least a bit wasteful. I don't really mind that, except it is a hassle to debug, but it also seems to me to behave non-deterministically, because you don't know which loads will finish first. Will I end up with the new data for SECOND
if the relation between FIRST
and SECOND
changed, or will I end up with the cached values?
I know that I can mitigate this by keeping score of when to restart the second loader, but there has to be a better way of doing this.
To clarify a bit: The problem is most prominent if rows in FIRST
contains a reference to rows in SECOND
and after the back navigation the row(s) in FIRST
loaded does not point to the same row(s) in SECOND
as before.
There is only one LoaderManager per activity or fragment, but a LoaderManager can manage multiple loaders. To get LoaderManager, call getSupportLoaderManager() from the activity or fragment. To start loading data from a loader, call either initLoader() or restartLoader() .
A CursorLoader is a specialized member of Android's loader framework specifically designed to handle cursors. In a typical implementation, a CursorLoader uses a ContentProvider to run a query against a database, then returns the cursor produced from the ContentProvider back to an activity or fragment.
Loaders are special purpose classes that manage loading and reloading updated data asynchronously in the background using AsyncTask. Introduced in Android 3.0, loaders have these characteristics: They are available to every Activity and Fragment. They provide asynchronous loading of data in the background.
Given that the only thing your first loader does is effectively prepare arguments for your second loader, you should subclass your own AsyncTaskLoader and do the whole operation within one loader.
This article contains a very in-depth example of a custom AsyncTaskLoader, which I'm sure you could adapt to your own needs. You also should look at the CursorLoader source code for a better grasp of how to write your own.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With