page.title=Membuat Penyedia Konten @jd:body
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 penyedia, lakukanlah hal-hal berikut:
Anda tidak mengharuskan penyedia untuk menggunakan database SQLite jika hanya digunakan dalam aplikasi sendiri.
Berikutnya, ikuti langkah-langkah ini untuk membangun penyedia:
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:
Ingatlah bahwa Anda tidak harus menggunakan database untuk mengimplementasikan repository. Penyedia muncul secara eksternal sebagai satu set tabel, yang serupa dengan sebuah database relasional, namun ini bukan persyaratan untuk implementasi internal penyedia.
Berikut ini adalah beberapa tip untuk mendesain struktur data penyedia:
_ID
.
Anda juga bisa menggunakan BLOB untuk mengimplementasikan tabel yang tidak bergantung skema. Dalam tipe tabel ini, Anda mendefinisikan kolom kunci utama, kolom tipe MIME, dan satu atau beberapa kolom generik sebagai BLOB. Arti dari data dalam kolom-kolom BLOB ditunjukkan oleh nilai dalam kolom tipe MIME. Cara ini memungkinkan Anda menyimpan berbagai tipe baris dalam tabel yang sama. Tabel "data" {@link android.provider.ContactsContract.Data} Penyedia Kontak adalah contoh tabel yang tidak bergantung skema tersebut.
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.
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
.
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.
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.
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:
*
: Mencocokkan string yang memiliki karakter yang sah dengan panjang berapa saja.
#
: Mencocokkan string karakter numerik dengan panjang berapa saja.
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:
content://com.example.app.provider/table1
: Tabel bernama table1
.
content://com.example.app.provider/table2/dataset1
: Tabel bernama
dataset1
.
content://com.example.app.provider/table2/dataset2
: Tabel bernama
dataset2
.
content://com.example.app.provider/table3
: Tabel bernama table3
.
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/*
content://com.example.app.provider/table2/*
:
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.
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.
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:
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:
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:
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()}.
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.
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.
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); } }
Kelas {@link android.content.ContentProvider} memiliki dua metode untuk menghasilkan tipe-tipe MIME:
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:
vnd
android.cursor.item/
android.cursor.dir/
vnd.<name>
.<type>
Anda menyediakan <name>
dan <type>
.
Nilai <name>
harus unik secara global,
dan nilai <type>
harus unik bagi pola URI
yang sesuai. Pilihan tepat untuk <name>
adalah nama perusahaan Anda atau
sebagian dari nama paket Android aplikasi Anda. Pilihan tepat untuk
<type>
adalah string yang mengidentifikasi tabel yang terkait dengan
URI.
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
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
.
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.
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.
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:
android:permission
dari
elemen
<provider>
.
android:readPermission
dan
android:writePermission
dari elemen
<provider>
. Kedua izin akan didahulukan daripada izin yang diharuskan oleh
android:permission
.
<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.
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
.
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:
android:name
)
android:grantUriPermssions
: Flag izin sementara.
android:permission
: Izin baca/tulis tunggal untuk tingkat penyedia.
android:readPermission
: Izin baca untuk tingkat penyedia.
android:writePermission
: Izin tulis untuk tingkat penyedia.
Izin dan atribut-atribut yang sesuai dijelaskan lebih detail di bagian Mengimplementasikan Izin Penyedia Konten.
android:enabled
: Flag yang memperbolehkan sistem untuk memulai penyedia.
android:exported
: Flag yang memperbolehkan aplikasi lain untuk menggunakan penyedia ini.
android:initOrder
: Urutan yang digunakan untuk memulai penyedia ini,
relatif terhadap penyedia lain dalam proses yang sama.
android:multiProcess
: Flag yang memperbolehkan sistem untuk memulai penyedia
dalam proses yang sama dengan proses klien pemanggil.
android:process
: Nama proses tempat penyedia harus
berjalan.
android:syncable
: Flag yang menunjukkan bahwa data penyedia harus
disinkronkan dengan data di server.
Atribut-atribut ini didokumentasikan dengan lengkap dalam topik panduan pengembang untuk elemen
<provider>
.
android:icon
: Sumber daya drawable, berisi ikon untuk penyedia.
Ikon ini muncul di sebelah label penyedia dalam daftar aplikasi dalam menu
Settings > Apps > All.
android:label
: Label informatif yang menjelaskan penyedia atau
datanya, atau keduanya. Label ini muncul dalam daftar aplikasi di
Settings > Apps > All.
Atribut-atribut ini didokumentasikan dengan lengkap dalam topik panduan pengembang untuk elemen
<provider>
.
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.