Skip to content

Most visited

Recently visited

navigation

コンテンツ プロバイダの基本

コンテンツ プロバイダは、データの中央リポジトリへのアクセスを管理します。プロバイダは Android アプリケーションの一部であり、通常、データを処理するための独自の UI を備えています。 ただし、コンテンツ プロバイダは主に他のアプリケーションでの使用を意図したものです。アプリケーションはプロバイダ クライアント オブジェクトを使用してプロバイダにアクセスします。 さらに、プロバイダとプロバイダ クライアントにはデータを扱うための一貫性のある標準のインターフェースが備わっており、プロセス間の通信や安全なデータ アクセスを処理します。

このトピックでは、次の項目に関する基本的な内容を説明します。

概要

外部アプリケーションでは、コンテンツ プロバイダのデータは、リレーショナル データベースで使用する表と同様に、1 つ以上の表として表示されます。 行はプロバイダが収集するデータの何らかのタイプのインスタンスを表しており、行内のそれぞれの列はインスタンスに対して収集した個々のデータを表しています。

たとえば、Android プラットフォームに組み込まれているプロバイダの 1 つに単語リストがありますが、ここにはユーザーが保存しておく標準以外の単語のスペリングが格納されます 表 1 は、このプロバイダの表にデータがどのように格納されているのかを表しています。

表 1. サンプルの単語リスト表

word app id frequency locale _ID
mapreduceuser1100en_US1
precompileruser14200fr_FR2
appletuser2225fr_CA3
constuser1255pt_BR4
intuser5100en_UK5

表 1 の各行は、標準の辞書には含まれていない単語のインスタンスを表しています。 各列は、該当する単語のデータの一部(その単語が最初に見つかったロケールなど)を表しています。 列の見出しは、プロバイダに格納される列の名前です。 行のロケールを調べるには、locale 列を参照します。このプロバイダの場合、_ID 列が「プライマリキー」の列の役割を果たし、プロバイダはこの列を自動的に保持します。

注: プロバイダはプライマリキーを持つ必要がなく、プライマリキーがある場合は _ID をプライマリキーの列名として使用する必要はありません。 ただし、プロバイダのデータを ListView にバインドする場合は、いずれかの列の名前を _ID とする必要があります。 この要件についての詳細は、クエリ結果を表示するセクションをご覧ください。

プロバイダにアクセスする

アプリケーションは、ContentResolver クライアント オブジェクトを使用して、コンテンツ プロバイダのデータにアクセスします。 このオブジェクトには、プロバイダ オブジェクトの同名のメソッドを呼び出すメソッドが備わっています。これは、ContentProvider の具体的なサブクラスのインスタンスのいずれかになります。 ContentResolver メソッドには、永続ストレージの基本的な「CRUD」(作成、取得、更新、削除)機能が備わっています。

クライアント アプリケーションのプロセスにおける ContentResolver オブジェクトと、プロバイダを所有するアプリケーションの ContentProvider オブジェクトが、プロセス間の通信を自動的に処理します。さらに、ContentProvider は、データのリポジトリと、外部に表形式で表示されるデータの間の抽象化レイヤーとして機能します。

注: 通常、アプリケーションがプロバイダにアクセスする場合、そのマニフェスト ファイルで特定のパーミッションを要求する必要があります。 詳細は、コンテンツ プロバイダ パーミッションセクションをご覧ください。

たとえば、単語リスト プロバイダから単語とそのロケールの一覧を取得するには、ContentResolver.query() を呼び出します。 query() メソッドによって、単語リスト プロバイダが定義するContentProvider.query() メソッドが呼び出されます。 コードの次の行は、ContentResolver.query() 呼び出しを表しています。

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows

表 2 は、query(Uri,projection,selection,selectionArgs,sortOrder) の引数と SQL SELECT 文の対応関係を示しています。

表 2. SQL クエリと比較した Query()

query() 引数 SELECT キーワード / パラメータ
UriFROM table_nameUri は、table_name という名前のプロバイダの表にマッピングされます。
projectioncol,col,col,...projection は、取得するそれぞれの行に含まれる列の配列です。
selectionWHERE col = valueselection は、行を選択する際の基準を指定します。
selectionArgs(正確に一致するものはありません。 選択句では、? プレースホルダーが選択引数に置き換わります)
sortOrderORDER BY col,col,...sortOrder は、返される Cursor 内で行が表示される順番を指定します。

コンテンツ URI

コンテンツ URI は、プロバイダのデータを特定する URI です。コンテンツ URI には、プロバイダ全体の識別名(認証局)と表をポイントする名前(パス)が含まれます。 プロバイダの表にアクセスするためのクライアント メソッドを呼び出す場合、その引数の 1 つがコンテンツ URI になります。

コードの前半の行では、定数 CONTENT_URI に、単語リストの「words」表のコンテンツ URI が入ります。 ContentResolver オブジェクトは URI の認証局をパースし、認証局を既知のプロバイダのシステム表と比較して、プロバイダを「解決」します。 その後、ContentResolver は、クエリ引数を正しいプロバイダに送信できます。

ContentProvider は、アクセスする表を選択するのに、コンテンツ URI のパス部分を使用します。 通常、プロバイダは公開する各表のパスを持ちます。

コードの前半の行では、「words」表の完全な URI は次のようになります。

content://user_dictionary/words

ここで、user_dictionary 文字列はプロバイダの認証局になり、words 文字列は表のパスになります。 文字列 content://スキーム)は常に存在し、これがコンテンツ URI であることを示します。

多くのプロバイダでは、URI の末尾に ID 値を付加することで、表内の 1 つの行にアクセスできます。 たとえば、_ID4 の行を単語リストから取得するには、次のコンテンツ URI を使用します。

Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

通常は、一連の行を取得してからいずれかの行を更新、削除するような場合に ID 値を使用します。

注: Uri クラスと Uri.Builder クラスには、正しい形式の URI オブジェクトを文字列から作成するための、便利なメソッドが用意されています。 ContentUris クラスには、URI に ID 値を付加するための便利なメソッドが用意されています。 前述のスニペットは withAppendedId() を使用して、UserDictionary コンテンツ URI に ID を付加しています。

プロバイダからデータを取得する

このセクションでは、単語リスト プロバイダの例を使い、プロバイダからデータを取得する方法を説明します。

わかりやすくするために、このセクションのコード スニペットは「UI スレッド」の ContentResolver.query() を呼び出しています。 ただし、実際のコードでは、個別のスレッドで非同期にクエリを実行する必要があります。 この操作は、CursorLoader クラスを使用して行うこともできます。このクラスについての詳細は、ローダに関するガイドをご覧ください。 さらに、コードの行はスニペットのみであり、完全なアプリケーションにはなっていません。

プロバイダからデータを取得するには、次の基本的な手順に従います。

  1. プロバイダの読み取りアクセスを要求します。
  2. プロバイダにクエリを送信するコードを定義します。

読み取りアクセス パーミッションを要求する

プロバイダからデータを取得するには、アプリケーションからプロバイダの読み取りアクセスを要求します。 実行時にはこのパーミッションを要求できません。その代わりに、<uses-permission> 要素とプロバイダで定義した正確なパーミッション名を使用して、このパーミッションを必要としていることをマニフェストで指定する必要があります。 この要素をマニフェストで指定すると、実際に、このパーミッションをアプリケーションに「要求」することになります。 ユーザーがアプリケーションをインストールすると、この要求が暗黙的に付与されることになります。

使用する読み取りアクセス パーミッションの正確な名前と、プロバイダが使用するその他のアクセス パーミッションの名前については、プロバイダのドキュメントをご覧ください。

プロバイダにアクセスする際のパーミッションのロールの詳細は、コンテンツ プロバイダ パーミッションセクションをご覧ください。

単語リスト プロバイダはパーミッション android.permission.READ_USER_DICTIONARY をマニフェスト ファイルで定義するため、プロバイダからの読み取りを行うアプリケーションはこのパーミッションを要求する必要があります。

クエリを作成する

プロバイダからデータを取得するため、次はクエリを作成します。最初のスニペットで、単語リスト プロバイダにアクセスするための変数をいくつか定義します。


// A "projection" defines the columns that will be returned for each row
String[] mProjection =
{
    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
};

// Defines a string to contain the selection clause
String mSelectionClause = null;

// Initializes an array to contain selection arguments
String[] mSelectionArgs = {""};

次のスニペットでは、単語リスト プロバイダの例を使って、ContentResolver.query() を使用する方法を示しています。 プロバイダ クライアント クエリは SQL クエリに似たものであり、戻り値となる一連の列、一連の選択基準、並べ替えの順番を含みます。

クエリの戻り値となる一連の列は、投影(変数 mProjection)と呼ばれます。

取得する行を指定する式は、選択句と選択引数に分割されます。 選択句は論理式やブール式、列名、値(変数 mSelectionClause)の組み合わせになります。 値の代わりに置き換え可能なパラメータ ? を指定すると、クエリ メソッドによって、選択引数の配列(変数 mSelectionArgs)から値が取得されます。

次のスニペットでは、ユーザーが単語を入力しない場合、選択句が null に設定され、クエリによってプロバイダのすべての単語が返されます。 ユーザーが単語を入力すると、選択句が UserDictionary.Words.WORD + " = ?" に設定され、選択引数の配列の最初の要素が、ユーザーが入力した単語に設定されます。

/*
 * This defines a one-element String array to contain the selection argument.
 */
String[] mSelectionArgs = {""};

// Gets a word from the UI
mSearchString = mSearchWord.getText().toString();

// Remember to insert code here to check for invalid or malicious input.

// If the word is the empty string, gets everything
if (TextUtils.isEmpty(mSearchString)) {
    // Setting the selection clause to null will return all words
    mSelectionClause = null;
    mSelectionArgs[0] = "";

} else {
    // Constructs a selection clause that matches the word that the user entered.
    mSelectionClause = UserDictionary.Words.WORD + " = ?";

    // Moves the user's input string to the selection arguments.
    mSelectionArgs[0] = mSearchString;

}

// Does a query against the table and returns a Cursor object
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    mProjection,                       // The columns to return for each row
    mSelectionClause                   // Either null, or the word the user entered
    mSelectionArgs,                    // Either empty, or the string the user entered
    mSortOrder);                       // The sort order for the returned rows

// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
    /*
     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
     * call android.util.Log.e() to log this error.
     *
     */
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {

    /*
     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
     * an error. You may want to offer the user the option to insert a new row, or re-type the
     * search term.
     */

} else {
    // Insert code here to do something with the results

}

このクエリは、 SQL 文に似ています。

SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;

この SQL 文では、コントラクト クラスの定数ではなく、実際の列の名前が使用されます。

悪意のある入力から保護する

コンテンツ プロバイダが管理するデータが SQL データベース内にある場合、外部の信頼されていないデータを未処理の SQL 文に含めると SQL インジェクションが発生することがあります。

次のような選択句を考えてみます。

// Constructs a selection clause by concatenating the user's input to the column name
String mSelectionClause =  "var = " + mUserInput;

このようにすれば、ユーザーが悪意のある SQL を SQL 文に連結できるようになります。 たとえば、ユーザーが mUserInput に「nothing; DROP TABLE *;」と入力すると、選択句は var = nothing; DROP TABLE *; となります。 選択句は SQL 文として処理されるため、この場合、基本的な SQLite データベースのすべての表が消去されることがあります(プロバイダに SQL インジェクションの試みの検出が設定されていない場合)。

この問題を回避するには、? を置き換え可能パラメータとして使う選択句と、選択引数の個別の配列を使用します。 そうすることで、ユーザー入力は、SQL 文の一部として解釈されずに、クエリに直接バインドされます。 SQL として扱われないため、ユーザー入力によって悪意のある SQL が挿入されることはありません。ユーザー入力を含める際に連結を使用しないで、次の選択句を使用します。

// Constructs a selection clause with a replaceable parameter
String mSelectionClause =  "var = ?";

選択引数の配列を次のように設定します。

// Defines an array to contain the selection arguments
String[] selectionArgs = {""};

選択引数の配列に次のように値を格納します。

// Sets the selection argument to the user's input
selectionArgs[0] = mUserInput;

プロバイダが SQL データベースに基づいたものではない場合でも、選択内容を指定する場合は、? を置き換え可能パラメータとして使う選択句と、選択引数の配列を使用することをお勧めします。

クエリ結果を表示する

ContentResolver.query() クライアント メソッドは、常に Cursor を返します。これには、クエリの選択基準に一致する、行へのクエリの投影によって指定される列が含まれます。 Cursor オブジェクトは、含まれる行と列へのランダム読み取りアクセスを提供します。 Cursor メソッドを使用すると、結果の行の繰り返し、各列のデータタイプの識別、列からのデータの取得、結果のその他のプロパティの確認といった操作が可能となります。 一部の Cursor を実装すると、プロバイダのデータが変更された場合にオブジェクトが自動的に更新されたり、Cursor が変更された場合にオブザーバ オブジェクトのメソッドがトリガーされたり、その両方が実行されたりします。

注: クエリを作成するオブジェクトの特性に基づいて、プロバイダによって列へのアクセスが制限されることがあります。 たとえば、連絡先プロバイダにより同期アダプタは一部の列へのアクセスが制限されるため、その場合はアクティビティやサービスを返しません。

選択基準に一致する行がない場合は、Cursor.getCount() が 0(空のカーソル)の Cursor オブジェクトを返します。

内部エラーが発生した場合、クエリの結果はプロバイダによって異なります。null が返されることもあれば、Exception がスローされることもあります。

Cursor は行の「一覧」であることから、Cursor のコンテンツを表示する場合は、SimpleCursorAdapter 経由で ListView にリンクすることをお勧めします。

次のスニペットは前のスニペットのコードの続きです。クエリによって取得する Cursor を含む SimpleCursorAdapter オブジェクトを作成し、このオブジェクトを ListView のアダプタに設定します。

// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] mWordListColumns =
{
    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
};

// Defines a list of View IDs that will receive the Cursor columns for each row
int[] mWordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(),               // The application's Context object
    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
    mCursor,                               // The result from the query
    mWordListColumns,                      // A string array of column names in the cursor
    mWordListItems,                        // An integer array of view IDs in the row layout
    0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
mWordList.setAdapter(mCursorAdapter);

注: Cursor によって ListView を戻すには、カーソルに _ID という名前の列を含める必要があります。 そのため、ListView には表示されまませんが、前述のクエリは「words」表に _ID 列を取得します。 また、このような制限があることから、大部分のプロバイダがそれぞれの表に _ID 列を持ちます。

クエリ結果を取得する

クエリ結果を単に表示するだけでなく、他のタスクに使用することもできます。たとえば、単語リストからスペリングを取得して、それを他のプロバイダで検索するといった操作も可能です。 そのためには、次のように Cursor の行に操作を繰り返します。


// Determine the column index of the column named "word"
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);

/*
 * Only executes if the cursor is valid. The User Dictionary Provider returns null if
 * an internal error occurs. Other providers may throw an Exception instead of returning null.
 */

if (mCursor != null) {
    /*
     * Moves to the next row in the cursor. Before the first movement in the cursor, the
     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
     * exception.
     */
    while (mCursor.moveToNext()) {

        // Gets the value from the column.
        newWord = mCursor.getString(index);

        // Insert code here to process the retrieved word.

        ...

        // end of while loop
    }
} else {

    // Insert code here to report an error if the cursor is null or the provider threw an exception.
}

Cursor の実装には「取得」メソッドがいくつか備わっており、オブジェクトからさまざまなタイプのデータを取得できます。 たとえば、前述のスニペットは getString() を使用しています。 さらに、列のデータタイプを示す値を返す getType() メソッドも使用しています。

コンテンツ プロバイダ パーミッション

プロバイダのアプリケーションでは、他のアプリケーションがプロバイダのデータにアクセスするのに必要なパーミッションを指定できます。 これらのパーミッションを設定しておけば、ユーザーはアプリケーションがアクセスしようとしているデータの種類を把握できます。 他のアプリケーションは、プロバイダの要件に基づき、そのプロバイダにアクセスするのに必要なパーミッションを要求します。 エンドユーザーがアプリケーションをインストールする際には、要求されたパーミッションを確認できます。

プロバイダのアプリケーションでパーミッションを指定していない場合は、他のアプリケーションはプロバイダのデータにアクセスできません。 ただし、指定したパーミッションに関係なく、プロバイダのアプリケーションのコンポーネントには完全な読み取り権限と書き込み権限を常に付与する必要があります。

前述のとおり、単語リスト プロバイダには、データを取得するための android.permission.READ_USER_DICTIONARY パーミッションが必要です。 プロバイダには、データの挿入、更新、削除に対してそれぞれ個別の android.permission.WRITE_USER_DICTIONARY パーミッションがあります。

プロバイダにアクセスするのに必要なパーミッションを取得するには、アプリケーションのマニフェスト ファイルの <uses-permission> 要素を使用してパーミッションを要求する必要があります。 Android Package Manager でアプリケーションをインストールする場合は、アプリケーションが要求するすべてのパーミッションをユーザーが承認する必要があります。 ユーザーがすべてのパーミッションを承認すると、Package Manager がインストールを続行します。ユーザーが承認しない場合は、Package Manager がインストールを中止します。

次の <uses-permission> 要素は、単語リスト プロバイダへの読み取りアクセスを要求します。

    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">

プロバイダ アクセスへのパーミッションの影響については、セキュリティとパーミッションに関するガイドをご覧ください。

データを挿入、更新、削除する

データを収集するには、プロバイダからデータを取得するのと同様に、プロバイダ クライアントとプロバイダの ContentProvider 間で操作を行います。 対応する ContentProvider のメソッドに渡した引数を使用して、ContentResolver のメソッドを呼び出します。 プロバイダとプロバイダ クライアントが、セキュリティとプロセス間の通信を自動的に処理します。

データを挿入する

プロバイダにデータを挿入するには、ContentResolver.insert() メソッドを呼び出します。 このメソッドはプロバイダに新しい行を挿入し、その行のコンテンツ URI を返します。 このスニペットは、単語リスト プロバイダに新しい単語を挿入する方法を示しています。

// Defines a new Uri object that receives the result of the insertion
Uri mNewUri;

...

// Defines an object to contain the new values to insert
ContentValues mNewValues = new ContentValues();

/*
 * Sets the values of each column and inserts the word. The arguments to the "put"
 * method are "column name" and "value"
 */
mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
mNewValues.put(UserDictionary.Words.WORD, "insert");
mNewValues.put(UserDictionary.Words.FREQUENCY, "100");

mNewUri = getContentResolver().insert(
    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
    mNewValues                          // the values to insert
);

新しい行のデータは 1 つの ContentValues オブジェクトに入ります。これは 1 行カーソルの形式に似ています。 このオブジェクト内の列は同じデータタイプを持つ必要はなく、値をまったく指定しない場合は、ContentValues.putNull() を使用して列を null に設定できます。

この列は自動的に保持されるため、スニペットは _ID 列を追加しません。 プロバイダは、追加するそれぞれの行に、_ID の一意の値を割り当てます。 通常、プロバイダはこの値を表のプライマリキーとして使用します。

newUri で返されるコンテンツ URI は、新たに追加された行を特定するものであり、次の形式となります。

content://user_dictionary/words/<id_value>

<id_value> は、新しい行の _ID のコンテンツです。 ほとんどのプロバイダではコンテンツ URI のこの形式を自動的に検出し、特定の行で要求された操作を実行します。

返された Uri から _ID の値を取得するには、ContentUris.parseId() を呼び出します。

データを更新する

行を更新するには、挿入操作と同じように、値を更新した ContentValues オブジェクトと、クエリ操作のときと同じように選択基準を使用します。 使用するクライアント メソッドは ContentResolver.update() です。 更新する列の ContentValues オブジェクトに値を追加する操作のみが必要になります。 列のコンテンツを消去する場合は、値を null に設定します。

次のスニペットは、ロケール言語が「en」となっているすべての行のロケールを null に変更します。 戻り値は、更新された行の数となります。

// Defines an object to contain the updated values
ContentValues mUpdateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
String[] mSelectionArgs = {"en_%"};

// Defines a variable to contain the number of updated rows
int mRowsUpdated = 0;

...

/*
 * Sets the updated value and updates the selected words.
 */
mUpdateValues.putNull(UserDictionary.Words.LOCALE);

mRowsUpdated = getContentResolver().update(
    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    mUpdateValues                       // the columns to update
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

また、ContentResolver.update() を呼び出すときには、ユーザー入力をサニタイズする必要があります。 詳細は、悪意のある入力から保護するのセクションをご覧ください。

データを削除する

行の削除は行データの取得と似た操作になります。削除する行の選択基準を指定することで、クライアント メソッドが削除した行の数を返します。 次のスニペットは appid が「user」となっている行を削除します。メソッドによって削除された行の数が返されます。


// Defines selection criteria for the rows you want to delete
String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};

// Defines a variable to contain the number of rows deleted
int mRowsDeleted = 0;

...

// Deletes the words that match the selection criteria
mRowsDeleted = getContentResolver().delete(
    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

また、ContentResolver.delete() を呼び出すときには、ユーザー入力をサニタイズする必要があります。 詳細は、悪意のある入力から保護するのセクションをご覧ください。

プロバイダ データタイプ

コンテンツ プロバイダではさまざまなデータタイプを利用できます。単語リスト プロバイダで提供できるのはテキストのみですが、次のような形式を提供できます。

それ以外にも、プロバイダでは 64KB バイト配列として実装されるバイナリ ラージ オブジェクト(BLOB)もよく使用されます。 利用可能なデータタイプについては、Cursor クラスの「get」メソッドをご覧ください。

プロバイダの各列のデータタイプは、通常、そのドキュメントに一覧が記載されています。 単語リスト プロバイダのデータタイプは、コントラクト クラス UserDictionary.Words のリファレンスに一覧が記載されています(コントラクト クラスの詳細は、コントラクト クラスセクションをご覧ください)。 さらに、Cursor.getType() を呼び出すことで、データタイプを判断することもできます。

プロバイダは、プロバイダによって定義される各コンテンツ URI の MIME データタイプも保持します。MIME タイプ情報を使用すると、プロバイダが提供するデータをアプリケーションが処理できるかを判断したり、MIME タイプに基づいて処理のタイプを選択したりできます。 通常は、複雑なデータ構造やファイルを持つプロバイダで作業をする際に MIME タイプが必要になります。 たとえば、連絡先プロバイダの ContactsContract.Data 表は MIME タイプを使用して、それぞれの行に格納される連絡先データのタイプをラベル付けします。 コンテンツ URI に対応する MIME タイプを取得するには、ContentResolver.getType() を呼び出します。

MIME タイプ リファレンスセクションでは、標準とカスタムの両方の MIME タイプについて説明しています。

プロバイダ アクセスの別の形式

アプリケーションの開発では、次に示すプロバイダ アクセスの別の 3 つの形式が重要になります。

バッチアクセスとインテント経由の修正については、次のセクションで説明しています。

バッチアクセス

多数の行を挿入する場合や、同じメソッド呼び出しで複数の表に行を挿入する場合は、プロバイダへのバッチアクセスを使用すると便利です。また、一般的には、境界をまたいでプロセス全体にトランザクションとして一連の操作を実行する場合にもバッチアクセスが便利です。

「バッチモード」でプロバイダにアクセスするには、ContentProviderOperation オブジェクトの配列を作成してから、ContentResolver.applyBatch() を使用してそれらのオブジェクトをコンテンツ プロバイダに送信します。 このメソッドには、特定のコンテンツ URI ではなく、コンテンツ プロバイダの認証局を渡します。 そうすることで、配列内のそれぞれの ContentProviderOperation オブジェクトが別々の表に対して機能するようになります。 ContentResolver.applyBatch() への呼び出しでは、結果の配列が返されます。

ContactsContract.RawContacts コントラクト クラスの説明には、バッチ挿入を示したコード スニペットが記載されています。 Contact Manager のサンプル アプリケーションでは、ContactAdder.java ソースファイルでのバッチアクセスの例が紹介されています。

インテント経由のデータアクセス

インテントを使用すれば、コンテンツ プロバイダに間接的にアクセスできます。アプリケーションにアクセス パーミッションがない場合でも、パーミッションを持つアプリケーションから結果のインテントを取得したり、パーミッションを持つアプリケーションをアクティベートしてそのアプリケーションをユーザーに使用させたりすることで、ユーザーがプロバイダのデータにアクセスできるようになります。

一時的なパーミッションによりアクセスを取得する

適切なアクセス パーミッションがない場合でも、パーミッションを持つアプリケーションにインテントを送信し、「URI」パーミッションを含む結果のインテントを受け取ることで、コンテンツ プロバイダのデータにアクセスできます。 これらのパーミッションは特定のコンテンツ URI のパーミッションであり、パーミッションを受け取ったアクティビティが終了するまで効力を持ちます。 永続的なパーミッションを持つアプリケーションは、次のように結果のインテントにフラグを設定し、一時的なパーミッションを付与します。

注: これらのフラグは、認証局がコンテンツ URI に含まれるプロバイダへの全般的な読み取りアクセスと書き込みアクセスを付与するものではありません。 アクセスは URI 自身に限定されます。

プロバイダは、<provider> 要素の android:grantUriPermission 属性や、<provider> 要素の <grant-uri-permission> 子要素を使用して、コンテンツ URI の URI パーミッションを自身のマニフェストで定義します。 URI パーミッションのメカニズムについての詳細は、URI パーミッション セクションのセキュリティとパーミッションに関するガイドをご覧ください。

たとえば、READ_CONTACTS パーミッションを持たない場合でも、連絡先プロバイダの連絡先のデータを取得できます。 連絡先の誕生日にグリーティング カードをオンラインで送信するアプリケーションなどにこのメカニズムを利用できます。 ユーザーのすべての連絡先やそのすべての情報へのアクセス件を付与する READ_CONTACTS を要求する代わりに、アプリケーションで使用する連絡先をユーザーが制御できるように設定することもできます。 そのためには、次の手順を使用します。

  1. アプリケーションが、メソッド startActivityForResult() を使用して、アクション ACTION_PICK と「連絡先」MIME タイプ CONTENT_ITEM_TYPE を含むインテントを送信します。
  2. このインテントは連絡帳アプリの「selection」アクティビティのインテント フィルタに一致するため、アクティビティがフォアグラウンドに移動します。
  3. selection アクティビティでは、更新する連絡先をユーザーが選択します。 この操作が行われると、selection が setResult(resultcode, intent) を呼び出し、アプリケーションに戻すインテントを設定します。 インテントには、ユーザーが選択した連絡先のコンテンツ URI と、「エクストラ」フラグ FLAG_GRANT_READ_URI_PERMISSION が含まれます。 これらのフラグがあることで、コンテンツ URI がポイントする連絡先のデータを読み取るための URI パーミッションがアプリに付与されます。 その後、selection アクティビティによって、アプリケーションに制御を返すための finish() が呼び出されます。
  4. アクティビティがフォアグラウンドに戻り、システムがアクティビティの onActivityResult() メソッドを呼び出します。 このメソッドは、連絡帳アプリの selection アクティビティによって作成された結果のインテントを受け取ります。
  5. 結果のインテントのコンテンツ URI を使用すれば、マニフェストで永続的なアクセス パーミッションをプロバイダに要求していなくても、連絡先プロバイダから連絡先のデータを読み取ることができます。 その後、連絡先の誕生日情報やメールアドレスを取得し、グリーティング カードをオンラインで送信できます。

別のアプリケーションを使用する

パーミッションを持つアプリケーションをアクティベートし、そのアプリケーションをユーザーが使用すれば、アクセス パーミッションを持たないデータをユーザーが修正できるようになります。

たとえば、アプリケーションの挿入 UI をアクティベートできる ACTION_INSERT インテントをカレンダー アプリケーションが受け入れるとします。 このインテントに「エクストラ」データを渡すことで、アプリケーションがそのデータを使用して、事前に入力された UI を作成します。 繰り返し発生するイベントは複雑な構文を持つため、カレンダー プロバイダにイベントを挿入する場合は、ACTION_INSERT でカレンダー アプリをアクティベートしてから、ユーザーにイベントを挿入してもらうことをお勧めします。

コントラクト クラス

コントラクト クラスは、アプリケーションがコンテンツ URI、列名、インテント アクション、コンテンツのその他の機能で作業する際に役立つ定数を定義します。 プロバイダでコントラクト クラスが自動的に含まれることはありません。プロバイダのデベロッパーはコントラクト クラスを定義して、その他のデベロッパーが使用できるようにする必要があります。 Android プラットフォームに含まれる多くのプロバイダは、パッケージ android.provider 内に対応するコントラクト クラスを持ちます。

たとえば、単語リスト プロバイダは、コンテンツ URI と列名の定数を含む、コントラクト クラス UserDictionary を持つとします。 「words」表のコンテンツ URI は、定数 UserDictionary.Words.CONTENT_URI で定義されます。 さらに、UserDictionary.Words クラスは、このガイドのサンプルのスニペットで使用される、列名の定数を持ちます。 たとえば、クエリの投影は次のように定義できます。

String[] mProjection =
{
    UserDictionary.Words._ID,
    UserDictionary.Words.WORD,
    UserDictionary.Words.LOCALE
};

もう一方のコントラクト クラスは、連絡先プロバイダのクラス ContactsContract です。 このクラスのリファレンスには、サンプルのコード スニペットが記載されています。サブクラスの 1 つである ContactsContract.Intents.Insert は、インテントとインテント データの定数を含むコントラクト クラスです。

MIME タイプ リファレンス

コンテンツ プロバイダは、標準の MIME メディア タイプかカスタムの MIME タイプ文字列、またはその両方を返すことができます。

MIME タイプは次の形式となります

type/subtype

たとえば、よく利用される MIME タイプ text/html は、text タイプと html サブタイプを持ちます。 プロバイダから URI に対してこのタイプが返される場合、その URI を使用するクエリは HTML タグを含むテキストを返すことになります。

「ベンダー固有の」MIME タイプとも呼ばれるカスタムの MIME タイプ文字列には、より複雑な typesubtype の値が含まれます。 複数行の場合、type 値は常に

vnd.android.cursor.dir

となり、1 つの行の場合は

vnd.android.cursor.item

となります。

subtype はプロバイダ固有の値になります。通常、Android 組み込みプロバイダは単純な subtype を使用します。 たとえば、連絡先アプリケーションが電話番号の行を作成すると、行には次の MIME タイプが設定されます。

vnd.android.cursor.item/phone_v2

subtype の値は単純に phone_v2 となっていることがわかります。

他のプロバイダ デベロッパーは、プロバイダの認証局と表明に基づいて独自のパターンの subtype を作成できます。 たとえば、列車の時刻表を含むプロバイダについて考えてみます。 プロバイダの認証局は com.example.trains であり、表 Line1、Line2、Line3 を持ちます。 表 Line1 のコンテンツ URI

content://com.example.trains/Line1

に対しては、プロバイダは次の MIME タイプを返します

vnd.android.cursor.dir/vnd.example.line1

表 Line2 の行 5 のコンテンツ URI

content://com.example.trains/Line2/5

に対しては、プロバイダは次の MIME タイプを返します

vnd.android.cursor.item/vnd.example.line2

ほとんどのコンテンツ プロバイダが、使用する MIME タイプに対してコントラクト クラス定数を定義します。たとえば、連絡先プロバイダのコントラクト クラス ContactsContract.RawContacts は、1 つの未加工連絡先行の MIME タイプに、定数 CONTENT_ITEM_TYPE を定義します。

1 つの行のコンテンツ URI については、コンテンツ URI セクションをご覧ください。

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.