開発するアプリケーションの用途や目的によっては、他のアプリケーションが公開している情報を利用したり、逆に他のアプリケーションにデータを公開することもあるでしょう。その際に用いるのがコンテントプロバイダという仕組みです。
Androidでは標準でいくつかのコンテントプロバイダが公開されています。代表的なものは発信者履歴(CallLog)やコンタクトリスト(Contacts)です。
コンテントプロバイダへの問い合わせ(この問い合わせのことをクエリと呼びます。)は、リレーショナルデータベース(RDB)と同じように行えるよう設計されており、カーソルやデータセットといったRDBへアクセスする一般的な考え方でデータを参照することができます。
この節では、ContentProvideを用いてデータを参照する方法を紹介します。
それぞれのContentProviderはユニークに識別するためのURIを公開しています。
1つのアプリケーションがデータセットを複数公開している場合は、それぞれの別のURIを定義・公開しています。
たとえば、コンタクトリストではそれぞれ別のURIでEmailアドレス帳や電話番号リストを公開しています。
このURIは「content://」という文字列から始まり、その後に完全修飾クラス名が続きます。
データ利用側(クライアント)のプログラムコードをシンプルにするために定数化されていることが多いようです。
以下は、E-mailアドレス帳を表す定数の例です。
android.provider.ContactsContract.CommonDataKinds.Email.CONTENT_URI
ContentProviderでデータを参照したい場合はContentResolverオブジェクトを使い、クライアントとしてデータ提供側のアプリケーションと通信します。
ContentProviderはデータの問い合わせだけでなく、データの追加・変更・削除も行うことができ、それらは全てこのContentResolverオブジェクトを介して行います。
ContentResolverオブジェクトの取得方法は以下の通りです。
ContentResolver resolver = getContentResolver();
ContentProviderへの問い合わせはContentResolverクラスに定義されているquery()メソッドもしくはActivityクラスに定義されているmanagedQuery()メソッドのいずれかを使用します。どちらのメソッドも同じ引数、同じ戻り値(Cursorオブジェクト)を返すようになっています。
この2つのメソッドの違いは、managedQuery()メソッドはCursorオブジェクトのライフサイクルをアクティビティが管理するようになり、アクティビティが一時停止したときにはCursorをアンロードし、アクティビティが再開したときに再問合わせするといった細かい挙動を制御してくれるという点です。
このため、query()メソッドを実行したときに必要となるような、クライアント側で細かくCursorオブジェクトの再読み込みを行うプログラムコードを記述する必要がなくなります。
query()メソッドおよびmanagedQuery()メソッドの引数は以下の通りです。
データ型 | 引数名 | 概要 |
---|---|---|
Uri | uri | ContentProviderを識別するURI |
String[] | projection | 取得するカラムのカラム名のリスト |
String | selection | 取得する行を識別するフィルタ(SQLのWHERE句に相当) |
String[] | selectionArgs | クエリパラメータのリスト |
String | sortOrder | 取得したデータのソート順(SQLのORDER BY句に相当) |
※SQLについては後述します。
では実際の使用例を見てみましょう。
これはCursorオブジェクトを取得するまでの例で、コンタクトリストから名前と電話番号を取得しようとしています。
import android.database.Cursor; import android.provider.Contacts.People; //中略 ContentResolver resolver = getContentResolver(); String[] projection = new String[] { People._ID, People._COUNT, People.NAME, People.NUMBER }; Uri uri = People.CONTENT_URI; Cursor cur = managedQuery(uri, projection, null, null, People.NAME + " ASC");
4行目でContentResolverオブジェクトの取得を行っています。
12行目から16行目でCursorオブジェクトを取得しています。ここでは、managedQuery()メソッドを使用していますね。
プログラム言語によってはデータセットなどと呼ばれることもありますが、基本的な考え方は同じです。
※データベースについては後述します。
Cursorはデータベースと同じようにデータが2次元の表形式となっています。縦軸をカラム(列)、横軸をロウ(行)といいます。
各行は1つの関連したデータを表し、各列には必ずカラム名とデータ型が定義されています。
query()メソッドやmanagedQuery()メソッドの戻り値としての取得したCursorオブジェクトのカラム名、デフォルト順序、データタイプはそれぞれのContentProviderで別個ものとなります。
ただし、すべてのContentProviderは_IDおよび_COUNTカラムを持っており、_IDカラムには各レコードの一意な数値(データベースでいうところの主キー)を、_COUNTカラムには返されたレコードの件数(データベースでいうところのグループ関数COUNTの結果)を保持しています。
Cursorオブジェクトからデータを取得するには以下のように行います。
Cursorオブジェクトには各データ型専用のgetString()、getInt()および、getFloat()、getBlob()といった読み込みメソッドが定義されています。つまり、Cursorオブジェクトからデータを取得するためには、そのカラムのデータ型を知っておく必要があります。
ただし、どのようなデータ型であったとしても、getString()メソッドを使用すればそのデータの文字列表現を取得することができます。
たとえば、「1.23」といったfloat型のデータだったとしても、「”1.23”」といったString型に変換したものを取得することができるようになっています。
実際の使用例は以下のようになります。
if (cur.moveToFirst()) { String name; String phoneNumber; int nameColumn = cur.getColumnIndex(People.NAME); int phoneColumn = cur.getColumnIndex(People.NUMBER); do { name = cur.getString(nameColumn); phoneNumber = cur.getString(phoneColumn); } while (cur.moveToNext()); }
1行目で、moveToFirst()メソッドを使用してCursorオブジェクトの先頭行に移動しています。
仮にクエリ結果が1件もない場合は、このメソッドの実行結果がfalseとなるため、以降の処理は行いません。
5行目、6行目で取得するカラムの列インデックスを取得しています。これは、getColumnIndex()メソッドで取得することができ、引数には列インデックスを取得したいカラム名を指定します。このカラム名は一般的に定数化されていることが多いようです。
8行目から11行目でdo-while文を用いてループしています。
先ほど取得した列インデックスを使用して、データの取得を行っているのが9行目、10行目です。
Cursorオブジェクトの1行をすべて読み込んだら、moveToNext()メソッドを実行しCursorオブジェクトの次の行に移動します。
このメソッドはCursorオブジェクトの末尾行まで読み込みが終わるとfalseを返すため、do-while文のループを抜けます。
データを変更する場合は、Cursorオブジェクトのデータを操作するのではなく、以下のメソッドを用いて行います。
これらのメソッドはContentResolverクラスに定義されているメソッドです。
目的 | ContentResolverクラスのメソッド |
---|---|
新たなレコードを追加する | insert() |
既存のレコードに新たな値を追加する | |
既存のレコードを更新する | update() |
既存のレコードを削除する | delete() |