How to use Android CursorLoader - with Example


              A CursorLoader is normally used to interact with a ContentProvider asynchronously. It can query in the background against a ContentProvider so the UI thread can continuously interact with the user. This avoids the UI thread being un-responsive while querying ContentProvider. The CursorLoader will initiate the query in separate thread, retrieve the results from ContentProvider and reconnects to the Activity when it is finished.

How to use CursorLoader?

LoaderCallbacks<Cursor> interface is used to create a CursorLoader. This interface provides set of callback methods to initialize, run a CursorLoader.
The activity should implement this interface to use the CursorLoader.
Example:

public class MainActivity extends FragmentActivity implements
  LoaderManager.LoaderCallbacks<Cursor> {
    .
    .
}

To initialize a background query we have to use LoaderManager.initLoader() method. This will initiate the background tasks. To get LoaderManager in a FragmentActivity getSupportLoaderManager() is used.
Example:
getSupportLoaderManager().initLoader(1, null, this);

Once we initialize the query, onCreateLoader() method will be invoked by Android. In this method we can create our CursorLoader against the ContentProvider. We can provide Projections, Selection criteria, Sort order during the creation of CursorLoader.
Example:

@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
 Uri CONTENT_URI = ContactsContract.RawContacts.CONTENT_URI;
 return new CursorLoader(this, CONTENT_URI, null, null, null, null);
}

Once we are done with creation of CursorLoader, the ContentProvider URI specified in the CursorLoader object will be queried. All the projections, selections, sort order will be applied to query the ContentProvider (if specified any).
Once the background thread completes the query, onLoadFinished() method will be invoked.
onLoadFinished() is another callback method which will give us a Cursor with result data from the query. We can use this cursor to get the data and do some processing to display it to the user.

onLoaderReset() method is invoked when the cursor becomes stale/invalid. If the Cursor is being placed in a CursorAdapter, you should use the swapCursor(null) method to remove any references it has to the Loader's data.

Sample Application:

In this sample application we are using CursorLoader to query Contacts Content provider. To run this on emulator we are inserting a new contact from code and querying the same using CursorLoader. 
Note: To read contacts we need to have READ_CONTACTS permission. To write a new contact we need to have WRITE_CONTACTS permission.

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cursorloaderexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <uses-permission android:name="android.permission.READ_CONTACTS" >
    </uses-permission>
    <uses-permission android:name="android.permission.WRITE_CONTACTS" >
    </uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.cursorloaderexample.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>


Layout xml file:

<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"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/res"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="34dp"
        android:layout_marginTop="22dp" />

</RelativeLayout>


Activity class:

package com.example.cursorloaderexample;

import java.util.ArrayList;

import android.content.ContentProviderOperation;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends FragmentActivity implements
  LoaderManager.LoaderCallbacks<Cursor> {
 TextView resTextView = null;

 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  resTextView = (TextView) findViewById(R.id.res);
  ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
  int rawContactInsertIndex = ops.size();
  ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
    .withValue(RawContacts.ACCOUNT_TYPE, "Test")
    .withValue(RawContacts.ACCOUNT_NAME, "CTE").build());

  ops.add(ContentProviderOperation
    .newInsert(Data.CONTENT_URI)
    .withValueBackReference(Data.RAW_CONTACT_ID,
      rawContactInsertIndex)
    .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
    .withValue(StructuredName.DISPLAY_NAME, "test name")
    .withValue(Phone.NUMBER, 1234567890).build());
  try {
     //Use below statement to insert a new contact
     //getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
  } catch (Exception e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
  }
  getSupportLoaderManager().initLoader(1, null, this);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.activity_main, menu);
  return true;
 }

 @Override
 public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
  Uri CONTENT_URI = ContactsContract.RawContacts.CONTENT_URI;
  return new CursorLoader(this, CONTENT_URI, null, null, null, null);
 }

 @Override
 public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
  cursor.moveToFirst();
  StringBuilder res = new StringBuilder();
  while (!cursor.isAfterLast()) {
   res.append("\n" + cursor.getString(21) + "-" + cursor.getString(22));

   cursor.moveToNext();
  }
  resTextView.setText(res);

 }

 @Override
 public void onLoaderReset(Loader<Cursor> loader) {
  // If the Cursor is being placed in a CursorAdapter, you should use the
  // swapCursor(null) method to remove any references it has to the
  // Loader's data.
 }

 @Override
 protected void onDestroy() {
  // TODO Auto-generated method stub
  super.onDestroy();
 }

}


Output Screenshot:




Source code of this application:
CursorLoaderExample.zip





Reactions:

0 comments :

Post a Comment