コンテンツ プロバイダは、データのセントラル リポジトリへのアクセスを管理します。プロバイダは Android アプリの一部であり、多くの場合、データを操作する独自の UI を提供します。ただし、コンテンツ プロバイダは主に他の これらのアプリケーションは、プロバイダ クライアント オブジェクトを使用してプロバイダにアクセスします。プロバイダは データへの一貫した標準インターフェースを提供し、 安全なデータアクセスを実現します。
通常、コンテンツ プロバイダは次の 2 つのシナリオのいずれかで連携します。 別のアプリで既存のコンテンツ プロバイダにアクセスする場合や、 アプリ内で新しいコンテンツ プロバイダを作成し、他のアプリとデータを共有する。
このページ では、既存のコンテンツ プロバイダを利用する際の基本事項について説明しています。実装方法や コンテンツ プロバイダを使用する場合は、 <ph type="x-smartling-placeholder"></ph> コンテンツ プロバイダを作成する。
このトピックで説明する内容は次のとおりです。
- コンテンツ プロバイダの仕組み。
- コンテンツ プロバイダからのデータの取得に使用する API。
- コンテンツ プロバイダへのデータの挿入、データの更新、または削除に使用する API。
- プロバイダでの作業に役立つその他の API 機能。
概要
コンテンツ プロバイダは外部アプリに対し、リレーショナル データベースのテーブルに似た 1 つ以上のテーブルとしてデータを提供します。行はプロバイダが収集するなんらかのデータのインスタンスを表し、行の各列はインスタンスに対して収集した個々のデータを表します。
コンテンツ プロバイダは、アプリケーションのデータ ストレージ レイヤへのアクセスを さまざまな API とコンポーネントが存在します。図 1 に示すように、これには次のものが含まれます。
- アプリデータへのアクセスを他のアプリと共有する
- ウィジェットにデータを送信する
SearchRecentSuggestionsProvider
を使用して、検索フレームワークを介してアプリのカスタム検索候補を返すAbstractThreadedSyncAdapter
の実装を使用してアプリデータをサーバーと同期するCursorLoader
を使用して UI にデータを読み込む
プロバイダにアクセスする
コンテンツ プロバイダのデータにアクセスする場合は、アプリの Context
の ContentResolver
オブジェクトを使用し、クライアントとしてプロバイダと通信します。「
ContentResolver
オブジェクトは、プロバイダ オブジェクトである
ContentProvider
を実装するクラスのインスタンス。
プロバイダ オブジェクトはクライアントからデータ リクエストを受け取り、リクエストされたアクションを実施して、結果を返します。このオブジェクトには、プロバイダ オブジェクトの同じ名前のメソッドを呼び出すメソッドがありますが、
ContentProvider
の具象サブクラスのいずれかのインスタンス。「
ContentResolver
メソッドは、基本的な
CRUD(作成、取得、更新、削除)関数を使用します。
UI から ContentProvider
にアクセスするための一般的なパターンでは、CursorLoader
を使用してバックグラウンドで非同期クエリを実行します。「
UI の Activity
または Fragment
は
CursorLoader
をクエリに追加することで、
ContentResolver
を使用した ContentProvider
。
これにより、クエリの実行中に引き続き UI を使用できます。このパターンには、図 2 に示すようにさまざまなオブジェクトとのやり取りと、基となるストレージ メカニズムが含まれます。
注: アプリがプロバイダにアクセスするには、通常、マニフェスト ファイルで特定の権限をリクエストする必要があります。この開発パターンについては、 コンテンツ プロバイダの権限のセクション。
Android プラットフォームの組み込みプロバイダの 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 ステートメントと一致します。
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 の会話全体を
説明します。
プロバイダからデータを取得する基本的な手順は次のとおりです。
- プロバイダの読み取りアクセス権限をリクエストします。
- プロバイダにクエリを送信するコードを定義します。
読み取りアクセス権限をリクエストする
プロバイダからデータを取得するには、アプリケーションで読み取りアクセス権限を
接続します。実行時にこの権限をリクエストすることはできません。代わりに、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()
メソッドも使用しています。
クエリ結果リソースを解放する
Cursor
オブジェクトは、
不要になった場合はクローズして、関連するリソースが解放されるようにします。
できます。これを行うには、次のメソッドを呼び出します。
close()
、または
Java プログラミング言語の try-with-resources
ステートメント、または
Kotlin プログラミング言語の use()
関数。
コンテンツ プロバイダの権限
プロバイダのアプリで、他のアプリが許可する必要のある権限を指定できます。 プロバイダのデータにアクセスする必要があります。これらの権限により、ユーザーはどのデータが 許可されません。他のアプリは、プロバイダの要件に基づき、プロバイダにアクセスするために必要な権限をリクエストします。エンドユーザーがアプリをインストールするとき、リクエストされた権限が表示されます。
プロバイダのアプリケーションで権限が指定されていない場合、他のアプリケーションには権限がありません。 プロバイダのデータにアクセスすることはできません(プロバイダがエクスポートされていない場合を除く)。またコンポーネントも 状態にかかわらず、プロバイダのアプリケーションには、常に完全な読み取りと書き込みのアクセス権が 付与できます。
単語リスト プロバイダでは、
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 つの形式のプロバイダ アクセスが重要です。
-
バッチ アクセス: バッチ アクセスを作成し、
それを
ContentProviderOperation
クラスに適用し、ContentResolver.applyBatch()
。 -
非同期クエリ: 別のスレッドでクエリを実行します。Google Chat では
CursorLoader
オブジェクトを使用します。ローダのガイドに、この方法の例が記載されています。 - インテントを使用したデータアクセス: ただし、インテントを送信することはできません。 直接プロバイダに送る場合、そのプロバイダのアプリケーションにインテントを送信できます。 通常、プロバイダのデータを修正するのに最適な手段となります。
以下のセクションでは、インテントを使用したバッチアクセスとバッチ変更について説明します。
バッチアクセス
プロバイダへのバッチアクセスは、多数の行を挿入する 1 回のメソッド呼び出しで複数のテーブルの行に格納できます。また、通常は一連の処理を実行する場合に、 アトミック オペレーションと呼ばれる、トランザクションとしてプロセスの境界を越えるオペレーションを実行します。
バッチモードでプロバイダにアクセスするには
ContentProviderOperation
オブジェクトの配列を作成してから、
コンテンツ プロバイダにディスパッチし、
ContentResolver.applyBatch()
。テストに合格すると、
このメソッドに対するコンテンツ プロバイダのオーソリティではなく、特定のコンテンツ URI です。
これにより、配列内の各 ContentProviderOperation
オブジェクトが機能するようになります。
別のテーブルと比較しますContentResolver.applyBatch()
を呼び出すと、結果の配列が返されます。
ContactsContract.RawContacts
コントラクト クラスの説明
には、バッチ挿入を示すコード スニペットが含まれています。
インテントを使用したデータアクセス
インテントを使用すると、コンテンツ プロバイダに間接的にアクセスできます。このユーザーに アプリにユーザーのアクセスが許可されていない場合でも、 権限があるアプリから結果インテントを取得するか、 ユーザーがその環境で作業できるようにすることです。
一時的な権限でアクセス権を取得する
適切なアクセス権がなくても、コンテンツ プロバイダのデータにアクセスできる その権限を持っているアプリにインテントを送信します。 URI 権限を含む結果インテントを受け取ります。 これは特定のコンテンツ URI の権限であり、権限を受け取るアクティビティが終了するまで効力を持ちます。永続的な権限を持つアプリは、結果のインテントにフラグを設定することで、一時的な権限を付与します。
-
読み取り権限:
FLAG_GRANT_READ_URI_PERMISSION
-
書き込み権限:
FLAG_GRANT_WRITE_URI_PERMISSION
注: これらのフラグは、コンテンツ 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
をリクエストしています。これにより、すべての
ユーザーの連絡先とすべての情報を保護するには、ユーザーが
アプリケーションが使用する連絡先に
直接アクセスできます手順は次のとおりです。
-
アプリで、アクションを含むインテントを送信します。
ACTION_PICK
と「連絡先」MIME タイプCONTENT_ITEM_TYPE
を使用し、 メソッドstartActivityForResult()
。 - このインテントは、指定された IP アドレスの 連絡帳アプリの「選択」アクティビティがフォアグラウンドに表示されます。
-
選択アクティビティで、更新する連絡先をユーザーが選択します。この場合、選択アクティビティは
setResult(resultcode, intent)
を呼び出して、アプリに返すインテントを設定します。インテントにコンテンツ URI が含まれている ユーザーが選択した連絡先と「エクストラ」ですフラグFLAG_GRANT_READ_URI_PERMISSION
。これらのフラグにより、コンテンツ URI が指す連絡先のデータを読み取るための URI 権限がアプリに付与されます。その後、選択アクティビティはfinish()
を呼び出して、制御をアプリに返します。 -
アクティビティがフォアグラウンドに戻り、システムがアクティビティの
onActivityResult()
メソッドを呼び出します。このメソッドは、連絡帳アプリの選択アクティビティによって作成された結果のインテントを受け取ります。 - 結果のインテントのコンテンツ 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_URI
。
UserDictionary.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 セクション。