The most common way for apps to share structured data is to use content providers. These providers present data in a table-like form for easy consumption.
If we are targeting an Android version earlier than 3.0, we will need to add the Xamarin Android Support v4 NuGet package.
Content providers give us a way to access data from other apps and services on the device. Here, we will view the contacts through an Android content provider. Let's take a look at the following steps:
[assembly: UsesPermission( Manifest.Permission.ReadContacts)]
string uri = ContactsContract.Contacts.ContentUri;
string[] projection = { ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.Contacts.InterfaceConsts.DisplayName };
e
in their name:string filter = string.Format("{0} like ?", ContactsContract.Contacts.InterfaceConsts.DisplayName); string[] filterArgs = { "%e%" };
string sort = ContactsContract.Contacts.InterfaceConsts.DisplayName;
CursorLoader
type to start the connection:var loader = new CursorLoader( this, uri, projection, filter, filterArgs, sort);
var cursor = (ICursor)loader.LoadInBackground();
if (cursor.MoveToFirst()) { int idIdx = cursor.GetColumnIndex(projection[0]); int nameIdx = cursor.GetColumnIndex(projection[1]); do { long id = cursor.GetLong(idIdx); string name = cursor.GetString(nameIdx); } while (cursor.MoveToNext()); }
cursor.Close();
Using a ContentProvider
type allows us to access data from both a central repository as well as access data from other apps or services installed on the device. Content providers offer a consistent and uniform interface for accessing data from any provider. They make accessing any data type or structure similar to accessing tables from a database.
An app can access data from a content provider through a CursorLoader
or ContentResolver
type. Both allow data queries, but a content resolver has additional operations for inserting, updating, and deleting, and the loader supports background threads.
The resolver's Query()
method, or the loader's constructor, accepts several parameters that provide the means to obtain data. The first parameter is the URI that determines which and what content provider to access. The URI identifies the provider, or the authority, to be accessed and the particular data in the provider or the path.
The next parameter is the projection, or the columns, to be returned in the resulting query. This consists of a collection of the column names.
The next two relate to the where
clause or the filter aspect. This consists of two parts, the first being the actual textual statement and the second being the collection of parameter values. The where
clause should use a parameter—the ?
character—which will be substituted with the appropriate value from the array when the query is executed.
The final parameter is the column used to sort the resulting data and the name of the column we wish to sort by.
In order to obtain the values from the result, we can make use of the several getter methods, such as GetString()
or GetLong()
. There is also the GetColumnIndex()
method, which allows us to find columns by name instead of position.
Using a CursorLoader
allows us to make requests on a separate thread to the UI (however, in this example we don't do that). We can also use the ContentResolver
property on the Context
type. This gives us access to more actions, such as insert, update, and delete; however, these operations occur on the UI thread.
In order to delete data, we construct a where
clause and pass in an array of values to the Delete()
method, as we would when filtering the query. The items returned in the filter will be removed.
In order to insert data, we pass a set of key-value pairs via a ContentValues
object. Items are added via the Put()
method with the key being the column name.
Updating data is simply a combination of the delete and insert aspects. We pass a query and arguments along with the collection of values to the Update()
method. The items returned by the filter are updated with the values provided.