12

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.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Andreas Hagen
  • 2,335
  • 2
  • 19
  • 34
  • Not knowing what your data looks like, but it smells like a database view might help and allow you to use just a single loader? – Ilan Klinghofer Aug 19 '15 at 01:16
  • Would that not be the same thing as a subquery, join, etc, and is that updated the same way as other queries? I.e. `SELECT first.id, second.id FROM first JOIN second ON first.id = second.first`, where first or second is edited, will i get a new update from `LoaderManager`? – Andreas Hagen Aug 19 '15 at 03:26
  • Sample of what specifically I'm thinking of: http://sqlfiddle.com/#!5/3dd28/2/0 – Andreas Hagen Aug 19 '15 at 03:28
  • Some logcat output with exception stacktrace would be helpful – Urizev Sep 08 '15 at 22:41
  • How? This is not a segfault or similar, neither is there any useful information in logcat for this. I am asking for the best known approach to solve the problem, not for help with a crash. – Andreas Hagen Sep 09 '15 at 01:53

1 Answers1

1

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.

JoeyJubb
  • 2,341
  • 1
  • 12
  • 19
  • Do you mean this one: http://developer.android.com/reference/android/content/AsyncTaskLoader.html? Your link is broken. Also it looks like you had the Chinese site or something. – Andreas Hagen Sep 10 '15 at 13:41
  • Yeah i did! Sorry! When I google the android stuff the chinese site comes up.. literally no idea why – JoeyJubb Sep 10 '15 at 13:46
  • For some reason it does that for me as well. I think Google done goofed with their language settings. =) – Andreas Hagen Sep 10 '15 at 13:47
  • This AsyncTaskLoader solution; Will it automatically load new results if the database is updated like LoaderManager does? – Andreas Hagen Sep 10 '15 at 13:49
  • All the way in the bottom right of those pages are the language options, but still don't know how to fix that particular problem for once and for all – JoeyJubb Sep 10 '15 at 13:50
  • Yes! If you ook at the CursorLoader code, you'll see a registerContentObserver method that you'll need to implement as well. (Given that your loader will likely be loading one cursor then another, you'll have to decide if you want to watch one, the other or both) – JoeyJubb Sep 10 '15 at 13:51
  • 1
    Defiantly want to watch both. =) Thanks. I will try out this solution next time I work on the problem (probably in like a couple of weeks, so busy with other things at the moment ^^,) and if it works I'll accept your answer if nothing better comes along before that. =D – Andreas Hagen Sep 10 '15 at 13:53