Question

I have a scenario where I am trying to use Loaders to load data from my SQLite db. There's two things I need to do:

  1. I need to get a cursor that represents all goals (a.k.a. plans)
  2. For each goal, I need to get another cursor that represents the payments toward that specific goal

It's easy to get a cursor that basically selects all the goals from the db, however, how do I add the second layer where I obtain a cursor for the payments made towards each goal (payments are stored in a separate Payments db, each payment is a record with a parent id for the goal to link it)?

I was thinking of doing everything in the Loader's onLoadFinished as such:

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    switch (loader.getId()) {
        case LOADER_PLANS:
            mListPlans.clear();

            while (cursor.moveToNext()) {
                mListPlans.add(new Plan(cursor));
                mListPlanPos = mListPlans.size() - 1;
                getLoaderManager().restartLoader(LOADER_PAYMENTS, null, this);
            }

            mAdapter.notifyDataSetChanged();
            break;
        case LOADER_PAYMENTS:
            mListPlans.get(mListPlanPos).setPayments(cursor);
            break;
        default:
            break;
    } }

Please ignore the added complexity that I'm storing everything from the db into an ArrayList.

Obviously the above code doesn't work, and I think it's totally the wrong approach. I can't figure out how to do a "multi-layer" Loader such as this, any push in the right direction is appreciated.

Thanks!

Was it helpful?

Solution

Ok, I'll answer my own question if someone else comes across this let's say, "multi-instance" Loader situation. I got it to work as intended, but I had to change my approach in a couple key ways.

So, the above code is actually pretty close to what I used, here's the resulting code that works perfectly:

 @Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    switch (loader.getId()) {
        case LOADER_PLANS:
            mListPlans.clear();

            while (cursor.moveToNext()) {
                mListPlans.add(new Plan(cursor));
            }

            getLoaderManager().restartLoader(LOADER_PAYMENTS, null, this);

            break;
        case LOADER_PAYMENTS:
            for (Plan plan : mListPlans) {
                plan.setPayments(cursor);
            }

            // Notify the adapter that we've updated it's data set mListPlans.
            mAdapter.notifyDataSetChanged();

I haven't included the rest of the onLoadFinished() callback, it's not necessary for this demo. So, as you can see, there's no more looping of the Loader itself, instead, I've changed my logic to loop through each plan in my ArrayList in the Payment instance of the Loader. The Plans instance still loads the Payments instance, but just once each run.

For this to work, I changed the logic of the Payments query to grab all payments, and then I use my data Object's logic to add the right payments from the cursor to itself. This not only results in more straight-forward code, but it's a lot faster. Instead of multiple queries to slow internal storage (the DB), I'm putting the Cursor in much faster memory and running through it with loops.

I was pleasantly surprised by how clean this solution is versus the old way I used to query the DbHelper directly and use startManagingCursor() to well, ya know. The problem with the old way was Deprecated methods as well, I'd get ANRs for loading cursors on the UI thread. This solution fixes both of those, and it also structures my logic in a much more "detached" way, I'm no longer relying on the order of my code's execution, using the callbacks I have much more control over all data related aspects as I know exactly when and where the UI will be updated.

Loaders, especially when I start to combine them with ContentProviders, I'm sure will be a eurka! moment close to that of when I learned to convert my apps to using Fragments (also well worth the climb, still learning of course).

All in all, keep at it, I almost gave up here folks...

Thanks!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top