Introduction To Loading Data Asynchronously in Android
Loaders are an Android construct that make it easy to load data in an activity or a fragment in an asynchronous manner.
Loaders can be bound to a data source and, when content changes in the data source, they can deliver refreshed results. Additionally, they keep the cursor context of a connection and, when they reconnect, they do not need to re-query the data.
Basics of Android Loaders
Each Activity or Fragment can have at most one LoaderManager class that is used to manage Loaders’ instances. Typically, to use loaders in an Android application, we need the following:
- Activity or Fragment class
- Instance of LoaderManager
- CursorLoader class to load data
- Widget to display loader’s data
- Data source
Hands On with Android Loaders
Let’s create a simple Android application that demonstrates working with Loaders.
Fire up Android Studio and Start a new Android Studio Project.
Figure 1: Opening Android Studio
Provide LoaderDemo as the Application Name and click Next.
Figure 2: Naming the Application
On the next screen, leave the default values and click Next.
Figure 3: Leaving the default values in place
On the “Add an activity to Mobile” page, choose “Blank Activity”. This creates an application with a single activity.
Figure 4: Adding a Blank Activity
We then are prompted to customize the activity. We will leave the default values unchanged.
Figure 5: Again, leaving the default values in place
Click Finish to create the project files.
First, we declare the capability to read contacts in an application manifest.
//AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns_android="http://schemas.android.com/apk/res/android"
package="com.example.vipul.loaderdemo" >
<uses-permission android_name="android.permission.READ_CONTACTS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android_name="android.intent.action.MAIN" />
<category android_name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
We then update the layout file to add a listview.
//activitymain.xml <RelativeLayout xmlns_android= "http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/listView" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="122dp" /> </RelativeLayout>
Open MainActivity.java and include a few imports.
//MainActivity.java import android.app.ListActivity; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.app.LoaderManager.LoaderCallbacks; import android.widget.ListView; import android.widget.SimpleCursorAdapter;
Next, we update the class declaration to extend LoaderCallbacks.
public class MainActivity extends ListActivity implements LoaderCallbacks<Cursor> {
We then add a few member variables.
CursorLoader myCursorLoader; String currentFilter; SimpleCursorAdapter myDisplayAdapter; ListView yourListView = getListView(); static final String[] CONTACTS_SUMMARY_PROJECTION = new String[]{ Contacts._ID, Contacts.DISPLAY_NAME };
Next, we update the default code in the onCreate method to intiialize the loadermanager:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); yourListView = getListView(); myDisplayAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, new String[] { Contacts.DISPLAY_NAME}, new int[] { android.R.id.text1}, 0); setListAdapter(myDisplayAdapter); getLoaderManager().initLoader(0, null, this); }
Lastly, we implement the callback methods for the loader.
// This method is called when initLoader is invoked. // In this method, we create the CursorLoader @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { // Create a cursor // uri: The URI for the content to retrieve. // projection: A list of which columns to return. // Passing null will return all columns, // which is inefficient. // selection: A filter declaring which rows to return, // formatted as an SQL WHERE clause (excluding the WHERE itself). // Passing null will return all rows for the given URI. // selectionArgs: You may include ?s in the selection, // which will be replaced by the values from selectionArgs, // in the order that they appear in the selection. // The values will be bound as Strings. // sortOrder: How to order the rows, formatted as an SQL ORDER BY // clause (excluding the ORDER BY itself). Passing null will use // the default sort order, which may be unordered. Uri baseUri; if (currentFilter != null) { baseUri = Uri.withAppendedPath (ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(currentFilter)); } else { baseUri = ContactsContract.Contacts.CONTENT_FILTER_URI; } String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; myCursorLoader = new CursorLoader(this, baseUri,CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " ASC"); return myCursorLoader; }
On the onLoadFinished callback, we swap the cursor with the data.
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { myDisplayAdapter.swapCursor(data); }
In the onLoaderReset callback, we set the swap to null.
@Override public void onLoaderReset(Loader<Cursor> loader) { myDisplayAdapter.swapCursor(null); }
Our application is now complete. When you run it, you will see the loaders in action.
Summary
In this article, we learned how to use loaders to get data loaded asynchronously in Android Applications. I hope you have found this information useful.
Resources
- http://developer.android.com/guide/components/loaders.html
- http://www.grokkingandroid.com/using-loaders-in-android/
About the Author
Vipul Patel is a technology geek based in Seattle. He can be reached at vipul.patel@hotmail.com. You can visit his LinkedIn profile at https://www.linkedin.com/pub/vipul-patel/6/675/508.