Part 2 - Using the Contacts ContentProvider
- PDF for offline use
- Sample Code:
Let us know how you feel about this
Translation Quality
0/250
last updated: 2018-01
Code that uses access data exposed by a ContentProvider
doesn't
require a reference to the ContentProvider
class at all. Instead, a
Uri is used to create a cursor over the data exposed by the
ContentProvider
. Android uses the Uri to search the system for the
application that exposes a ContentProvider
with that identifier. The
Uri is a string, typically in a reverse-DNS format such as
com.android.contacts/data
.
Rather than making developers remember this string, the Android
Contacts provider exposes its metadata in the
android.provider.ContactsContract
class. This class is used to
determine the Uri of the ContentProvider
as well as the names of the
tables and columns that can be queried.
Some data types also require special permission to access. The built-in
contacts list requires the android.permission.READ_CONTACTS
permission in the AndroidManifest.xml file.
There are three ways to create a cursor from the Uri:
ManagedQuery() – The preferred approach in Android 2.3 (API Level 10) and earlier, a
ManagedQuery
returns a cursor and also automatically manages refreshing the data and closing the cursor. This method is deprecated in Android 3.0 (API Level 11).ContentResolver.Query() – Returns an unmanaged cursor, which means it must be refreshed and closed explicitly in code.
CursorLoader().LoadInBackground() – Introduced in Android 3.0 (API Level 11),
CursorLoader
is now the preferred way to consume aContentProvider
.CursorLoader
queries aContentResolver
on a background thread so the UI isn't blocked. This class can be accessed in older versions of Android using the v4 compatibility library.
Each of these methods has the same basic set of inputs:
- Uri – The fully qualified name of the
ContentProvider
. - Projection – Specification of which columns to select for the cursor.
- Selection – Similar to a SQL
WHERE
clause. - SelectionArgs – Parameters to be substituted in the Selection.
- SortOrder – Columns to sort by.
Creating Inputs for a Query
The ContactsProvider
sample code performs a very simple query against
Android's built-in Contacts provider. You do not need to know the
actual Uri or column names - all the information required to query the
Contacts ContentProvider
is available as constants exposed by the
ContactsContract
class.
Regardless of which method is used to retrieve the cursor, these same objects are used as parameters as shown in the ContactsProvider/ContactsAdapter.cs file:
var uri = ContactsContract.Contacts.ContentUri;
string[] projection = {
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.Contacts.InterfaceConsts.PhotoId,
};
For this example, the selection
, selectionArgs
and sortOrder
will be ignored
by setting them to null
.
Creating a Cursor from a Content Provider Uri
Once the parameter objects have been created, they can be used in one of the following three ways:
Using a Managed Query
Applications targeting Android 2.3 (API Level 10) or earlier should use this method:
var cursor = activity.ManagedQuery(uri, projection, null, null, null);
This cursor will be managed by Android so you do not need to close it.
Using ContentResolver
Accessing ContentResolver
directly to get a cursor against a
ContentProvider
can be done like this:
var cursor = activity.ContentResolver(uri, projection, null, null, null);
This cursor is unmanaged, so it must be closed when no longer required. Ensure that the code closes a cursor that is open, otherwise an error will occur.
cursor.Close();
Alternatively, you can call StartManagingCursor()
and
StopManagingCursor()
to 'manage' the cursor. Managed cursors are
automatically deactivated and re-queried when Activities are stopped
and restarted.
Using CursorLoader
Applications built for Android 3.0 (API Level 11) or newer should use this method:
var loader = new CursorLoader (activity, uri, projection, null, null, null);
var cursor = (ICursor)loader.LoadInBackground();
The CursorLoader
ensures that all cursor operations are done on a
background thread, and can intelligently re-use an existing cursor
across activity instances when an activity is restarted (e.g. due to a
configuration change) rather that reload the data again.
Earlier Android versions can also use the CursorLoader
class by using the
v4 support libraries.
Displaying the Cursor Data with a Custom Adapter
To display the contact image we'll use a custom adapter, so that we can
manually resolve the PhotoId
reference to an image file path.
To display data with a custom adapter, the example uses a
CursorLoader
to retrieve all the Contact data into a local collection
in the FillContacts method from
ContactsProvider/ContactsAdapter.cs:
void FillContacts ()
{
var uri = ContactsContract.Contacts.ContentUri;
string[] projection = {
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.Contacts.InterfaceConsts.PhotoId
};
// CursorLoader introduced in Honeycomb (3.0, API11)
var loader = new CursorLoader(activity, uri, projection, null, null, null);
var cursor = (ICursor)loader.LoadInBackground();
contactList = new List<Contact> ();
if (cursor.MoveToFirst ()) {
do {
contactList.Add (new Contact{
Id = cursor.GetLong (cursor.GetColumnIndex (projection [0])),
DisplayName = cursor.GetString (cursor.GetColumnIndex (projection [1])),
PhotoId = cursor.GetString (cursor.GetColumnIndex (projection [2]))
});
} while (cursor.MoveToNext());
}
}
Then implement the BaseAdapter's methods using the contactList
collection. The adapter is implemented just as it would be with any
other collection – there is no special handling here because the
data is sourced from a ContentProvider
:
Activity activity;
public ContactsAdapter (Activity activity)
{
this.activity = activity;
FillContacts ();
}
public override int Count {
get { return contactList.Count; }
}
public override Java.Lang.Object GetItem (int position)
{
return null; // could wrap a Contact in a Java.Lang.Object to return it here if needed
}
public override long GetItemId (int position)
{
return contactList [position].Id;
}
public override View GetView (int position, View convertView, ViewGroup parent)
{
var view = convertView ?? activity.LayoutInflater.Inflate (Resource.Layout.ContactListItem, parent, false);
var contactName = view.FindViewById<TextView> (Resource.Id.ContactName);
var contactImage = view.FindViewById<ImageView> (Resource.Id.ContactImage);
contactName.Text = contactList [position].DisplayName;
if (contactList [position].PhotoId == null) {
contactImage = view.FindViewById<ImageView> (Resource.Id.ContactImage);
contactImage.SetImageResource (Resource.Drawable.ContactImage);
} else {
var contactUri = ContentUris.WithAppendedId (ContactsContract.Contacts.ContentUri, contactList [position].Id);
var contactPhotoUri = Android.Net.Uri.WithAppendedPath (contactUri, Contacts.Photos.ContentDirectory);
contactImage.SetImageURI (contactPhotoUri);
}
return view;
}
The image is displayed (if it exists) using the Uri to the image file on the device. The application looks like this:
Using a similar code pattern, your application can access a wide variety of system data including the user's photos, videos and music. Some data types require special permissions to be requested in the project's AndroidManifest.xml.
Displaying the Cursor Data with a SimpleCursorAdapter
The cursor could also be displayed with a SimpleCursorAdapter
(although only the name will be displayed, not the photo). This code
demonstrates how to use a ContentProvider
with SimpleCursorAdapter
(this code does not appear in the sample):
var uri = ContactsContract.Contacts.ContentUri;
string[] projection = {
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName
};
var loader = new CursorLoader (this, uri, projection, null, null, null);
var cursor = (ICursor)loader.LoadInBackground();
var fromColumns = new string[] {ContactsContract.Contacts.InterfaceConsts.DisplayName};
var toControlIds = new int[] {Android.Resource.Id.Text1};
adapter = new SimpleCursorAdapter (this, Android.Resource.Layout.SimpleListItem1, cursor, fromColumns, toControlsIds);
listView.Adapter = adapter;
Refer to the
ListViews and Adapters
for further information on implementing SimpleCursorAdapter
.
Let us know how you feel about this
Translation Quality
0/250
Xamarin Workbook
If it's not already installed, install the Xamarin Workbooks app first. The workbook file should download automatically, but if it doesn't, just click to start the workbook download manually.