Preferred method of loading dependent queries with LoaderManager
Asked Answered
B

1

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.

Burgwell answered 6/8, 2015 at 4:0 Comment(5)
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?Orthogenesis
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?Burgwell
Sample of what specifically I'm thinking of: sqlfiddle.com/#!5/3dd28/2/0Burgwell
Some logcat output with exception stacktrace would be helpfulCongratulatory
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.Burgwell
Z
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.

Zworykin answered 10/9, 2015 at 13:20 Comment(7)
Do you mean this one: developer.android.com/reference/android/content/…? Your link is broken. Also it looks like you had the Chinese site or something.Burgwell
Yeah i did! Sorry! When I google the android stuff the chinese site comes up.. literally no idea whyZworykin
For some reason it does that for me as well. I think Google done goofed with their language settings. =)Burgwell
This AsyncTaskLoader solution; Will it automatically load new results if the database is updated like LoaderManager does?Burgwell
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 allZworykin
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)Zworykin
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. =DBurgwell

© 2022 - 2024 — McMap. All rights reserved.