What's the best way to defer a query managed by a Loader?

Go To StackoverFlow.com

3

I am using the LoaderManager to handle a query that is backing a listview. It relies on some parameters that aren't known until later in the process though. Currently I call

getSupportLoaderManager().initLoader(0, null, callback);

within onCreate(). This will immediately create the loader and execute the query, before the parameters are all known. To handle that, I have a dummy query in onCreateLoader() when not all params are known, and a flag in my callback handler that checks within onLoadFinished() whether or not we have a legitimate query. If it's not, I ignore the cursor data. When it comes time to do a real query, I call

getSupportLoaderManager().restartLoader(0, null, callback);

and also set my flag to true, so that onLoadFinshed() handles it properly.

What I'm doing above seems kind of hacky; is there a better way? I originally tried to call initLoader() later, when I first need the query, but then things fail upon orientation changes, similar to this question.

2012-04-04 22:09
by qix
The answer probably is: don't use a Loader. Use something else to move the work to the background, like AsyncTask - CommonsWare 2012-04-04 22:13
I was afraid of that. Although I may be able to still use a loader without this hack by using Fragment-specific LoaderManagers, if I can defer the fragment's creation.. - qix 2012-04-11 08:20
still hacky but you dont need a flag in onLoadFinished if your dummy query returns no rows (SELECT * from sqlite_master LIMIT 0 - thanks matt ;) ) or you have a CursorLoader subclass like EmptyCursorLoader that covers this for yo - Dori 2012-07-10 14:08
This is bothering me too. The sample code in the developer guide uses a loader restart each time the parameters change; it uses a cursor loader, but with an AsyncTaskLoader, the loadInBackground() will have to consult instance state for its query parameters, as you've found. The state model for a loader is then very much messed up. I've ventured a bit into using Square Otto for a simpler, threadsafe async delivery mechanism. That looks promising, but I'm worried that I end up reinventing 2/3ds the loader framework to handle orientation changes, so, yeah - Barend 2012-12-20 23:50


0

Just to close the question, for my use case I was able to rely on using the per-fragment loader managers, instantiating those when I needed them and had the info. If this doesn't fit anyone else's use case, perhaps my original hack is good enough, if you want to avoid writing AsyncTasks and take advantage of some of the built-in functionality of a loader.

2012-04-17 06:50
by qix


0

You can simply call restartLoader without calling initLoader first. Just remove the initLoader call from onCreate, and run restartLoader once you have your params, no flag needed.

2012-04-05 01:18
by ptc
So this actually worked when getSupportLoaderManager().enableDebugLogging(true), but fails when this is false and I do a rotation. When I rotate back, I get the error "Called doRetain when not started: LoaderManager" - qix 2012-04-11 08:17
same for me on ICS - this does not seem to work at presen - Dori 2012-07-10 12:37
@qix: If you are calling initLoader/restartLoader inside onResume instead of onCreate you still need to call getLoaderManager/getSupportedLoaderManager inside onCreate in order to properly initialize the loader manager - Brian Attwell 2015-05-26 09:26


0

I found another, much simpler solution. For some reason you have to call initLoader() in your onCreate(), but because you have to pass LoaderCallbacks every time you call restartLoader() it doesn't really matter, what callbacks you use in this first, dummy call to initLoader(). I have created very simple utility methods:

public static void initEmptyLoaders(Activity activity, int... ids) {
    initEmptyLoaders(activity, activity.getLoaderManager(), ids);
}

public static void initEmptyLoaders(final Context ctx, LoaderManager loaderManager, int... ids) {
    LoaderManager.LoaderCallbacks callbacks = new LoaderManager.LoaderCallbacks() {
        @Override
        public Loader onCreateLoader(int i, Bundle bundle) {
            return new Loader(ctx);
        }

        @Override
        public void onLoadFinished(Loader loader, Object o) {
        }

        @Override
        public void onLoaderReset(Loader loader) {
        }
    };

    for (int id : ids) {
        loaderManager.initLoader(id, null, callbacks);
    }
}

Now I call initEmptyLoaders() in my onCreate() with every loader id I may need later - everything works :-)

2013-01-17 08:04
by broot


0

This is an old issue than still exist in some androids, and the support libraries are more updated. Then use the android.support.v4.* version of the Loader API to fix it.

2013-12-05 07:59
by Daniel De León
Ads