Tips

Androidアプリの4大要素 ContentProvider 【Android Tips】

1
Androidアプリの4大要素とインテント
ContentProvider
この節では、Androidアプリケーションの4大要素のうちの1つ”ContentProvider”を紹介します。ContentProviderは異なるアプリケーション間でデータ共有を行うためのコンポーネントです。同様の機能を持つアプリケーションでそれぞれユーザーデータを管理するのではなく、ユーザーデータをただ1つのアプリケーションに集約することができるという利点があります。
ContentProvider概要
アプリケーションは記録したい情報をファイルシステム(内部保存域)やSQLiteデータベースを用いて保存することができます。これらの情報はアプリケーション毎に独立しているため、他のアプリケーションからは参照する事ができません。
開発するアプリケーションの用途や目的によっては、他のアプリケーションが公開している情報を利用したり、逆に他のアプリケーションにデータを公開することもあるでしょう。その際に用いるのがコンテントプロバイダという仕組みです。

Androidでは標準でいくつかのコンテントプロバイダが公開されています。代表的なものは発信者履歴(CallLog)やコンタクトリスト(Contacts)です。

 

コンテントプロバイダへの問い合わせ(この問い合わせのことをクエリと呼びます。)は、リレーショナルデータベース(RDB)と同じように行えるよう設計されており、カーソルやデータセットといったRDBへアクセスする一般的な考え方でデータを参照することができます。

 

この節では、ContentProvideを用いてデータを参照する方法を紹介します。

ContentProviderを用いたデータ参照
以下にContentProviderを用いて他のアプリケーションからデータを受け取る際の注意点をピックアップしました。


それぞれの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オブジェクトの操作
カーソルはデータベースから取得したデータを表すときに一般的よく使われる考え方です。
プログラム言語によってはデータセットなどと呼ばれることもありますが、基本的な考え方は同じです。
※データベースについては後述します。

 

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()


Androidアプリ開発の必須知識!JAVAプログラミングを学べる連載リンク

はじめてのJAVA 連載

Recent News

Recent Tips

Tag Search