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

コンテンツ プロバイダは、データのセントラル リポジトリへのアクセスを管理します。プロバイダは Android アプリの一部であり、多くの場合、データを操作する独自の UI を提供します。ただし、コンテンツ プロバイダは主に他の これらのアプリケーションは、プロバイダ クライアント オブジェクトを使用してプロバイダにアクセスします。プロバイダは データへの一貫した標準インターフェースを提供し、 安全なデータアクセスを実現します。

通常、コンテンツ プロバイダは次の 2 つのシナリオのいずれかで連携します。 別のアプリで既存のコンテンツ プロバイダにアクセスする場合や、 アプリ内で新しいコンテンツ プロバイダを作成し、他のアプリとデータを共有する。

このページ では、既存のコンテンツ プロバイダを利用する際の基本事項について説明しています。実装方法や コンテンツ プロバイダを使用する場合は、 <ph type="x-smartling-placeholder"></ph> コンテンツ プロバイダを作成する

このトピックで説明する内容は次のとおりです。

  • コンテンツ プロバイダの仕組み。
  • コンテンツ プロバイダからのデータの取得に使用する API。
  • コンテンツ プロバイダへのデータの挿入、データの更新、または削除に使用する API。
  • プロバイダでの作業に役立つその他の API 機能。

概要

コンテンツ プロバイダは外部アプリに対し、リレーショナル データベースのテーブルに似た 1 つ以上のテーブルとしてデータを提供します。行はプロバイダが収集するなんらかのデータのインスタンスを表し、行の各列はインスタンスに対して収集した個々のデータを表します。

コンテンツ プロバイダは、アプリケーションのデータ ストレージ レイヤへのアクセスを さまざまな API とコンポーネントが存在します。図 1 に示すように、これには次のものが含まれます。

  • アプリデータへのアクセスを他のアプリと共有する
  • ウィジェットにデータを送信する
  • SearchRecentSuggestionsProvider を使用して、検索フレームワークを介してアプリのカスタム検索候補を返す
  • AbstractThreadedSyncAdapter の実装を使用してアプリデータをサーバーと同期する
  • CursorLoader を使用して UI にデータを読み込む
コンテンツ プロバイダと他のコンポーネントの関係。

図 1. コンテンツ プロバイダと他のコンポーネントとの関係。

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

コンテンツ プロバイダのデータにアクセスする場合は、アプリの ContextContentResolver オブジェクトを使用し、クライアントとしてプロバイダと通信します。「 ContentResolver オブジェクトは、プロバイダ オブジェクトである ContentProvider を実装するクラスのインスタンス。

プロバイダ オブジェクトはクライアントからデータ リクエストを受け取り、リクエストされたアクションを実施して、結果を返します。このオブジェクトには、プロバイダ オブジェクトの同じ名前のメソッドを呼び出すメソッドがありますが、 ContentProvider の具象サブクラスのいずれかのインスタンス。「 ContentResolver メソッドは、基本的な CRUD(作成、取得、更新、削除)関数を使用します。

UI から ContentProvider にアクセスするための一般的なパターンでは、CursorLoader を使用してバックグラウンドで非同期クエリを実行します。「 UI の Activity または FragmentCursorLoader をクエリに追加することで、 ContentResolver を使用した ContentProvider

これにより、クエリの実行中に引き続き UI を使用できます。このパターンには、図 2 に示すようにさまざまなオブジェクトとのやり取りと、基となるストレージ メカニズムが含まれます。

ContentProvider、他のクラス、ストレージの間のやり取り。

図 2. ContentProvider、他のクラス、ストレージ間のやり取り。

注: アプリがプロバイダにアクセスするには、通常、マニフェスト ファイルで特定の権限をリクエストする必要があります。この開発パターンについては、 コンテンツ プロバイダの権限のセクション。

Android プラットフォームの組み込みプロバイダの 1 つに、単語リスト プロバイダがあります。 には、ユーザーが保持しておきたい標準外の単語が格納されています。表 1 は、このプロバイダのテーブルにデータがどのように格納されるかを示しています。

表 1: 単語リストテーブルの例。

word app id frequency locale _ID
mapreduce user1 100 en_US 1
precompiler user14 200 fr_FR 2
applet user2 225 fr_CA 3
const user1 255 pt_BR 4
int user5 100 en_UK 5

表 1 の各行は、1 つの単語に 1 対 1 で 同じ言語です。各列は、その単語のデータの一部を表します(例: 自動的に検出されます。列の見出しは、プロバイダに格納される列の名前です。たとえば、ある行の言語 / 地域を参照するには、その locale 列を参照します。対象 このプロバイダの場合、_ID 列は主キー列として機能し、 プロバイダが自動的に維持します。

単語リスト プロバイダから単語とその言語 / 地域のリストを取得するには、ContentResolver.query() を呼び出します。query() メソッドにより、単語リスト プロバイダが定義する ContentProvider.query() メソッドが呼び出されます。ContentResolver.query() 呼び出しを次のコード行に示します。

Kotlin

// Queries the UserDictionary and returns results
cursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
        projection,                        // The columns to return for each row
        selectionClause,                   // Selection criteria
        selectionArgs.toTypedArray(),      // Selection criteria
        sortOrder                          // The sort order for the returned rows
)

Java

// Queries the UserDictionary and returns results
cursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    projection,                        // The columns to return for each row
    selectionClause,                   // Selection criteria
    selectionArgs,                     // Selection criteria
    sortOrder);                        // The sort order for the returned rows

表 2 は、 query(Uri,projection,selection,selectionArgs,sortOrder) は、次の SQL SELECT ステートメントと一致します。

表 2: query() と SQL クエリの比較

query() 引数 SELECT キーワード / パラメータ
Uri FROM table_name Uri は、table_name という名前のプロバイダのテーブルにマッピングされます。
projection col,col,col,... projection は、各行に含まれる列の配列です。 確認できます。
selection WHERE col = value selection は、行を選択する際の基準を指定します。
selectionArgs 完全に一致するものはありません。選択引数が内の ? 個のプレースホルダを置き換えます 選択句を使用します。
sortOrder ORDER BY col,col,... sortOrder は、返される Cursor で行が表示される順序を指定します。

コンテンツ URI

コンテンツ URI は、プロバイダのデータを特定する URI です。コンテンツ URI プロバイダ全体のシンボリック名(オーソリティ)と、 テーブルを参照する名前(パス)があります。クライアント メソッドを呼び出してプロバイダのテーブルにアクセスする場合、引数のうち 1 つはテーブルのコンテンツ URI です。

上記のコード行の定数は、 CONTENT_URI には、次のコンテンツ URI が含まれます。 単語リスト プロバイダの Words テーブル。ContentResolver このオブジェクトは URI の権限を解析し、それを使用してプロバイダを解決します。 既知のプロバイダのシステム テーブルと認証機関を比較します。「 そうすると、ContentResolver はクエリ引数を適切な関数にディスパッチできます。 接続します。

ContentProvider は、アクセスするテーブルを選択するために、コンテンツ URI のパス部分を使用します。プロバイダには通常、公開する各テーブルへのパスがあります。

上記のコード行の Words テーブルの完全な URI は次のとおりです。

content://user_dictionary/words
  • content:// 文字列は、常に存在するスキームです。 コンテンツ URI として識別します。
  • user_dictionary 文字列はプロバイダのオーソリティです。
  • words 文字列はテーブルのパスです。

多くのプロバイダでは、ID 値を追加することでテーブル内の単一行にアクセスできる を追加します。たとえば、_ID を持つ行を取得するには、次のようにします。 単語リスト プロバイダの 4 にある次のコンテンツ URI を使用できます。

Kotlin

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

Java

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

ID 値は、一連の行の取得後に更新や削除を行うときによく使用します。 そのうちの 1 つです

注: Uri クラスと Uri.Builder クラスには、文字列から適切な形式の URI オブジェクトを作成するための便利なメソッドが用意されています。「 ContentUris クラスには、ID 値を 指定します。上記のスニペットでは、withAppendedId() を使用して単語リスト プロバイダのコンテンツ URI に ID を付加しています。

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

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

わかりやすくするために、このセクションのコード スニペットでは、 UI スレッドに対する ContentResolver.query()。イン 別のスレッドで非同期的にクエリを実行します。Google Chat では CursorLoader クラスを使用する。これについては、 詳しくは、をご覧ください ローダのガイドをご覧ください。また、コード行はスニペットにすぎません。1 対 1 の会話全体を 説明します。

プロバイダからデータを取得する基本的な手順は次のとおりです。

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

読み取りアクセス権限をリクエストする

プロバイダからデータを取得するには、アプリケーションで読み取りアクセス権限を 接続します。実行時にこの権限をリクエストすることはできません。代わりに、kubectl の マニフェストでこの権限を取得する必要があります。 <uses-permission> 要素によって定義された正確な権限名が 接続します。

マニフェストでこの要素を指定すると、 許可する必要があります。ユーザーがアプリをインストールすると、このリクエストが暗黙的に付与されます。

使用しているプロバイダの読み取りアクセス権限の正確な名前と、プロバイダで使用されている他のアクセス権限の名前を確認するには、プロバイダのドキュメントをご覧ください。

プロバイダへのアクセスにおける権限の役割については、 コンテンツ プロバイダの権限のセクション。

単語リスト プロバイダはマニフェスト ファイルで権限 android.permission.READ_USER_DICTIONARY を定義するため、プロバイダからの読み取りを行うアプリは、この権限をリクエストする必要があります。

クエリを作成する

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

Kotlin

// A "projection" defines the columns that are returned for each row
private val mProjection: Array<String> = arrayOf(
        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
private var selectionClause: String? = null

// Declares an array to contain selection arguments
private lateinit var selectionArgs: Array<String>

Java

// A "projection" defines the columns that are 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 selectionClause = null;

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

次のスニペットでは、単語リスト プロバイダの例を使用して、ContentResolver.query() の使用方法を示しています。プロバイダ クライアント クエリは SQL クエリに似ており、 返される列のセット、選択条件のセット、並べ替え順序です。

クエリによって返される列のセットは射影と呼ばれ、 変数は mProjection です。

取得する行を指定する式は、選択句と選択引数に分割されます。選択句は論理式とブール式を組み合わせたものです。 列名、列値などです変数は mSelectionClause です。「 置換可能なパラメータ ? を値の代わりに使用する場合、クエリメソッドは値を取得します。 これは選択引数配列から返されます。これは変数 mSelectionArgs です。

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

Kotlin

/*
 * This declares a String array to contain the selection arguments.
 */
private lateinit var selectionArgs: Array<String>

// Gets a word from the UI
searchString = searchWord.text.toString()

// Insert code here to check for invalid or malicious input

// If the word is the empty string, gets everything
selectionArgs = searchString?.takeIf { it.isNotEmpty() }?.let {
    selectionClause = "${UserDictionary.Words.WORD} = ?"
    arrayOf(it)
} ?: run {
    selectionClause = null
    emptyArray<String>()
}

// Does a query against the table and returns a Cursor object
mCursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI, // The content URI of the words table
        projection,                       // The columns to return for each row
        selectionClause,                  // Either null or the word the user entered
        selectionArgs,                    // Either empty or the string the user entered
        sortOrder                         // The sort order for the returned rows
)

// Some providers return null if an error occurs, others throw an exception
when (mCursor?.count) {
    null -> {
        /*
         * Insert code here to handle the error. Be sure not to use the cursor!
         * You might want to call android.util.Log.e() to log this error.
         */
    }
    0 -> {
        /*
         * Insert code here to notify the user that the search is unsuccessful. This isn't
         * necessarily an error. You might 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
    }
}

Java

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

// Gets a word from the UI
searchString = searchWord.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(searchString)) {
    // Setting the selection clause to null returns all words
    selectionClause = null;
    selectionArgs[0] = "";

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

    // Moves the user's input string to the selection arguments
    selectionArgs[0] = searchString;

}

// 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
    projection,                       // The columns to return for each row
    selectionClause,                  // Either null or the word the user entered
    selectionArgs,                    // Either empty or the string the user entered
    sortOrder);                       // 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 can
     * 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 is unsuccessful. This isn't necessarily
     * an error. You can 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 インジェクションの原因となる場合があります

次の選択句について考えてみましょう。

Kotlin

// Constructs a selection clause by concatenating the user's input to the column name
var selectionClause = "var = $mUserInput"

Java

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

これを行うと、ユーザーが悪意のある SQL を SQL ステートメントに連結してしまう可能性があります。 たとえば、ユーザーは「nothing;DROP TABLE *;」と記述します。mUserInput、つまり 選択句 var = nothing; DROP TABLE *; になります。

以降、 選択句は SQL ステートメントとして扱われるため、プロバイダがすべての (ただし、プロバイダが SQLite データベース内のテーブルをキャッチするように設定されている場合を除く) SQL インジェクションの試行。

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

Kotlin

// Constructs a selection clause with a replaceable parameter
var selectionClause = "var = ?"

Java

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

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

Kotlin

// Defines a mutable list to contain the selection arguments
var selectionArgs: MutableList<String> = mutableListOf()

Java

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

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

Kotlin

// Adds the user's input to the selection argument
selectionArgs += userInput

Java

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

置換可能なパラメータとして ? を使用する選択句と、 選択引数の配列は、プロバイダが必須ではない場合でも、選択内容を指定するのに推奨される方法です。 SQL データベースに基づくクエリです

クエリ結果を表示する

ContentResolver.query() クライアント メソッドは、常に Cursor を返します。これには、クエリの選択基準に一致する行のクエリの射影で指定される列が含まれます。 Cursor オブジェクトは、そのオブジェクト内の行と列へのランダム読み取りアクセスを提供します。 含まれます。

Cursor メソッドを使用して、 各列のデータ型を判別し、列からデータを取り出して、他の列の 結果のプロパティです。

一部の Cursor の実装は自動的に行われます。 プロバイダのデータが変更されたときにオブジェクトを更新し、オブザーバー オブジェクトのメソッドをトリガーする Cursor が変更されたとき、あるいはその両方が発生したとき。

注: プロバイダは、データの特性に基づいて列へのアクセスを制限できます。 クエリを作成するオブジェクトです。たとえば、連絡先プロバイダは、一部の列のアクセスを アクティビティやサービスに返されません。

選択条件に一致する行がない場合、プロバイダは Cursor オブジェクトを返します。 Cursor.getCount() は 0、つまり空のカーソルです。

内部エラーが発生した場合、クエリの結果はプロバイダによって異なります。かもしれない null を返すか、Exception をスローできます。

Cursor は行のリストであるため、 Cursor のコンテンツは、ListView にリンクするものです。 SimpleCursorAdapter を使用します。

次のスニペットは、前のスニペットのコードの続きです。また、 Cursor を含む SimpleCursorAdapter オブジェクト 設定し、このオブジェクトをサービスのアダプタとして設定します。 ListView

Kotlin

// Defines a list of columns to retrieve from the Cursor and load into an output row
val wordListColumns : Array<String> = arrayOf(
        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 receive the Cursor columns for each row
val wordListItems = intArrayOf(R.id.dictWord, R.id.locale)

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

// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter)

Java

// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] wordListColumns =
{
    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 receive the Cursor columns for each row
int[] wordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
cursorAdapter = 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
    wordListColumns,                       // A string array of column names in the cursor
    wordListItems,                         // An integer array of view IDs in the row layout
    0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter);

注: Cursor を使用して ListView を返すには、カーソルに _ID という名前の列を含める必要があります。このため、前のクエリでは、_ID 列を取得し、 Words テーブルに対して表示されますが、これは ListView には表示されません。 この制限により、ほとんどのプロバイダにはそれぞれに _ID 列がある理由も説明されています。 共有します

クエリ結果からデータを取得する

クエリ結果は、表示するだけでなく他のタスクにも使用できます。対象 たとえば、単語リスト プロバイダからスペルを取得して、 利用できます。これを行うには、次の例に示すように Cursor 内の行を反復処理します。

Kotlin

/*
* Only executes if the cursor is valid. The User Dictionary Provider returns null if
* an internal error occurs. Other providers might throw an Exception instead of returning null.
*/
mCursor?.apply {
    // Determine the column index of the column named "word"
    val index: Int = getColumnIndex(UserDictionary.Words.WORD)

    /*
     * 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 get an
     * exception.
     */
    while (moveToNext()) {
        // Gets the value from the column
        newWord = getString(index)

        // Insert code here to process the retrieved word
        ...
        // End of while loop
    }
}

Java

// 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 might 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 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 の実装には「get」メソッドがいくつか用意されており、オブジェクトからさまざまなタイプのデータを取得できます。たとえば上記のスニペットでは getString() を使用しています。また、列のデータ型を示す値を返す getType() メソッドも使用しています。

コンテンツ プロバイダの権限

プロバイダのアプリで、他のアプリが許可する必要のある権限を指定できます。 プロバイダのデータにアクセスする必要があります。これらの権限により、ユーザーはどのデータが 許可されません。他のアプリは、プロバイダの要件に基づき、プロバイダにアクセスするために必要な権限をリクエストします。エンドユーザーがアプリをインストールするとき、リクエストされた権限が表示されます。

プロバイダのアプリケーションで権限が指定されていない場合、他のアプリケーションには権限がありません。 プロバイダのデータにアクセスすることはできません(プロバイダがエクスポートされていない場合を除く)。またコンポーネントも 状態にかかわらず、プロバイダのアプリケーションには、常に完全な読み取りと書き込みのアクセス権が 付与できます。

単語リスト プロバイダでは、 android.permission.READ_USER_DICTIONARY 権限(そこからデータを取得する)。 プロバイダには独立した android.permission.WRITE_USER_DICTIONARY があります。 データを挿入、更新、削除する権限が含まれます。

プロバイダにアクセスするために必要な権限を取得するには、アプリのマニフェスト ファイルで <uses-permission> 要素を使用して権限をリクエストします。Android Package Manager がアプリケーションをインストールすると、ユーザーは アプリケーションがリクエストするすべての権限を承認する必要があります。ユーザーが承認すると Package Manager がインストールを続行します。ユーザーが承認しなかった場合、パッケージ管理システムは インストールを停止します。

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

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

権限がプロバイダ アクセスに及ぼす影響については、 セキュリティに関するヒント

データの挿入、更新、削除

プロバイダからデータを取得する場合と同じ方法で、プロバイダ クライアントとプロバイダの ContentProvider の間のやり取りを使用してデータを変更します。対応する ContentProvider のメソッドに渡す引数を使用して ContentResolver のメソッドを呼び出します。プロバイダとプロバイダ セキュリティとプロセス間通信を自動的に処理します。

データの挿入

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

Kotlin

// Defines a new Uri object that receives the result of the insertion
lateinit var newUri: Uri
...
// Defines an object to contain the new values to insert
val newValues = ContentValues().apply {
    /*
     * Sets the values of each column and inserts the word. The arguments to the "put"
     * method are "column name" and "value".
     */
    put(UserDictionary.Words.APP_ID, "example.user")
    put(UserDictionary.Words.LOCALE, "en_US")
    put(UserDictionary.Words.WORD, "insert")
    put(UserDictionary.Words.FREQUENCY, "100")

}

newUri = contentResolver.insert(
        UserDictionary.Words.CONTENT_URI,   // The UserDictionary content URI
        newValues                           // The values to insert
)

Java

// Defines a new Uri object that receives the result of the insertion
Uri newUri;
...
// Defines an object to contain the new values to insert
ContentValues newValues = new ContentValues();

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

newUri = getContentResolver().insert(
    UserDictionary.Words.CONTENT_URI,   // The UserDictionary content URI
    newValues                           // 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 である。戻り値は、更新された行数です。

Kotlin

// Defines an object to contain the updated values
val updateValues = ContentValues().apply {
    /*
     * Sets the updated value and updates the selected words.
     */
    putNull(UserDictionary.Words.LOCALE)
}

// Defines selection criteria for the rows you want to update
val selectionClause: String = UserDictionary.Words.LOCALE + "LIKE ?"
val selectionArgs: Array<String> = arrayOf("en_%")

// Defines a variable to contain the number of updated rows
var rowsUpdated: Int = 0
...
rowsUpdated = contentResolver.update(
        UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
        updateValues,                      // The columns to update
        selectionClause,                   // The column to select on
        selectionArgs                      // The value to compare to
)

Java

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

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

// Defines a variable to contain the number of updated rows
int rowsUpdated = 0;
...
/*
 * Sets the updated value and updates the selected words.
 */
updateValues.putNull(UserDictionary.Words.LOCALE);

rowsUpdated = getContentResolver().update(
    UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
    updateValues,                      // The columns to update
    selectionClause,                   // The column to select on
    selectionArgs                      // The value to compare to
);

呼び出し時のユーザー入力をサニタイズする ContentResolver.update()。詳細情報 悪意のある入力から保護するをご覧ください。

データの削除

行の削除は、行データの取得に似ています。行の選択条件を指定します クライアント メソッドは削除された行数を返します。 次のスニペットでは、アプリ ID が "user" と一致する行を削除します。削除した行数がメソッドから返されます。

Kotlin

// Defines selection criteria for the rows you want to delete
val selectionClause = "${UserDictionary.Words.APP_ID} LIKE ?"
val selectionArgs: Array<String> = arrayOf("user")

// Defines a variable to contain the number of rows deleted
var rowsDeleted: Int = 0
...
// Deletes the words that match the selection criteria
rowsDeleted = contentResolver.delete(
        UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
        selectionClause,                   // The column to select on
        selectionArgs                      // The value to compare to
)

Java

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

// Defines a variable to contain the number of rows deleted
int rowsDeleted = 0;
...
// Deletes the words that match the selection criteria
rowsDeleted = getContentResolver().delete(
    UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
    selectionClause,                   // The column to select on
    selectionArgs                      // The value to compare to
);

呼び出し時のユーザー入力をサニタイズする ContentResolver.delete()。詳細情報 悪意のある入力からの保護のセクションをご覧ください。

プロバイダのデータ型

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

  • 整数
  • 長整数(long)
  • 浮動小数点
  • 長精度浮動小数点(double)

プロバイダがよく使用するもう一つのデータ型は、バイナリ ラージ オブジェクト(BLOB)です。 64 KB バイトの配列。使用可能なデータ型は、Cursor クラスの「get」メソッドで確認できます。

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

プロバイダは、定義する各コンテンツ URI の MIME データ型情報も保持します。Google Chat では MIME タイプ情報を使用して、アプリケーションが要求したデータを MIME タイプに基づいて処理のタイプを選択したりできます。通常は 複雑な構成のプロバイダを扱う場合は、 保存する必要があります。

たとえば連絡先プロバイダの ContactsContract.Data テーブルでは、MIME タイプを使用して、各行に格納されている連絡先データのタイプにラベルを付けます。コンテンツ URI に対応する MIME タイプを取得するには、ContentResolver.getType() を呼び出します。

MIME タイプのリファレンスのセクションでは、 標準とカスタムの両方の MIME タイプの構文を生成します。

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

アプリ開発では、次の 3 つの形式のプロバイダ アクセスが重要です。

以下のセクションでは、インテントを使用したバッチアクセスとバッチ変更について説明します。

バッチアクセス

プロバイダへのバッチアクセスは、多数の行を挿入する 1 回のメソッド呼び出しで複数のテーブルの行に格納できます。また、通常は一連の処理を実行する場合に、 アトミック オペレーションと呼ばれる、トランザクションとしてプロセスの境界を越えるオペレーションを実行します。

バッチモードでプロバイダにアクセスするには ContentProviderOperation オブジェクトの配列を作成してから、 コンテンツ プロバイダにディスパッチし、 ContentResolver.applyBatch()。テストに合格すると、 このメソッドに対するコンテンツ プロバイダのオーソリティではなく、特定のコンテンツ URI です。

これにより、配列内の各 ContentProviderOperation オブジェクトが機能するようになります。 別のテーブルと比較しますContentResolver.applyBatch() を呼び出すと、結果の配列が返されます。

ContactsContract.RawContacts コントラクト クラスの説明 には、バッチ挿入を示すコード スニペットが含まれています。

インテントを使用したデータアクセス

インテントを使用すると、コンテンツ プロバイダに間接的にアクセスできます。このユーザーに アプリにユーザーのアクセスが許可されていない場合でも、 権限があるアプリから結果インテントを取得するか、 ユーザーがその環境で作業できるようにすることです。

一時的な権限でアクセス権を取得する

適切なアクセス権がなくても、コンテンツ プロバイダのデータにアクセスできる その権限を持っているアプリにインテントを送信します。 URI 権限を含む結果インテントを受け取ります。 これは特定のコンテンツ URI の権限であり、権限を受け取るアクティビティが終了するまで効力を持ちます。永続的な権限を持つアプリは、結果のインテントにフラグを設定することで、一時的な権限を付与します。

注: これらのフラグは、コンテンツ URI にオーソリティが含まれているプロバイダへの全般的な読み取りアクセス権または書き込みアクセス権を付与するものではありません。アクセスは URI 自体に限定されます。

コンテンツ URI を別のアプリに送信する場合は、これらの URI を少なくとも 1 つ含めます 使用できます。これらのフラグは、 Android 11(API レベル 30)以降をターゲットとしているインテントの集合です。

  • コンテンツ URI が表すデータの読み取りまたは書き込み 渡されます。
  • パッケージを入手 公開設定に合致するコンテンツ プロバイダを含むアプリに URI オーソリティ。インテントを送信するアプリと、 2 つの異なるアプリが含まれている可能性があります。

プロバイダは、マニフェストでコンテンツ URI の URI 権限を定義します。ただし、 android:grantUriPermissions 属性の <provider> 各要素と <grant-uri-permission> 子要素です。 <provider> 要素です。URI 権限のメカニズムについては、 Android での権限に関するガイドをご覧ください。

たとえば、READ_CONTACTS 権限がない場合でも、連絡先プロバイダの連絡先データを取得できます。これは連絡先の誕生日にグリーティング メールを送信するアプリで利用できます。以前の READ_CONTACTS をリクエストしています。これにより、すべての ユーザーの連絡先とすべての情報を保護するには、ユーザーが アプリケーションが使用する連絡先に 直接アクセスできます手順は次のとおりです。

  1. アプリで、アクションを含むインテントを送信します。 ACTION_PICK と「連絡先」MIME タイプ CONTENT_ITEM_TYPE を使用し、 メソッド startActivityForResult()
  2. このインテントは、指定された IP アドレスの 連絡帳アプリの「選択」アクティビティがフォアグラウンドに表示されます。
  3. 選択アクティビティで、更新する連絡先をユーザーが選択します。この場合、選択アクティビティは setResult(resultcode, intent) を呼び出して、アプリに返すインテントを設定します。インテントにコンテンツ URI が含まれている ユーザーが選択した連絡先と「エクストラ」ですフラグ FLAG_GRANT_READ_URI_PERMISSION。これらのフラグにより、コンテンツ URI が指す連絡先のデータを読み取るための URI 権限がアプリに付与されます。その後、選択アクティビティは finish() を呼び出して、制御をアプリに返します。
  4. アクティビティがフォアグラウンドに戻り、システムがアクティビティの onActivityResult() メソッドを呼び出します。このメソッドは、連絡帳アプリの選択アクティビティによって作成された結果のインテントを受け取ります。
  5. 結果のインテントのコンテンツ URI を使用すると、マニフェストで永続的な読み取りアクセス権限をプロバイダにリクエストしていなくても、連絡先プロバイダから連絡先データを読み取ることができます。その後、連絡先の生年月日に関する情報を取得できます。 挨拶文を送信します。

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

アクセス権のないデータをユーザーが変更できるようにするもう 1 つの方法は、 権限のあるアプリケーションを起動して、ユーザーがそこで作業を行えるようにする。

たとえば、カレンダー アプリケーションは ACTION_INSERT インテントを使用して、 挿入 UI を使用できます。このインテントに「追加」のデータを渡すと、アプリはこのデータを使用して UI を事前入力します。定期的な予定は構文が複雑であるため、カレンダー プロバイダにイベントを挿入する場合は、ACTION_INSERT でカレンダー アプリを有効にしてからユーザーにイベントを挿入してもらうことをおすすめします。

ヘルパーアプリを使用してデータを表示する

アプリにアクセス権限がある場合でも、 別のアプリでデータを表示する場合です。たとえば、カレンダー アプリケーションは ACTION_VIEW インテントを使用して、特定の日付やイベントを表示できます。 これにより、独自の UI を作成しなくてもカレンダー情報を表示できます。 この機能について詳しくは、 カレンダー プロバイダの概要

インテントの送信先アプリは、このアプリでなくてもかまいません。 関連付けられていますたとえば、連絡先プロバイダから連絡先を取得して、連絡先の画像のコンテンツ URI を含む ACTION_VIEW インテントを画像ビューアに送信できます。

コントラクト クラス

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

たとえば単語リスト プロバイダには、コンテンツ URI と列名の定数を含むコントラクト クラス UserDictionary があります。「 Words テーブルのコンテンツ URI は、定数 UserDictionary.Words.CONTENT_URIUserDictionary.Words クラスには列名定数も含まれています。 ベスト プラクティスを実装しています。たとえば、クエリ射影は 次のように定義します。

Kotlin

val projection : Array<String> = arrayOf(
        UserDictionary.Words._ID,
        UserDictionary.Words.WORD,
        UserDictionary.Words.LOCALE
)

Java

String[] projection =
{
    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 タイプ文字列は、 複雑な type 値と subtype 値。複数の行の場合、型の値は常に次のようになります。

vnd.android.cursor.dir

単一行の場合、型の値は常に次のようになります。

vnd.android.cursor.item

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

vnd.android.cursor.item/phone_v2

サブタイプの値は phone_v2 です。

他のプロバイダ デベロッパーは、プロバイダの構成に基づいて、サブタイプの独自のパターンを作成できます。 テーブル名を定義します。たとえば、列車の時刻表を含むプロバイダについて考えてみます。プロバイダのオーソリティは 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 を定義します。

単一行のコンテンツ URI については、 コンテンツ URI セクション。