page.title=Membuat Penyedia Konten @jd:body

Dalam dokumen ini

  1. Mendesain Penyimpanan Data
  2. Mendesain URI Konten
  3. Mengimplementasikan Kelas ContentProvider
    1. Metode-Metode yang Diperlukan
    2. Mengimplementasikan metode query()
    3. Mengimplementasikan metode insert()
    4. Mengimplementasikan metode delete()
    5. Mengimplementasikan metode update()
    6. Mengimplementasikan metode onCreate()
  4. Mengimplementasikan Tipe MIME Penyedia Konten
    1. Tipe MIME untuk tabel
    2. Tipe MIME untuk file
  5. Mengimplementasikan Kelas Kontrak
  6. Mengimplementasikan Izin Penyedia Konten
  7. Elemen <provider>
  8. Intent dan Akses Data

Kelas-kelas utama

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

Contoh-Contoh Terkait

  1. Aplikasi contoh Note Pad

Lihat juga

  1. Dasar-Dasar Penyedia Konten
  2. Penyedia Kalender

Penyedia konten mengelola akses ke repository data pusat. Anda mengimplementasikan penyedia sebagai satu atau beberapa kelas dalam aplikasi Android, bersama elemen-elemen dalam file manifes. Salah satu kelas Anda mengimplementasikan subkelas {@link android.content.ContentProvider}, yang merupakan antarmuka antara penyedia Anda dan aplikasi lain. Walaupun penyedia konten dimaksudkan untuk menyediakan data bagi aplikasi lain, Anda tentu saja bisa memiliki aktivitas dalam aplikasi yang memungkinkan pengguna melakukan query dan memodifikasi data yang dikelola oleh penyedia Anda.

Bagian selebihnya dalam topik ini adalah daftar langkah-langkah dasar untuk membangun penyedia konten dan daftar API yang akan digunakan.

Sebelum Anda Mulai Membangun

Sebelum Anda mulai membangun penyedia, lakukanlah hal-hal berikut:

  1. Putuskan apakah Anda memerlukan penyedia konten. Anda perlu membangun sebuah penyedia konten jika ingin menyediakan salah satu atau beberapa dari fitur berikut:

    Anda tidak mengharuskan penyedia untuk menggunakan database SQLite jika hanya digunakan dalam aplikasi sendiri.

  2. Jika Anda belum siap melakukannya, bacalah topik Dasar-Dasar Penyedia Konten untuk mengetahui selengkapnya tentang penyedia.

Berikutnya, ikuti langkah-langkah ini untuk membangun penyedia:

  1. Desain penyimpanan mentah untuk data Anda. Penyedia konten menawarkan data dengan dua cara:
    Data file
    Data yang biasanya masuk ke dalam file, misalnya foto, audio, atau video. Simpan file dalam ruang privat aplikasi Anda. Untuk merespons permintaan file dari aplikasi lain, penyedia Anda bisa menawarkan handle ke file tersebut.
    Data "terstruktur"
    Data yang biasanya masuk ke dalam database, larik, atau struktur serupa. Simpan data dalam bentuk yang kompatibel dengan tabel berisi baris dan kolom. Baris mewakili entitas, misalnya satu orang atau satu barang inventori. Kolom mewakili beberapa data untuk entitas itu, misalnya nama orang atau harga barang. Cara umum untuk menyimpan tipe data ini adalah dalam database SQLite, namun Anda bisa menggunakan tipe penyimpanan apa saja yang persisten. Untuk mengetahui selengkapnya tentang tipe penyimpanan yang tersedia di sistem Android, lihat bagian Mendesain Penyimpanan Data.
  2. Definisikan sebuah implementasi konkret kelas {@link android.content.ContentProvider} dan metode yang diperlukannya. Kelas ini adalah antarmuka antara data Anda dan bagian selebihnya pada sistem Android. Untuk informasi selengkapnya tentang kelas ini, lihat bagian Mengimplementasikan Kelas ContentProvider.
  3. Definisikan string otoritas, semua URI isinya, dan nama-nama kolom penyedia. Jika Anda ingin penyedia aplikasi menangani intent, definisikan juga semua tindakan intent, data ekstra, dan flag. Definisikan juga izin yang akan Anda syaratkan terhadap aplikasi yang ingin mengakses data Anda. Anda harus mempertimbangkan pendefinisian semua nilai ini sebagai konstanta di kelas kontrak terpisah; nantinya, Anda bisa mengekspos kelas ini kepada pengembang lain. Untuk informasi selengkapnya tentang URI konten, lihat bagian Mendesain URI Konten. Untuk informasi selengkapnya tentang intent, lihat bagian Intent dan Akses Data.
  4. Tambahkan bagian opsional lainnya, seperti data contoh atau implementasi {@link android.content.AbstractThreadedSyncAdapter} yang bisa menyinkronkan data antara penyedia dan data berbasis cloud.

Mendesain Penyimpanan Data

Penyedia konten adalah antarmuka ke data yang disimpan dalam format terstruktur. Sebelum membuat antarmuka, Anda harus memutuskan cara menyimpan data. Anda bisa menyimpan data dalam bentuk apa saja yang Anda sukai, kemudian mendesain antarmuka untuk membaca dan menulis data yang diperlukan.

Berikut ini adalah beberapa teknologi penyimpanan data yang tersedia di Android:

Pertimbangan desain data

Berikut ini adalah beberapa tip untuk mendesain struktur data penyedia:

Mendesain URI Konten

URI konten adalah URI yang mengidentifikasi data dalam penyedia. URI Konten berisi nama simbolis seluruh penyedia (otoritasnya) dan sebuah nama yang menunjuk ke tabel atau file (path). Bagian id opsional menunjuk ke satu baris dalam tabel. Setiap metode akses data {@link android.content.ContentProvider} memiliki sebuah URI konten sebagai argumen; hal ini memungkinkan Anda menentukan tabel, baris, atau file yang akan diakses.

Dasar-dasar URI konten dijelaskan dalam topik Dasar-Dasar Penyedia Konten.

Mendesain otoritas

Penyedia biasanya memiliki otoritas tunggal, yang berfungsi sebagai nama internal Android-nya. Untuk menghindari konflik dengan penyedia lain, Anda harus menggunakan kepemilikan domain internet (secara terbalik) sebagai basis otoritas penyedia Anda. Karena saran ini juga berlaku untuk nama-nama paket Android, Anda bisa mendefinisikan otoritas penyedia sebagai perluasan dari nama paket yang berisi penyedia. Misalnya, jika nama paket Android Anda adalah com.example.<appname>, Anda harus memberikan penyedia Anda otoritas com.example.<appname>.provider.

Mendesain struktur path

Pengembang biasanya membuat URI konten dari otoritas dengan menambahkan path yang menunjuk ke masing-masing tabel. Misalnya, jika Anda memiliki dua tabel table1 dan table2, Anda mengombinasikan otoritas dari contoh sebelumnya untuk menghasilkan URI konten com.example.<appname>.provider/table1 dan com.example.<appname>.provider/table2. Path tidak dibatasi pada segmen tunggal, dan tidak harus berupa tabel untuk masing-masing tingkat path.

Menangani ID URI konten

Berdasarkan standar, penyedia menawarkan akses ke satu baris dalam tabel dengan menerima URI konten dengan sebuah nilai ID untuk baris itu di akhir URI. Juga berdasarkan standar, penyedia mencocokkan nilai ID dengan kolom _ID tabel, dan melakukan akses yang diminta terhadap baris yang cocok.

Standar ini memudahkan pola desain umum untuk aplikasi yang mengakses penyedia. Aplikasi melakukan query terhadap penyedia dan menampilkan {@link android.database.Cursor} yang dihasilkan dalam {@link android.widget.ListView} dengan menggunakan {@link android.widget.CursorAdapter}. Definisi {@link android.widget.CursorAdapter} mengharuskan salah satu kolom dalam {@link android.database.Cursor} berupa _ID

Pengguna kemudian mengambil salah satu baris yang ditampilkan dari UI untuk menemukan atau memodifikasi data. Aplikasi mengambil baris yang sesuai dari {@link android.database.Cursor} yang mendukung {@link android.widget.ListView}, mengambil nilai _ID untuk baris ini, menambahkannya ke URI konten, dan mengirim permintaan akses ke penyedia. Penyedia nanti bisa melakukan query atau modifikasi terhadap baris yang persis diambil pengguna.

Pola URI konten

Untuk membantu Anda memilih tindakan yang diambil bagi URI konten yang masuk, API penyedia menyertakan kelas praktis {@link android.content.UriMatcher}, yang memetakan "pola-pola" URI konten ke nilai-nilai integer. Anda bisa menggunakan nilai-nilai integer dalam pernyataan switch yang memilih tindakan yang diinginkan untuk URI konten atau URI yang cocok dengan pola tertentu.

Pola URI konten mencocokkan dengan URI konten menggunakan karakter wildcard:

Sebagai contoh desain dan pemrograman penanganan URI konten, perhatikan penyedia dengan otoritas com.example.app.provider yang mengenali URI konten berikut yang menunjuk ke tabel-tabel:

Penyedia juga mengenali URI konten ini jika baris ID ditambahkan ke URI, misalnya content://com.example.app.provider/table3/1 untuk baris yang diidentifikasi oleh 1 dalam table3.

Pola-pola URI konten berikut akan menjadi mungkin:

content://com.example.app.provider/*
Mencocokkan URI konten di penyedia.
content://com.example.app.provider/table2/*:
Mencocokkan URI konten untuk tabel-tabel dataset1 dan dataset2, namun tidak mencocokkan URI konten untuk table1 atau table3.
content://com.example.app.provider/table3/#: Mencocokkan URI konten untuk satu baris di table3, misalnya content://com.example.app.provider/table3/6 untuk baris yang diidentifikasi oleh 6.

Cuplikan kode berikut menunjukkan cara kerja metode di {@link android.content.UriMatcher}. Kode ini menangani URI seluruh tabel secara berbeda dengan URI untuk satu baris, menggunakan pola URI konten content://<authority>/<path> untuk tabel, dan content://<authority>/<path>/<id> untuk satu baris.

Metode {@link android.content.UriMatcher#addURI(String, String, int) addURI()} memetakan otoritas dan path ke nilai integer. Metode {@link android.content.UriMatcher#match(Uri) match()} menghasilkan nilai integer URI. Pernyataan switch memilih antara melakukan query seluruh tabel dan melakukan query satu record:

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
    }

Kelas lainnya, {@link android.content.ContentUris}, menyediakan metode praktis untuk menggunakan bagian id URI konten. Kelas-kelas {@link android.net.Uri} dan {@link android.net.Uri.Builder} menyertakan metode praktis untuk mengurai objek {@link android.net.Uri} yang ada dan membuat objek baru.

Mengimplementasikan Kelas ContentProvider

Instance {@link android.content.ContentProvider} mengelola akses ke satu set data terstruktur dengan menangani permintaan dari aplikasi lain. Semua bentuk akses pada akhirnya akan memanggil {@link android.content.ContentResolver}, yang kemudian memanggil metode konkret {@link android.content.ContentProvider} untuk mendapatkan akses.

Metode-metode yang diperlukan

Kelas abstrak {@link android.content.ContentProvider} mendefinisikan enam metode abstrak yang harus Anda implementasikan sebagai bagian dari subkelas konkret Anda sendiri. Semua metode ini kecuali {@link android.content.ContentProvider#onCreate() onCreate()} dipanggil oleh aplikasi klien yang berupaya mengakses penyedia konten Anda:

{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) query()}
Mengambil data dari penyedia Anda. Menggunakan argumen untuk memilih tabel yang akan di-query, baris dan kolom yang akan dihasilkan, dan urutan sortir hasilnya. Menghasilkan data berupa objek {@link android.database.Cursor}.
{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}
Menyisipkan baris baru ke dalam penyedia Anda. Menggunakan argumen untuk memilih tabel tujuan dan mendapatkan nilai-nilai kolom yang akan digunakan. Menghasilkan URI konten untuk baris yang baru disisipkan.
{@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()}
Memperbarui baris yang ada di penyedia Anda. Menggunakan argumen untuk memilih tabel dan baris yang akan diperbarui dan mendapatkan nilai-nilai kolom yang diperbarui. Menghasilkan jumlah baris yang diperbarui.
{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()}
Menghapus baris dari penyedia Anda. Menggunakan argumen untuk memilih tabel dan baris yang akan dihapus. Menghasilkan jumlah baris yang dihapus.
{@link android.content.ContentProvider#getType(Uri) getType()}
Menghasilkan tipe MIME yang sesuai dengan URI konten. Metode ini dijelaskan lebih detail di bagian Mengimplementasikan Tipe MIME Penyedia Konten.
{@link android.content.ContentProvider#onCreate() onCreate()}
Inisialisasi penyedia Anda. Sistem Android memanggil metode ini segera setelah membuat penyedia Anda. Perhatikan bahwa penyedia Anda tidak dibuat hingga objek {@link android.content.ContentResolver} mencoba mengaksesnya.

Perhatikan bahwa metode-metode ini memiliki signature yang sama dengan metode-metode {@link android.content.ContentResolver} yang sama namanya.

Implementasi metode-metode ini harus memperhitungkan hal-hal berikut:

Mengimplementasikan metode query()

Metode {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()} harus menghasilkan objek {@link android.database.Cursor}, atau jika gagal, melontarkan {@link java.lang.Exception}. Jika menggunakan database SQLite sebagai penyimpanan data, Anda bisa mengembalikan{@link android.database.Cursor} yang dikembalikan oleh salah satu metode query() dari kelas {@link android.database.sqlite.SQLiteDatabase}. Jika query tidak mencocokkan baris apa pun, Anda harus mengembalikan instance {@link android.database.Cursor} yang metode {@link android.database.Cursor#getCount()}-nya mengembalikan 0. Anda harus mengembalikan null hanya jika terjadi kesalahan internal selama proses query.

Jika Anda tidak menggunakan database SQLite sebagai penyimpanan data, gunakan salah satu subkelas konkret {@link android.database.Cursor}. Misalnya, kelas {@link android.database.MatrixCursor} mengimplementasikan kursor dengan masing-masing baris berupa larik {@link java.lang.Object}. Dengan kelas ini, gunakan {@link android.database.MatrixCursor#addRow(Object[]) addRow()} untuk menambahkan baris baru.

Ingatlah bahwa sistem Android harus mampu mengomunikasikan {@link java.lang.Exception} lintas batas proses. Android bisa melakukannya untuk eksepsi berikut yang mungkin berguna dalam menangani kesalahan query:

Mengimplementasikan metode insert()

Metode {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} menambahkan satu baris baru ke tabel yang sesuai, dengan menggunakan nilai-nilai dalam argumen {@link android.content.ContentValues}. Jika kolom nama tidak ada dalam argumen {@link android.content.ContentValues}, Anda mungkin perlu menyediakan nilai default untuknya, baik dalam kode penyedia atau dalam skema database Anda.

Metode ini harus mengembalikan URI konten untuk baris baru. Untuk membuatnya, tambahkan nilai _ID baris baru (atau kunci utama lainnya) ke tabel URI konten, dengan menggunakan {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}.

Mengimplementasikan metode delete()

Metode {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} tidak harus menghapus baris-baris dari penyimpanan data Anda secara fisik. Jika menggunakan adaptor sinkronisasi bersama penyedia, Anda harus mempertimbangkan penandaan baris yang dihapus dengan flag "delete"; bukan menghilangkan baris itu sepenuhnya. Adaptor sinkronisasi bisa memeriksa baris yang dihapus dan menghilangkannya dari server sebelum menghapusnya dari penyedia.

Mengimplementasikan metode update()

Metode {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) update()} mengambil argumen {@link android.content.ContentValues} yang sama dengan yang digunakan oleh {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}, dan argumen-argumen selection dan selectionArgs yang sama dengan yang digunakan oleh {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} dan {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()}. Hal ini bisa memungkinkan Anda menggunakan kembali kode di antara metode-metode ini.

Mengimplementasikan metode onCreate()

Sistem Android memanggil {@link android.content.ContentProvider#onCreate() onCreate()} saat memulai penyedia. Anda harus melakukan tugas-tugas inisialisasi yang berjalan cepat saja dalam metode ini, serta menunda pembuatan database dan pemuatan data hingga penyedia benar-benar menerima permintaan terhadap data. Jika Anda melakukan tugas yang memakan waktu dalam {@link android.content.ContentProvider#onCreate() onCreate()}, Anda akan memperlambat startup penyedia. Pada gilirannya, hal ini akan memperlambat respons dari penyedia terhadap aplikasi lain.

Misalnya, jika menggunakan database SQLite, Anda bisa membuat sebuah objek {@link android.database.sqlite.SQLiteOpenHelper} baru di {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}, kemudian membuat tabel-tabel SQL saat pertama kali membuka database itu. Untuk memperlancar hal ini, saat pertama Anda memanggil {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase getWritableDatabase()}, metode ini memanggil metode {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()} secara otomatis.

Dua cuplikan berikut memperagakan interaksi antara {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} dan {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()}. Cuplikan pertama adalah implementasi {@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();
    }
}

Cuplikan berikutnya adalah implementasi {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) SQLiteOpenHelper.onCreate()}, yang menyertakan kelas helper:

...
// 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);
    }
}

Mengimplementasikan Tipe MIME Penyedia Konten

Kelas {@link android.content.ContentProvider} memiliki dua metode untuk menghasilkan tipe-tipe MIME:

{@link android.content.ContentProvider#getType(Uri) getType()}
Salah satu metode wajib yang harus Anda implementasikan untuk setiap penyedia.
{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}
Sebuah metode yang diharapkan untuk Anda implementasikan jika penyedia Anda menawarkan file.

Tipe MIME untuk tabel

Metode {@link android.content.ContentProvider#getType(Uri) getType()} mengembalikan {@link java.lang.String} dengan format MIME yang menjelaskan tipe data yang dikembalikan oleh argumen URI konten. Argumen {@link android.net.Uri} bisa berupa pola, bukannya URI tertentu; dalam hal ini, Anda harus mengembalikan tipe data terkait URI konten yang cocok dengan polanya.

Untuk tipe data umum misalnya teks, HTML, atau JPEG, {@link android.content.ContentProvider#getType(Uri) getType()} harus mengembalikan tipe MIME standar untuk data itu. Daftar lengkap tipe standar ini tersedia di situs web IANA MIME Media Types.

Untuk URI konten yang menunjuk ke baris atau baris-baris data tabel, {@link android.content.ContentProvider#getType(Uri) getType()} harus mengembalikan tipe MIME dalam format MIME khusus vendor Android:

Misalnya, jika otoritas penyedia adalah com.example.app.provider, dan penyedia mengekspos tabel bernama table1, tipe MIME untuk beberapa baris dalam table1 adalah:

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

Untuk satu baris table1, tipe MIME adalah:

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

Tipe MIME untuk file

Jika penyedia Anda menawarkan file, implementasikan {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}. Metode ini menghasilkan larik {@link java.lang.String} tipe MIME untuk file yang bisa dikembalikan penyedia Anda untuk URI konten bersangkutan. Anda harus memfilter tipe MIME yang Anda tawarkan dengan argumen filter tipe MIME, sehingga Anda hanya mengembalikan tipe MIME yang ingin ditangani klien.

Misalnya, perhatikan penyedia yang menawarkan gambar foto sebagai file dalam format .jpg, .png, dan .gif. Jika aplikasi memanggil {@link android.content.ContentResolver#getStreamTypes(Uri, String) ContentResolver.getStreamTypes()} dengan string filter image/* (sesuatu yang merupakan "gambar"), maka metode {@link android.content.ContentProvider#getStreamTypes(Uri, String) ContentProvider.getStreamTypes()} harus mengembalikan larik:

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

Jika aplikasi tertarik pada file .jpg, maka aplikasi bisa memanggil {@link android.content.ContentResolver#getStreamTypes(Uri, String) ContentResolver.getStreamTypes()} dengan string filter *\/jpeg, dan {@link android.content.ContentProvider#getStreamTypes(Uri, String) ContentProvider.getStreamTypes()} harus mengembalikan:

{"image/jpeg"}

Jika penyedia Anda tidak menawarkan tipe MIME apa pun yang diminta dalam string filter, {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} harus mengembalikan null.

Mengimplementasikan Kelas Kontrak

Kelas kontrak adalah kelas public final yang berisi definisi konstanta untuk URI, nama kolom, tipe MIME, dan metadata lain yang melekat ke penyedia. Kelas membentuk sebuah kontrak antara penyedia dan aplikasi lain dengan memastikan bahwa penyedia bisa diakses dengan benar sekalipun ada perubahan pada nilai URI sesungguhnya, nama kolom, dan seterusnya.

Kelas kontrak juga membantu pengembang karena kelas ini biasanya memiliki nama-nama simbolik untuk konstantanya, sehingga memperkecil kemungkinan pengembang menggunakan nilai yang salah untuk nama kolom atau URI. Karena berupa kelas, kelas ini bisa berisi dokumentasi Javadoc. Lingkungan pengembangan terpadu (IDE) seperti Eclipse secara otomatis bisa melengkapi nama-nama konstanta dari kelas kontrak dan menampilkan Javadoc untuk konstanta.

Pengembang tidak bisa mengakses file kelas milik kelas kontrak dari aplikasi Anda, namun bisa mengompilasinya secara statis ke dalam aplikasi mereka dari file .jar yang Anda sediakan.

Kelas {@link android.provider.ContactsContract} dan kelas-kelas tersarangnya adalah contoh kelas kontrak.

Mengimplementasikan Izin Penyedia Konten

Izin dan akses untuk semua aspek sistem Android dijelaskan secara detail dalam topik Keamanan dan Izin. Topik Penyimpanan Data juga menjelaskan keamanan dan izin terkait dengan berbagai tipe penyimpanan. Singkatnya, poin-poin pentingnya adalah:

Jika Anda ingin menggunakan izin penyedia konten untuk mengontrol akses ke data Anda, maka Anda harus menyimpan data Anda dalam file internal, database SQLite, atau "cloud" (misalnya, di server jauh), dan Anda harus membuat file dan database tetap privat bagi aplikasi Anda.

Mengimplementasikan izin

Semua aplikasi bisa membaca dari atau menulis ke penyedia Anda, sekalipun data yang mendasari adalah privat, karena secara default penyedia Anda tidak mengatur izin. Untuk mengubahnya, atur izin untuk penyedia dalam file manifes, dengan menggunakan atribut atau elemen anak dari elemen <provider>. Anda bisa mengatur izin yang berlaku pada seluruh penyedia, atau pada tabel tertentu, atau bahkan pada record tertentu, atau ketiganya.

Anda mendefinisikan izin untuk penyedia dengan satu atau beberapa elemen <permission> dalam file manifes. Untuk membuat izin unik bagi penyedia, gunakan scoping (pengaturan lingkup) bergaya Java untuk atribut android:name. Misalnya, beri nama izin membaca dengan com.example.app.provider.permission.READ_PROVIDER.

Daftar berikut menjelaskan lingkup penyedia izin, mulai dengan izin yang berlaku pada seluruh penyedia kemudian menjadi semakin sempit. Izin yang lebih sempit akan didahulukan daripada izin yang berlingkup lebih luas:

Izin baca-tulis tunggal tingkat penyedia
Suatu izin yang mengontrol akses baca-tulis bagi seluruh penyedia, ditetapkan dengan atribut android:permission dari elemen <provider>.
Izin baca-tulis terpisah tingkat penyedia
Satu izin baca dan satu izin tulis untuk seluruh penyedia. Anda menetapkan keduanya dengan atribut android:readPermission dan android:writePermission dari elemen <provider>. Kedua izin akan didahulukan daripada izin yang diharuskan oleh android:permission.
Izin tingkat path
Izin baca, tulis, atau baca/tulis untuk URI konten dalam penyedia Anda. Anda menetapkan tiap URI yang ingin dikontrol dengan elemen anak <path-permission> dari elemen <provider>. Untuk setiap URI konten yang ditetapkan, Anda bisa menetapkan satu izin baca/tulis, satu izin baca, atau satu izin tulis, atau ketiganya. Izin baca dan tulis akan didahulukan daripada izin baca/tulis. Juga, izin tingkat path akan didahulukan daripada izin tingkat penyedia.
Izin sementara
Tingkat izin yang memberikan akses sementara ke aplikasi, sekalipun aplikasi itu tidak memiliki izin yang biasanya diminta. Fitur akses sementara mengurangi jumlah izin yang harus diminta aplikasi dalam manifesnya. Bila Anda mengaktifkan izin sementara, satu-satunya aplikasi yang memerlukan izin "permanen" untuk penyedia adalah aplikasi yang mengakses terus-menerus semua data Anda.

Perhatikan izin yang Anda perlukan untuk mengimplementasikan penyedia dan aplikasi email, bila Anda ingin memperbolehkan aplikasi penampil gambar dari luar menampilkan lampiran foto dari penyedia Anda. Untuk memberikan akses yang diperlukan kepada penampil gambar tanpa mengharuskan izin, siapkan izin sementara untuk URI konten bagi foto. Desainlah aplikasi email Anda agar bila pengguna ingin menampilkan foto, aplikasi itu akan mengirim intent berisi URI konten foto dan flag izin ke penampil gambar. Penampil gambar nanti bisa melakukan query penyedia email untuk mengambil foto, sekalipun penampil itu tidak memiliki izin baca normal untuk penyedia Anda.

Untuk mengaktifkan izin sementara, atur atribut android:grantUriPermissions dari elemen <provider>, atau tambahkan satu atau beberapa elemen anak <grant-uri-permission> ke elemen <provider> Anda. Jika menggunakan izin sementara, Anda harus memanggil {@link android.content.Context#revokeUriPermission(Uri, int) Context.revokeUriPermission()} kapan saja Anda menghilangkan dukungan untuk URI konten dari penyedia, dan URI konten dikaitkan dengan izin sementara.

Nilai atribut menentukan seberapa banyak penyedia Anda yang dijadikan bisa diakses. Jika atribut diatur ke true, maka sistem akan memberikan izin sementara kepada seluruh penyedia, dengan mengesampingkan izin lain yang diharuskan oleh izin tingkat penyedia atau tingkat path.

Jika flag ini diatur ke false, maka Anda harus menambahkan elemen-elemen anak <grant-uri-permission> ke elemen <provider> Anda. Tiap elemen anak menetapkan URI konten atau URI yang telah diberi akses sementara.

Untuk mendelegasikan akses sementara ke sebuah aplikasi, intent harus berisi {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} atau flag {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, atau keduanya. Keduanya diatur dengan metode {@link android.content.Intent#setFlags(int) setFlags()}.

Jika atribut android:grantUriPermissions tidak ada, atribut ini diasumsikan sebagai false.

Elemen <provider>

Seperti halnya komponen {@link android.app.Activity} dan {@link android.app.Service}, subkelas {@link android.content.ContentProvider} harus didefinisikan dalam file manifes untuk aplikasinya, dengan menggunakan elemen <provider>. Sistem Android mendapatkan informasi berikut dari elemen:

Otoritas ({@code android:authorities})
Nama-nama simbolik yang mengidentifikasi seluruh penyedia dalam sistem. Atribut ini dijelaskan lebih detail di bagian Mendesain URI Konten.
Nama kelas penyedia ( android:name )
Kelas yang mengimplementasikan {@link android.content.ContentProvider}. Kelas ini dijelaskan lebih detail di bagian Mengimplementasikan Kelas ContentProvider.
Izin
Atribut-atribut yang menetapkan izin yang harus dimiliki aplikasi lain untuk mengakses data penyedia:

Izin dan atribut-atribut yang sesuai dijelaskan lebih detail di bagian Mengimplementasikan Izin Penyedia Konten.

Atribut-atribut startup dan kontrol
Atribut-atribut ini menentukan cara dan waktu sistem Android memulai penyedia, karakteristik proses penyedia, dan pengaturan runtime lainnya:

Atribut-atribut ini didokumentasikan dengan lengkap dalam topik panduan pengembang untuk elemen <provider>.

Atribut-atribut informatif
Ikon dan label opsional untuk penyedia:

Atribut-atribut ini didokumentasikan dengan lengkap dalam topik panduan pengembang untuk elemen <provider>.

Intent dan Akses Data

Aplikasi bisa mengakses penyedia konten secara tidak langsung dengan sebuah {@link android.content.Intent}. Aplikasi tidak memanggil satu pun dari metode-metode {@link android.content.ContentResolver} atau {@link android.content.ContentProvider}. Sebagai gantinya, aplikasi mengirim intent yang memulai aktivitas, yang sering kali merupakan bagian dari aplikasi penyedia sendiri. Aktivitas tujuan bertugas mengambil dan menampilkan data dalam UI-nya. Bergantung pada tindakan dalam intent, aktivitas tujuan juga bisa meminta pengguna untuk membuat modifikasi pada data penyedia. Intent juga bisa berisi data "ekstra" yang menampilkan aktivitas tujuan dalam UI; pengguna nanti memiliki pilihan untuk mengubah data ini sebelum menggunakannya untuk mengubah data di penyedia.

Anda mungkin perlu menggunakan akses intent guna membantu memastikan integritas data. Penyedia Anda mungkin bergantung pada data yang disisipkan, diperbarui, dan dihapusnya sesuai dengan logika bisnis yang didefinisikan dengan ketat. Jika demikian halnya, memperbolehkan aplikasi lain mengubah data Anda secara langsung bisa menyebabkan data yang tidak sah. Jika Anda ingin pengembang menggunakan akses intent, pastikan untuk mendokumentasikannya secara saksama. Jelaskan kepada mereka alasan akses intent yang menggunakan UI aplikasi Anda sendiri adalah lebih baik daripada mencoba memodifikasi data dengan kode mereka.

Menangani sebuah intent masuk yang ingin memodifikasi data penyedia Anda tidak berbeda dengan menangani intent lainnya. Anda bisa mengetahui selengkapnya tentang penggunaan intent dengan membaca topik Intent dan Filter Intent.