コンテンツプロバイダ
SQLiteデータベースのデータ等の、アプリケーションが管理しているリソースは、通常は他のアプリケーションから使う事はできません。
しかし、住所録などのデータを一元的に管理して、いろいろなアプリケーションから利用できると便利です。
アンドロイドのコンポーネントの1つであるコンテンツ プロバイダの実装により、 アプリケーションの管理しているリソースを、他のアプリケーションから利用する事ができます。
以前に、「ListViewとListActivity(3)-応用編」の「SimpleCursorAdapterクラス-コンテンツプロパイダよりデータを取得してリスト表示する。」で アンドロイドの「連絡先」データを、アンドロイドにはじめから用意されているコンテンツプロパイダーを利用してアクセスして、 ListViewウィジェットに表示する例を紹介しました。
コンテンツプロパイダで管理するデータとしてよく扱われるのは、SQLiteデータベースのデータです。
コンテンツプロパイダによるデータのアクセス方法も、SQLiteデータベースへのアクセスとよく似ています。
コンテンツプロパイダでどのような事ができるのかを説明する前に、実際に自前のコンテンツプロパイダを実装してみます。
コンテンツプロパイダの実装方法については、Android Developersのサンプルプログラム NotePadの、 NotePadProvider.javaのコードが参考になります。
NotePadProvider.javaのコードを参考に、 「SQLiteでデータベース」の記事で扱った、 人の名前と年令の列からなる簡単なSQLiteのテーブル「person_table」を管理するコンテンツプロパイダを実装してみます。
コンテンツプロバイダのデータ定義情報を示すクラスの作成
まず、他のアプリケーションから「person_table」データをアクセスできるように、 コンテンツプロバイダのデータ定義情報を記述したクラスPersonsを作成します。
リスト1-プロパイダ情報定義クラス(Persons.java)
コンテンツプロバイダのデータ定義情報を示すクラスには、BaseColumnインターフェースをimplementsします。
コンテンツプロバイダのデータには、データ行を識別するためののキーとなる「_id」列が必須です。
BaseColumnインターフェースには、「_id」列を示す_ID定数が定義されています。
AUTHORITY定数(7行目)は、コンテンツプロバイダを識別するための文字列です。
通常、この文字列には、コンテンツプロバイダクラスの完全修飾名 を小文字にしたものを使います。
CONTENT_URI定数は、コンテンツプロパイダの「person_table」のデータを示す「コンテキストURI」の、Uriオブジェクトを定義しています。
コンテンツプロパイダのコンテキストURIについては後述の「コンテントURIとUriMatcher」で説明します。
コンテンツプロバイダオブジェクトは、getTypeメソッドでプロバイダのデータタイプを示す文字列を返す必要があります。
ここでは、データタイプを示す文字列といして、全行のデータを返す時のデータタイプを示すCONTENT_TYPE定数と、 特定の行を返すことを示すCONTENT_ITEM_TYPE定数を定義しています。
(このデータタイプを示す文字列の値は、サンプルプログラムNotePadを参考にして適当に決めましたが、もしかしたら、違う文字列の方が良いのかも?)
13行目と14行目のNAME定数とAGE定数は、プロパイダが返すデータの列名を示す文字列を定義しています。
データベースにアクセスするためのヘルパークラスの作成
次にSQLiteデータベースにアクセスするための、ヘルパークラスを作成します。
リスト2-ヘルパークラス(PersonOpenHelper.java)
このコードは、「SQLiteでデータベース」のリスト1(1)とほぼ同じものです。
変更点としては、テーブルの列名の指定に、リスト1のPersonsクラスで定義した定数を、使っている事です。
コンテンツプロパイダクラスの実装
コンテンツプロパイダを実装するには、ContentProviderクラスを継承したクラスを作成します。リスト3-コンテンツプロパイダ(PersonProvider.java)
クラスのスタティックイニシャライザでの処理
25~34行目で、スタティックイニシャライザを使って、静的変数uriMatcherとpersonProjectionMapを初期化しています。
「スタティックイニシャライザ」については、Javaの文法の「4.5.1 静的初期化子」を参照して下さい。
uriMatcher変数については、次の「コンテントURIとUriMatcher」で説明します。
personProjectionMap変数については、queryメソッドのところで説明します。
コンテントURIとUriMatcher
コンテンツプロパイダは、データをURI(Uniform Resource Identifier)によりツリー構造で管理します。
コンテンツプロパイダを操作するには、「コンテンツプロバイダを識別するための文字列」に、データの種類を特定するためのパスを追加した、「コンテントURI」を指定します。
リスト3のコンテンツプロパイダPersonProviderの場合は、パスが「/persons」と指定された場合に、対象のデータとしてperson_tableの全行のデータを指定した事になります。
パスが、「/persons/数値」と指定された場合には、person_tableの _ID列の値が数値と一致する行のみを指定した事になります。
例えば、パスが「persons/2」と指定された場合に、person_tableの _id列の値が2、すなわち、name列の値が「遠藤 保仁」の行のみを指定した事になります。
「コンテントURI」は、WebページのURLの指定のしかたと同じです。
WebページのURLでは「http://Webサイトのホスト名/Webページを示すパス名」となりますが、 「コンテントURI」では「content://コンテンツプロバイダを識別するための文字列/データの種類を特定するためのパス」を指定する事になります。
「コンテントURI」より、データの種類を特定するためのパスに何が指定されているか判断するには、UriMatcherオブジェクトを使います。
UriMatcherを使うには、 まず、UriMatcherオブジェクトを作成して、そのUriMatcherオブジェクトにaddURIメソッドを使ってデータの種類を特定するためのパスのパターン を登録します。
addURIメソッドの第1引数には、コンテンツプロバイダを識別するための文字列を指定します。
第2引数には、データの種類を示すパスのパターンを指定します。
第3引数には、パスがコンテントURIとマッチした時に返される、int型の値を指定します。
以下は、リスト3のUriMatcherオブジェクトの定義部分です。
28行目のaddURIメソッドの第2引数の「persons/#」は、「persons/数値」とマッチするパスをUriMatcherオブジェクトに登録する事を意味しています。Persons.AUTHORITY定数には、リスト1の7行目で、「gudon.sample.personprovider.personprovider」が代入されています。
リスト3の20行目と21行目で、PERSONS定数には1が、PERSON_ID定数には2が代入されています。
コンテントURIが、どのパスとマッチしているか調べるには、matchメソッドを使います。
matchメソッドの引数には、コンテントURIを指定します。
このURIが、addURIメソッドで登録したパスに一致した場合には、addURIメソッドの第3引数で指定した値が返されます。
以下は、コンテントURIがどのパスとマッチしているかを判断するためのコードの例です。
ContentProviderクラスのメソッド
ContentProviderクラスを継承したクラスはonCreate.query,insert,update,delete,getTypeの5つのメソッドを実装する必要があります。
リスト3の、これらのメソッドの処理をみていきます。
onCreateメソッド
onCreateメソッドでは、ヘルパークラスを後で使うために、ヘルパークラスのオブジェクトを生成して、 インスタンス変数personOpenHelperに代入しているだけです。
queryメソッド
queryメソッドは、コンテンツプロパイダに問い合わせ要求があった場合によびだされます。
このメソッドで、問い合わせ条件にマッチしたデータを抽出して返します。
queryメソッドでは、SQLiteQueryBuilderオブジェクトを生成して、変数qbに代入しています。
SQLiteQueryBuilderは、SQLのSELECT文による問い合わせをおこなうためのオブジェクトです。
リスト3の46行目のsetTablesメソッドで、問い合わせ対象のテーブル名を指定しています。
48行目からのswitch文での処理で、 コンテントURIのパスが「persons/数値」の場合に、 appendWhereメソッドを使って_id列の値が一致する行のみを抽出しています。
ここで、50行目と53行目のsetProjectionMapメソッドは、問い合わせ結果の列を別名にマップするための、列名のペアをHahオブジェクトで指定します。
SELECT文でASにより、列名に別名をつけるのに相当します。
引数に指定されているpersonProjectionMapには、スタティックイニジャライザで、別名としてもともとの列名と同じ名前を登録しています。
(同じ名前を登録しているので、setProjectionMapメソッドは無くてもかまわないのですが、テーブルの列に別名をつける場合のサンプルコードとして、あえて使っています。)
61行目の、SQLiteQueryBuilderオブジェクトのqueryメソッドは、問い合わせを実行するためのメソッドで、 使い方はSQLiteDatabaseオブジェクトのqueryメソッドとほとんど同じです。
64行目のsetNotificationUriメソッドは、コンテンツプロパイダの状態の変更を通知するためのメソッドのようです。
サンプルプログラムNotePadProvider.javaのコードで使われていたので、理解できていませんがそのまま入れてあります。
insertメソッド
insertメソッドは、コンテンツプロパイダにデータを挿入する時に呼び出されるメソッドです。
このメソッドは、ただ単にSQLiteDatabaseオブジェクトのinsertメソッドを間接的に呼び出してます。
また、74~76行目で、ContentValuesオブジェクトに列データが指定されていない場合の処理の例として、「name」列のデータが指定されていない場合に、デフォルトのデータ「詠み人知らず」を指定するようにしています。insertメソッドは、挿入されたデータのコンテントURIを返す必要があるため、データの挿入が成功した場合には、ContentUris.withAppendedIdメソッドを使って、 挿入したデータのUriオブジェクトを作成しています(80~84行目)。
83行目のnotifyChangeメソッドは、 queryメソッドと同様、コンテンツプロパイダの状態の変更を通知するため処理のようで、 サンプルプログラムNotePadProvider.javaのコードで使われていたコードをそのまま入れてあります。
updateメソッド
コンテンツプロパイダのデータを、更新する時に呼び出されるメソッドです。
これも、ただ単に、SQLiteDatabaseオブジェクトのupdateメソッドを、間接的に呼び出しているだけです。
queryメソッドと同様、switch文での処理で、コンテントURIのパスが「persons/数値」の場合に、Where条件で _id列の値が一致する行のみを更新するようにしています。
deleteメソッド
コンテンツプロパイダのデータを、削除する時に呼び出されるメソッドです。
これも、ただ単に、SQLiteDatabaseオブジェクトのdeleteメソッドを、間接的に呼び出しているだけです。
他のメソッドと同様、コンテントURIのパスが「persons/数値」の場合に、Where条件で _id列の値が一致する行を削除するようにしています。
getTypeメソッド
データの種類を示す文字列を返します。
ここでは、コンテントURIのパスに対応した、リスト1のPersonsクラスで定義されている定数を返しています。
アクティビティからコンテンツプロパイダを操作
アクティビティから、コンテンツプロパイダを操作するコードを以下に示します。
このプログラムは、コンテンツプロパイダ・アプリケーション内のアクティビティでも、他のアプリケーションのアクティビティからでも、同じように動作します。
リスト4-コンテンツプロパイダを利用するアクティビティクラス(PersonProviderTest.java)
このプログラムの内容については、後述の「コンテンツ プロバイダの操作」で、説明します。
マニュフェストファイル
アンドロイドがコンテンツプロパイダを認識するためには、マニュフェストファイルにproviderタグを追加する必要があります。
providerタグのname属性には、コンテンツプロパイダのクラス名を指定します。
authorities属性には、コンテンツプロバイダを識別するための文字列(リスト1のAUTHORITY定数の値)を指定します。
リスト5-マニュフェストファイル(AndroidManifest.xml)
コンテンツ プロバイダの操作
リスト4のプログラムを実行すると、以下の図のような画面が表示されます。
リスト4のコードをもとに、コンテンツ プロバイダの操作について説明します
コンテンツ プロバイダを使うには、ContextオブジェクトのgetContentResolverメソッドを使って、ContentResolverオブジェクトを取得します(リスト4・22行目)。
次に、コンテントURIを引数に指定して、このContentResolverオブジェクトのメソッドを使って、コンテンツ プロバイダを操作します。
25行目~36行目は、insertメソッドを使ってコンテンツ プロバイダに、データを挿入する場合の例です。
「田中 達也」と「中村 憲剛」の、2行のデータを追加しています。
38行目~43行目はupdateメソッドを使ってコンテンツ プロバイダのデータを更新する場合の例です。
ContentUris.withAppendedIdメソッドは、コンテントURIにIDのパスを追加して、変更対象のデータの_ID列を限定しています。
_ID列の値が1の行、すなわち「本田 圭佑」の年齢を99才に変更しています。
45行目~50行目は、deleteメソッドを使って、コンテンツ プロバイダのデータを削除する場合の例です。
最初のdeleteメソッド(47行目)では、_ID列の値が2の行、すなわち「遠藤 保仁」のデータを削除しています。
次のdeleteメソッド(50行目)では、_パラメータを使って、年令が29才のデータを削除しています。
52行目~55行目は、managedQueryメソッドを使ってコンテンツ プロバイダのデータを、Cursorオブジェクトとして取得しています。
54行目のコメントをはずして、_ID列に1を指定すると、指定したID列のデータつまり「本田 圭佑」の行のみを、取得する事もできます。
managedQueryメソッドを実行すると、コンテンツ プロバイダのqueryメソッド(リスト3・43行目)が呼ばれます。
その後、SimpleCursorAdapterを使って、ListViewに取得したCursorオブジェクトのデータを表示しています。
ListtViewによるデータの表示については、「「SimpleCursorAdapterクラス-コンテンツプロパイダーよりデータを取得してリスト表示する。」を参照して下さい。
最後に、getColumnDataメソッドを実行して、ログに、取得した行の年令データを出力しています。
getColumnDataメソッドのコードは、Cursorオブジェクトにより行を走査して、列データを取得する場合のサンプルです。
このように、他のアプリケーション内のリソースを自アプリケーションから、SQLiteデータベースのデータを操作するように、手軽に利用する事ができます。
参考URL
- Android Developers
- 開発ガイド
- リファレンス
- サンプルプログラム