page.title=コンテンツ プロバイダの作成 @jd:body

本書の内容

  1. データ ストレージを設計する
  2. コンテンツ URI を設計する
  3. ContentProvider クラスを実装する
    1. 必須メソッド
    2. query() メソッドを実装する
    3. insert() メソッドを実装する
    4. delete() メソッドを実装する
    5. update() メソッドを実装する
    6. onCreate() メソッドを実装する
  4. コンテンツ プロバイダ MIME を実装する
    1. テーブルの MIME タイプ
    2. ファイルの MIME タイプ
  5. コントラクト クラスを実装する
  6. コンテンツ プロバイダ パーミッションを実装する
  7. <provider> 要素
  8. インテントとデータアクセス

キークラス

  1. {@link android.content.ContentProvider}
  2. {@link android.database.Cursor}
  3. {@link android.net.Uri}

関連サンプル

  1. Note Pad のサンプル アプリケーション

関連ドキュメント

  1. コンテンツ プロバイダの基本
  2. カレンダー プロバイダ

コンテンツ プロバイダは、データの中央リポジトリへのアクセスを管理します。Android アプリケーションでは 1 つ以上のクラスとしてプロバイダを実装し、要素をマニフェスト ファイルで実装します。 いずれか 1 つのクラスがサブラス {@link android.content.ContentProvider} を実装します。これは、プロバイダと他のアプリケーションとの間のインターフェースになります。 コンテンツ プロバイダは他のアプリケーションへのデータの提供を意図したものですが、ユーザーがプロバイダに管理されるデータを照会、修正するアクティビティをアプリケーション内に設定することもできます。

このトピックの残りの部分では、コンテンツ プロバイダと使用する API のリストをビルドするための基本的な手順を挙げていきます。

ビルドを開始する前に

ビルドを開始する前に、次の操作を行います。

  1. コンテンツ プロバイダが必要かどうかを決定します。次のような機能を 1 つ以上提供する場合に、コンテンツ プロバイダをビルドする必要があります。

    完全に独自アプリケーション内だけで使用する場合は、SQLite データベースを使用するプロバイダは必要はありません。

  2. 必要かどうかを決定していない場合は、プロバイダの詳細については「コンテンツ プロバイダの基本」のトピックをご覧ください。

次に、以下の手順に従ってプロバイダをビルドします。

  1. データの未処理のストレージを設計します。コンテンツ プロバイダは次の 2 つの方法でデータを提供します。
    ファイルデータ
    通常、写真、オーディオ、ビデオなどのファイルの形式となるデータです。 ファイルはアプリケーションのプライベート スペースに格納します。 ファイルに対する別のアプリケーションからの要求に応じて、プロバイダからファイルへのハンドルが提供されます。
    「構造化」データ
    通常、データベース、配列、類似の構造の形式となるデータです。 テーブルの行と列に互換性を持つ形式でデータが格納されます。行は、担当者や在庫にあるアイテムなどのエンティティを表します。 列は、担当者の名前やアイテムの価格などの、エンティティの一部のデータを表します。 一般的にこのタイプのデータは SQLite データベースに格納しますが、任意のタイプの永続ストレージを使用できます。 Android システムで使用できるストレージ タイプの詳細は、データ ストレージを設計するをご覧ください。
  2. {@link android.content.ContentProvider} クラスの具体的な実装とそれに必要なメソッドを定義します。 このクラスは、データと Android システムのそれ以外の部分とのインターフェースとなります。 このクラスの詳細は、ContentProvider クラスを実装するセクションをご覧ください。
  3. プロバイダの認証局の文字列、コンテンツ URI、列名を定義します。プロバイダのアプリケーションでインテントを処理する場合は、インテント アクション、エクストラ データ、フラグも定義します。 さらに、データにアクセスするアプリケーションに必要なパーミッションも定義します。 これらの値はすべて定数として個別のコントラクト クラスに定義するようにします。そうしておくと、後で他のデベロッパーに対してこのクラスを公開できます。 コンテンツ URI の詳細は、コンテンツ URI を設計するセクションをご覧ください。 インテントの使用に関する詳細については、インテントとデータアクセスセクションをご覧ください。
  4. サンプルデータや、プロバイダとクラウドベースのデータの間でデータを同期する {@link android.content.AbstractThreadedSyncAdapter} の実装などの、その他のオプション部分を追加します。

データ ストレージを設計する

コンテンツ プロバイダは、構造化された形式で保存されているデータへのインターフェースです。インターフェースを作成する前に、データの格納方法を決定しておく必要があります。 データは任意の形式で保存でき、必要に応じてデータの読み取りと書き込みを行うためのインターフェースを設計できます。

Android には、次のように、いくつかのデータ格納テクノロジーがあります。

データ設計上の考慮事項

次に、プロバイダのデータ構造を設計する際のヒントを示します。

コンテンツ URI を設計する

コンテンツ URI は、プロバイダのデータを特定する URI です。コンテンツ URI には、プロバイダ全体の識別名(認証局)とテーブルやファイルをポイントする名前(パス)が含まれます。 オプションの ID 部分は、テーブル内の個々の行を指します。 {@link android.content.ContentProvider} のそれぞれのデータアクセス メソッドは引数としてコンテンツ URI を使用します。これにより、アクセスするテーブル、行、ファイルを決定できます。

コンテンツ URI の基本については、「コンテンツ プロバイダの基本」トピックをご覧ください。

認証局を設計する

通常、プロバイダは、Android 内部の名前として使用される認証局を 1 つ持ちます。他のプロバイダとの競合を避けるためには、(逆に)プロバイダの認証局の基礎としてインターネット ドメインの所有権を使用する必要があります。 この推奨事項は Android パッケージ名にもあてはまるため、プロバイダを含むパッケージの名前の拡張子として、プロバイダの認証局を定義できます。 たとえば、Android パッケージ名が com.example.<appname> の場合、プロバイダに認証局 com.example.<appname>.provider を付与する必要があります。

パス構造を設計する

デベロッパーは通常、個々のテーブルを指すパスを末尾に追加して認証局からコンテンツ URI を作成します。 たとえば、table1table2 の 2 つのテーブルがある場合、前の例の認証局を組み合わせてコンテンツ URI com.example.<appname>.provider/table1com.example.<appname>.provider/table2 を作成します。 パスは 1 つのセグメントに限定されず、パスの各レベルにテーブルを作成する必要はありません。

コンテンツ URI ID を処理する

慣例として、URI の末尾にある行の ID 値を持つコンテンツ URI を受け取ることで、プロバイダはテーブル内の 1 つの行へのアクセスを提供します。さらに、慣例として、プロバイダは ID 値をテーブルの _ID 列とマッチングし、一致する行に対して要求されたアクセスを実行します。

この慣例により、プロバイダにアクセスするアプリの一般的なパターンを簡単に設計できます。アプリはプロバイダにクエリを実行し、{@link android.widget.CursorAdapter} を使用して、取得した {@link android.database.Cursor} を {@link android.widget.ListView} に表示します。 {@link android.widget.CursorAdapter} の定義には、{@link android.database.Cursor} のいずれかの列を _ID に設定する必要があります

その後、ユーザーはデータの確認や修正のために、表示された行のいずれかを UI から選択します。 アプリは {@link android.widget.ListView} を返す {@link android.database.Cursor} から対応する行を取得し、この行の _ID 値を取得し、その値をコンテンツ URI の末尾に追加して、プロバイダにアクセス要求を送ります。 その後、プロバイダは、ユーザーが選択した行にクエリや修正を実行します。

コンテンツ URI パターン

受け取ったコンテンツ URI に対するアクションを簡単に選択できるように、プロバイダ API には {@link android.content.UriMatcher} という便利なクラスが用意されています。このクラスはコンテンツ URI 「パターン」を正数値にマッピングします。 この正数値を switch 文で使用することで、特定のパターンに一致する 1 つ以上のコンテンツ URI に目的のアクションを選択できます。

コンテンツ URI パターンは、次のワイルドカード文字を使用するコンテンツ URI に一致します。

コンテンツ URI 処理の設計とコーディングの例として、テーブルを指す次のコンテンツ URI を認識する認証局 com.example.app.provider を持つプロバイダを考えてみます。

さらに、行 ID が末尾に追加されていると、プロバイダではこれらのコンテンツ URI が認識されます。たとえば、table31 で特定される行は content://com.example.app.provider/table3/1 になります。

次のようなコンテンツ URI パターンを使用できます。

content://com.example.app.provider/*
プロバイダの任意のコンテンツ URI に一致します。
content://com.example.app.provider/table2/*:
テーブル dataset1dataset2 のコンテンツ URI に一致しますが、table1table3 のコンテンツ URI には一致しません。
content://com.example.app.provider/table3/#: table3 の 1 つの行のコンテンツ URI に一致します。6 で特定される行は content://com.example.app.provider/table3/6 になります。

次のコード スニペットでは、{@link android.content.UriMatcher} のメソッドが機能する仕組みを示します。 このコードでは、テーブルにコンテンツ URI パターン content://<authority>/<path> を使用し、1 つの行に content://<authority>/<path>/<id> を使用することで、表全体の URI と 1 つの行の URI を異なる方法で処理します。

メソッド {@link android.content.UriMatcher#addURI(String, String, int) addURI()} は認証局とパスを正数値にマッピングします。 メソッド {@link android.content.UriMatcher#match(Uri) match()} は URI に正数値を返します。次のように、switch 文によって、クエリをテーブル全体に実行するか、1 つのレコードに実行するかが決まります。

public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher sUriMatcher;
...
    /*
     * The calls to addURI() go here, for all of the content URI patterns that the provider
     * should recognize. For this snippet, only the calls for table 3 are shown.
     */
...
    /*
     * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
     * in the path
     */
    sUriMatcher.addURI("com.example.app.provider", "table3", 1);

    /*
     * Sets the code for a single row to 2. In this case, the "#" wildcard is
     * used. "content://com.example.app.provider/table3/3" matches, but
     * "content://com.example.app.provider/table3 doesn't.
     */
    sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
...
    // Implements ContentProvider.query()
    public Cursor query(
        Uri uri,
        String[] projection,
        String selection,
        String[] selectionArgs,
        String sortOrder) {
...
        /*
         * Choose the table to query and a sort order based on the code returned for the incoming
         * URI. Here, too, only the statements for table 3 are shown.
         */
        switch (sUriMatcher.match(uri)) {


            // If the incoming URI was for all of table3
            case 1:

                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
                break;

            // If the incoming URI was for a single row
            case 2:

                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query
                 */
                selection = selection + "_ID = " uri.getLastPathSegment();
                break;

            default:
            ...
                // If the URI is not recognized, you should do some error handling here.
        }
        // call the code to actually do the query
    }

別の {@link android.content.ContentUris} には、コンテンツ URI の一部である id で作業するための便利なメソッドが備わっています。 クラス {@link android.net.Uri} と {@link android.net.Uri.Builder} には、既存の {@link android.net.Uri} オブジェクトを解析して新しいオブジェクトをビルドするための便利なメソッドがあります。

ContentProvider クラスを実装する

{@link android.content.ContentProvider} インスタンスは、他のアプリケーションからの要求を処理することで、一連の構造化されたデータへのアクセスを管理します。 どのような形式でアクセスする場合も、最終的には {@link android.content.ContentResolver} が呼び出されます。その後 {@link android.content.ContentProvider} の具象メソッドが呼び出され、アクセスを取得します。

必須メソッド

抽象クラス {@link android.content.ContentProvider} は 6 つの抽象メソッドを定義しますが、これらのメソッドは独自の具象サブクラスの一部として実装する必要があります。 次のように、{@link android.content.ContentProvider#onCreate() onCreate()} を除くこれらのメソッドは、コンテンツ プロバイダへのアクセスを試みるクライアント アプリケーションによって呼び出されます。

{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) query()}
プロバイダからデータを取得します。引数を使って、クエリを実行するテーブル、返す行と列、結果の並び順を選択します。 データは {@link android.database.Cursor} オブジェクトとして返されます。
{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}
プロバイダに新しい行を挿入します。引数を使って、挿入先のテーブルを選択し、使用する列の値を取得します。 新たに挿入した行のコンテンツ URI が返されます。
{@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()}
プロバイダ内の既存の行を更新します。引数を使って、更新するテーブルと行を選び、更新された列の値を取得します。 更新された行の数が返されます。
{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()}
プロバイダから行を削除します。引数を使って、削除するテーブルと行を選びます。 削除された行の数が返されます。
{@link android.content.ContentProvider#getType(Uri) getType()}
コンテンツ URI に対応する MIME タイプを返します。このメソッドの詳細は、コンテンツ プロバイダ MIME を実装するセクションをご覧ください。
{@link android.content.ContentProvider#onCreate() onCreate()}
プロバイダを初期化します。プロバイダが作成されると、Android システムがこのメソッドを即座に呼び出します。 {@link android.content.ContentResolver} オブジェクトがアクセスを試みるまでは、プロバイダは作成されません。

これらのメソッドが持つ署名は、同じ名前の {@link android.content.ContentResolver} メソッドの署名と同じになります。

これらのメソッドを実装する場合は、次の点を考慮する必要があります。

query() メソッドを実装する

{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()} メソッドは {@link android.database.Cursor} オブジェクトを返す必要があります。失敗した場合は、{@link java.lang.Exception} をスローします。 SQLite データベースをデータストレージとして使用する場合は、{@link android.database.sqlite.SQLiteDatabase} クラスのいずれかの query() メソッドが返す {@link android.database.Cursor} を返します。 クエリがどの行にも一致しない場合は、{@link android.database.Cursor#getCount()} メソッドが 0 を返す {@link android.database.Cursor} インスタンスを返す必要があります。 クエリプロセス中にエラーが発生した場合にのみ null を返します。

SQLite データベースをデータストレージとして使用しない場合は、{@link android.database.Cursor} クラスのいずれかの具象サブクラスを使用します。 たとえば、{@link android.database.MatrixCursor} クラスは、各行が {@link java.lang.Object} の配列となるカーソルを実装します。 このクラスでは、{@link android.database.MatrixCursor#addRow(Object[]) addRow()} を使って新しい行を追加します。

Android システムは、プロセスの境界をまたいで {@link java.lang.Exception} を送信できるように設定する必要があることにご注意ください。 Android では、クエリエラーを処理するのに便利な、次の例外を送信できます。

insert() メソッドを実装する

{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} メソッドは、{@link android.content.ContentValues} 引数の値を使用して、適切なテーブルに新しい行を追加します。 列名が {@link android.content.ContentValues} 引数にない場合は、プロバイダ コードがデータベース スキーマのいずれかで、デフォルト値を設定できます。

このメソッドは新しい行のコンテンツ URI を返します。作成するには、{@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()} を使用して、新しい行の _ID(または他のプライマリキー)の値をテーブルのコンテンツ URI の末尾に追加します。

delete() メソッドを実装する

{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} メソッドでは、データストレージから行を物理的に削除する必要はありません。 プロバイダで同期アダプタを使用する場合は、行全体を削除するのではなく、削除された行に「削除」フラグを付けるようにします。 同期アダプタは削除された行を探して、プロバイダから削除される前に、サーバーから行を削除できます。

update() メソッドを実装する

{@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()} メソッドは {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} が使用するのと同じ {@link android.content.ContentValues} 引数、{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} と {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()} が使用するのと同じ selectionselectionArgs 引数を使用します。 これにより、これらのメソッド間でコードを再利用できます。

onCreate() メソッドを実装する

Android システムはプロバイダを起動する際に {@link android.content.ContentProvider#onCreate() onCreate()} を呼び出します。このメソッドでは迅速に実行できる初期化タスクのみを実行するようにし、プロバイダが実際にデータの要求を受け取るまでは、データベースの作成とデータロードを保留します。 {@link android.content.ContentProvider#onCreate() onCreate()} で冗長なタスクを行う場合は、プロバイダの起動が遅くなります。 同様に、プロバイダから他のアプリケーションへの応答も遅くなります。

たとえば、SQLite データベースを使用すると、{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} に {@link android.database.sqlite.SQLiteOpenHelper} オブジェクトを作成し、データベースを初めて開くときに SQL テーブルを作成できます。 この操作を簡単にするために、{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase getWritableDatabase()} を初めて呼び出すときには、{@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()} メソッドが自動的に呼び出されます。

次の 2 つのスニペットは、{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} と {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()} との間のやり取りを表しています。 最初のスニペットは {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} の実装です。

public class ExampleProvider extends ContentProvider

    /*
     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
     * in a following snippet.
     */
    private MainDatabaseHelper mOpenHelper;

    // Defines the database name
    private static final String DBNAME = "mydb";

    // Holds the database object
    private SQLiteDatabase db;

    public boolean onCreate() {

        /*
         * Creates a new helper object. This method always returns quickly.
         * Notice that the database itself isn't created or opened
         * until SQLiteOpenHelper.getWritableDatabase is called
         */
        mOpenHelper = new MainDatabaseHelper(
            getContext(),        // the application context
            DBNAME,              // the name of the database)
            null,                // uses the default SQLite cursor
            1                    // the version number
        );

        return true;
    }

    ...

    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which table to open, handle error-checking, and so forth

        ...

        /*
         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
         *
         */
        db = mOpenHelper.getWritableDatabase();
    }
}

次のスニペットは {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()} の実装であり、ヘルパークラスを含みます。

...
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    "main " +                       // Table's name
    "(" +                           // The columns in the table
    " _ID INTEGER PRIMARY KEY, " +
    " WORD TEXT"
    " FREQUENCY INTEGER " +
    " LOCALE TEXT )";
...
/**
 * Helper class that actually creates and manages the provider's underlying data repository.
 */
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {

    /*
     * Instantiates an open helper for the provider's SQLite data repository
     * Do not do database creation and upgrade here.
     */
    MainDatabaseHelper(Context context) {
        super(context, DBNAME, null, 1);
    }

    /*
     * Creates the data repository. This is called when the provider attempts to open the
     * repository and SQLite reports that it doesn't exist.
     */
    public void onCreate(SQLiteDatabase db) {

        // Creates the main table
        db.execSQL(SQL_CREATE_MAIN);
    }
}

ContentProvider MIME タイプを実装する

{@link android.content.ContentProvider} クラスには、MIME タイプを返すための次の 2 つのクラスがあります。

{@link android.content.ContentProvider#getType(Uri) getType()}
任意のプロバイダに実装する必要がある必須メソッドの 1 つです。
{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}
ファイルを提供するプロバイダの場合に、実装が考えられるメソッドです。

テーブルの MIME タイプ

{@link android.content.ContentProvider#getType(Uri) getType()} メソッドは、MIME 形式で {@link java.lang.String} を返します。これは、コンテンツ URI 引数によって返されるデータのタイプを表します。 {@link android.net.Uri} 引数には特定の URI ではなくパターンを指定することもできます。この場合、パターンに一致するコンテンツ URI に関連付けられているデータのタイプを返します。

テキスト、HTML、JPEG といった一般的なタイプのデータの場合、{@link android.content.ContentProvider#getType(Uri) getType()} では該当するデータの標準的な MIME タイプを返す必要があります。 利用できるこれらの標準的なタイプの詳細なリストは、「IANA MIME Media Types」のウェブサイトをご覧ください。

テーブル データの 1 つ以上の行を指すコンテンツ URI の場合、{@link android.content.ContentProvider#getType(Uri) getType()} は、Android のベンダー固有の MIME 形式で MIME タイプを返す必要があります。

たとえば、プロバイダの認証局が com.example.app.provider の場合、テーブル名は table1 となり、table1 の複数行の MIME タイプは次のようになります。

vnd.android.cursor.dir/vnd.com.example.provider.table1

table1 の 1 つの行の場合は、MIME タイプは次のようになります。

vnd.android.cursor.item/vnd.com.example.provider.table1

ファイルの MIME タイプ

ファイルを提供するプロバイダの場合は、{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} を実装します。 このメソッドは、プロバイダが特定のコンテンツ URI に対して返すことができるファイルの MIME タイプの {@link java.lang.String} 配列を返します。クライアントで処理する MIME タイプのみを返すには、MIME タイプフィルタ引数によって、提供する MIME タイプをフィルタする必要があります。

たとえば、写真画像を .jpg.png.gif 形式で提供するプロバイダについて考えてみます。 アプリケーションが {@link android.content.ContentResolver#getStreamTypes(Uri, String) ContentResolver.getStreamTypes()} をフィルタ文字列 image/* (何らかの形式の「画像」)で呼び出す場合、{@link android.content.ContentProvider#getStreamTypes(Uri, String) ContentProvider.getStreamTypes()} メソッドは次のような配列を返します。

{ "image/jpeg", "image/png", "image/gif"}

アプリの対象が .jpg ファイルのみであり、アプリが {@link android.content.ContentResolver#getStreamTypes(Uri, String) ContentResolver.getStreamTypes()} をフィルタ文字列 *\/jpeg で呼び出す場合、{@link android.content.ContentProvider#getStreamTypes(Uri, String) ContentProvider.getStreamTypes()} は次の項目を返します。

{"image/jpeg"}

プロバイダが、フィルタ文字列で要求した MIME タイプを提供していない場合、{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} は null を返します。

コントラクト クラスを実装する

コントラクト クラスは public final クラスであり、URI、列名、MIME タイプ、プロバイダに関連するその他のメタデータを含みます。 このクラスは、URI や列名などの実際の値が変更された場合にも、プロバイダに正しくアクセスできることを保証することで、プロバイダとその他のアプリケーションとの間にコントラクトを構築します。

さらに、通常、コントラクト クラスではその定数にニーモニック名が設定されているため、デベロッパーが列名や URI に誤った値を使用しにくいようになっています。 クラスであるため、Javadoc ドキュメントを含めることができます。 Eclipse などの統合型開発環境では、コントラクト クラスから定数名が自動的に設定され、定数の Javadoc が表示されます。

デベロッパーはアプリケーションからコントラクト クラスのクラスファイルにアクセスできませんが、提供する .jar ファイルからクラスファイルをアプリケーションに静的にコンパイルできます。

{@link android.provider.ContactsContract} クラスとそのネストされたクラスは、コントラクト クラスの例です。

コンテンツ プロバイダ パーミッションを実装する

Android システムのあらゆる領域に対するパーミッションとクラスについては、「セキュリティとパーミッション」トピックをご覧ください。 また、トピック「Data Storage」にも、ストレージのさまざまなタイプに有効なセキュリティとパーミッションについて記載されています。 重要な点をまとめると次のようになります。

コンテンツ プロバイダ パーミッションを使用してデータへのアクセスを制御する場合は、内部ファイル、SQLite データベース、「クラウド」(リモート サーバーなど)にデータを格納し、ファイルやデータベースを自分のアプリケーションにのみ公開するように設定します。

パーミッションを実装する

デフォルトではプロバイダにはパーミッションが設定されていないため、基礎となるデータがプライベートに設定されている場合でも、すべてのアプリケーションは自分のプロバイダからの読み取りとプロバイダへの書き込みを実行できます。 これを変更するには、 <provider> 要素の属性や子要素を使用して、マニフェスト ファイルでプロバイダのパーミッションを設定します。 プロバイダ全体に適用するパーミッションを設定することもできますし、特定のテーブル、特定のレコード、これら 3 つすべてに適用するパーミッションを設定することもできます。

マニフェスト ファイルで 1 つ以上の <permission> 要素を使用して、プロバイダのパーミッションを定義します。 自分のプロバイダに独自のパーミッションを作成するには、 android:name 属性に Java スタイルのスコーピングを使用します たとえば、読み取りパーミッションの名前を com.example.app.provider.permission.READ_PROVIDER とします。

次のリストは、プロバイダ パーミッションの範囲を示しています。プロバイダ全体に適用するパーミッションから始まり、より詳細な範囲のものになっています。 より広い範囲を持つパーミッションよりも、範囲が限定されるパーミッションの方が優先されます。

プロバイダ レベルで 1 つの読み取りと書き込みのパーミッション
1 つのパーミッションでプロバイダ全体の読み取りと書き込みのアクセスの両方を制御します。 <provider> 要素の android:permission 属性で指定します。
プロバイダ レベルで別々の読み取りと書き込みパーミッション
プロバイダ全体の読み取りパーミッションと書き込みパーミッションです。これらのパーミッションは <provider> 要素の android:readPermission 属性と android:writePermission 属性で指定します。 これらは、 android:permission で要求するパーミッションよりも優先されます。
パスレベルのパーミッション
プロバイダのコンテンツ URI の読み取り、書き込み、読み取り / 書き込みパーミッションです。 <provider> 要素の <path-permission> 子要素を使用して、制御対象の各 URI を指定します。 指定する各コンテンツ URI に対して、読み取り / 書き込みパーミッション、読み取りパーミッション、書き込みパーミッション、3 つすべてを指定できます。 読み取りパーミッションと書き込みパーミッションは、読み取り / 書き込みパーミッションに優先します。 また、パスレベルのパーミッションはプロバイダ レベルのパーミッションに優先します。
一時的なパーミッション
アプリケーションへの一時的なアクセスを付与するパーミッション レベルです。アプリケーションに通常必要とするパーミッションが付与されていない場合でも付与できます。 一時的なアクセス機能により、アプリケーションのマニフェストに必要なパーミッションの数を減らせます。 一時的なパーミッションを有効にすると、プロバイダへの「永続的な」パーミッションを必要とするアプリケーションのみが、継続的にすべてのデータにアクセスできます。

プロバイダからの写真の添付ファイルを外部の画像ビューワ アプリケーションで表示する場合に、メール プロバイダとアプリに実装するパーミッションを考えてみます。 パーミッションを要求しなくても、画像ビューワに必要なアクセスを付与するには、写真のコンテンツ URI に一時的なパーミッションを設定します。 ユーザーが写真を表示しようとしたときに、アプリから写真のコンテンツ URI とパーミッション フラグを含むインテントを画像ビューワに送信するようにメール アプリを設計します。 ビューワにプロバイダの通常の読み取りパーミッションが付与されていない場合でも、画像ビューワはメール プロバイダにクエリを実行して写真を取得します。

一時的なパーミッションを有効にするには、 <provider> 要素の android:grantUriPermissions 属性を設定するか、1 つ以上の <grant-uri-permission> 子要素を <provider> 要素に追加します。 一時的なパーミッションを使用する場合、プロバイダからのコンテンツ URI のサポートを削除したときは常に {@link android.content.Context#revokeUriPermission(Uri, int) Context.revokeUriPermission()} を呼び出す必要があります。そうすると、コンテンツ URI が一時的なパーミッションに関連付けられます。

属性の値によって、プロバイダへのアクセスレベルが決まります。 属性を true に設定すると、システムによってプロバイダ全体への一時的なパーミッションが付与されます。このパーミッションは、プロバイダ レベルやパスレベルのパーミッションで必要になるその他のパーミッションよりも優先されます。

このフラグを false に設定する場合は、 <grant-uri-permission> 子要素を <provider> 要素に追加する必要があります。 それぞれの子要素は、一時的なアクセスが付与される 1 つ以上のコンテンツ URI を指定します。

一時的なアクセスをアプリケーションに委任するには、インテントに {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} フラグか {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} フラグ、またはその両方を含める必要があります。 これらは、{@link android.content.Intent#setFlags(int) setFlags()} メソッドで設定します。

android:grantUriPermissions 属性がない場合は、false であると仮定されます。

<provider> 要素

{@link android.app.Activity} コンポーネントや {@link android.app.Service} コンポーネントと同様に、 <provider> 要素を使用して {@link android.content.ContentProvider} のサブクラスをアプリケーションのマニフェスト ファイルに定義する必要があります。 Android システムは要素から次の情報を取得します。

認証局({@code android:authorities}
システム内のプロバイダ全体を特定する識別名です。この属性の詳細は、コンテンツ URI を設計するセクションをご覧ください。
プロバイダ クラス名( android:name
{@link android.content.ContentProvider} を実装するクラスです。このクラスの詳細は、ContentProvider クラスを実装するセクションをご覧ください。
パーミッション
他のアプリケーションがプロバイダのデータにアクセスするのに必要なパーミッションを指定する属性です。

パーミッションとそれに対応する属性の詳細は、コンテンツ プロバイダ パーミッションを実装するセクションをご覧ください。

起動と制御の属性
これらの属性は、Android システムの起動方法とそのタイミング、プロバイダのプロセスの特性、その他の実行時の設定を決定します。

属性に関する詳細は、開発ガイドの <provider> 要素に関するトピックに記載されています。

情報に関する属性
プロバイダのオプションのアイコンとラベルです。

属性に関する詳細は、開発ガイドの <provider> 要素に関するトピックに記載されています。

インテントとデータアクセス

{@link android.content.Intent} を使用すると、アプリケーションはコンテンツ プロバイダに間接的にアクセスできます。 アプリケーションは、{@link android.content.ContentResolver} や {@link android.content.ContentProvider} のメソッドを呼び出しません。 その代わりに、アクティビティを起動するインテントを送信します。これは通常、プロバイダ独自のアプリケーションの一部となっています。 対象のアクティビティは UI のデータの取得と表示を担当します。インテントのアクションに応じて、対象のアクティビティにより、ユーザーにプロバイダのデータの修正を求めるメッセージが表示されることがあります。 さらに、インテントには、対象のアクティビティが UI に表示する「エクストラ」データが含まれることがあります。ユーザーは使用前に、このデータを変更してプロバイダのデータを修正することもできます。

インテント アクセスを使用して、データの整合性を保証できます。プロバイダのデータの挿入、更新、削除は、厳格に定義されたビジネス ロジックによって規定されることがあります。 この場合、他のアプリケーションにデータを直接修正できるように許可すると、無効なデータが発生することがあります。 デベロッパーがインテント アクセスを使用する場合は、すべての操作を完全に文書化します。 コードを使用してデータを修正するよりも、独自のアプリケーションの UI を使用したインテント アクセスの方が優れている理由を説明します。

プロバイダのデータを修正するための受信インテントの処理は、その他のインテントの処理と同じです。 インテントの使用についての詳細は、「インテントとインテント フィルタ」をご覧ください。