Search code examples
androidandroid-activityandroid-sqliteandroid-loadermanagerandroid-loader

using Loader with a Fragment/Activity with UI widgets triggering updates to the loaders content


I'm using for a while the Loaders API's. I found it great solution and most elegant design to solve the generic problem of most apps: displaying "processed" dynamic content with minimum interference to the Fragment/Activity - the responsibility of loading the data and all the logic behind that is encapsulated within the Loader...

the problem starts when the UI initialized by the data returned from the loader, triggers change in the same data it initialized with.

to make it clearer, let's say that our main Activity or Fragment displays endless list of posts (like Facebook or Google+ app). Let's assume this app uses some kind of AsyncTaskLoader or a CusorLoader:

the ListView displaying the list of posts will be initialized with the loaders data at the right time of the Activity life-cycle.

now let's say that meanwhile a GCM push received, and triggered update to the data source. still everything is good: the loader will update the activity with the data change.

things are getting messy on the following scansion:

1) user clicked on the "Like" button of one of the posts

2) background task / service starts network operation to update the post status on server

3) UI should change immediately (adapter notify data set changed) without waiting to network response because the user have to know that something happens (might be also not a full screen blocking progress bar)

4) network request completed (not necessarily succeed)

in the following scansion things getting very fast out of control in all aspects:

  • same widgets that suppose to be updated in exclusive responsibility of the loaders, are forced to be updated locally to reflect temporary state that not necessarily will be the "final" state of the data source (e.g - if the network response will fail from all kind of reasons..)

  • now let's say that the network operation did not completed yet and still inflight, and from another reason (GCM Push / other background operation that was in the middle of execution) the Loader reload and notify the activity there is new data. now it's already very uncomfortable situation: there is a new data, but the UI still in the mode I described earlier. I found myself implementing logic that resolving such conflicts and decides whether the UI need to react to the loader data source or not.

  • now multiple this problem on the amount of actions you can do on posts, and the amount of posts.

from this kind of reasons, my conclusion is that Loader is very problematic solution when the data-source can change a lot from a "writing" operations triggered from the same UI initialized by this data source.

but, from the other hand, I don't have in sight any other solution to handle in an elegant design the problem I described, although it's a very common situation.

My question basically is:

  • Are there any guidelines about how to use loaders under this kind of scenarios as I described?

  • Is there a better solution / API components or design I'm not thinking of that addresses such requirements?

thanks in advance,


Solution

  • It is not the case that Loader is very problematic solution. Rather, web-service calls and push notifications are asynchronous, and the problem of integrating the result data delivered by these into the existing data set (and managing it within the Activity / Fragment life-cycle) is a complex situation which the framework has tried to simplify and generalize as far as possible. These generalizations are the LoaderManager, ContentProvider and SyncAdapter classes and their associated techniques.

    Without having seen any of your existing code or proposing any other advanced solutions, here is the simplest thing I can urge you to try (if you haven't already tried it): whenever a web-service call completes or a notification is received, call either

    Loader.reset();
    

    or

    LoaderManager.restartLoader();
    

    and make sure you clear your Adapters as

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
    
        // clear your adapters here
        listview.setAdapter(null);
        viewpager.setAdapter(null);
    }
    

    This loads the newly added data into the view. It is the programmer's responsibility to refresh the data set when an asynchronous task completes.