Saturday, January 25, 2014

Learning about Android Loaders: AsyncTaskLoader

Android introduced Loaders in 3.0 to ease asynchronous tasks on Activity and Fragments. It monitor the source of the data and automatically deliver new result when content changes. It's primary usage is to work with a database utilizing the CursorLoader, but it can also be used to do other asynchronous tasks. I want to take a look at how Loaders could be used as a replacement for AsyncTask.

The issue with AsyncTask is well known. If you rotate the device, you have to cancel the AsyncTask or the app will crash. Loaders handles rotation better, it automatically reconnect to the last loader’s cursor when being recreated after a configuration change avoiding the need to re-query their data.

There is an instance of LoaderManager in every Activity and Fragment. You simply call getLoaderManager(). Note that if you're using the support library, you will call getSupportLoaderManager() instead.

To do the actual task, create a class that extends the AsyncTaskLoader and implement the loadInBackground method. The return type will be whatever data you want to send back to the main app. In my example, the work is just to sleep for some time. And my return value is just how many milliseconds I slept.

To trigger the work, the first thing to do is to implement the LoaderCallbacks interface in your Activity or Fragment. You can then call initLoader in the Activity's onCreate method. In my test app, I'm actually going to start the task after clicking a button, so I call initLoader() and forceLoad(). The onCreateLoader method will then be triggered. Notice that it uses an integer ID to identify the Loader. I'm not sure, but my guess is that typically you only have one Loader to handle all the asynchronously tasks for an Activity or Fragment.

Finally, to get feedback on the UI, the onLoadFinished method will be called in the UI thread where you can show the result. In my example, I'm simply making a Toast message, but you could imagine where we can update a table with rows of values or show a text.

Note that while AsyncTask has onProgressUpdate(Integer progress) to show how much data has been fetched, a Loader doesn't have this capability out of the box.

I still have a lot to learn about Loaders after this short prototype. So far, it seems like a lot of work to avoid canceling and restarting tasks. If data fetch performance is a concern, which is always the case to a certain degree, then I would consider using AsyncTaskLoader instead of AsyncTask. But there are alternate solutions, such as RoboSpice, that maybe a better solution. My guess is that Loaders are much more effective when dealing with database, which I intent to try out soon.

1 comment: