page.title=Dasar-Dasar Penyedia Konten @jd:body
Penyedia konten mengelola akses ke repository data pusat. Penyedia adalah bagian dari aplikasi Android, yang sering menyediakan UI-nya sendiri untuk menggunakan data. Akan tetapi, penyedia konten terutama dimaksudkan untuk digunakan oleh aplikasi lain, yang mengakses penyedia itu melalui objek klien penyedia. Bersama-sama, penyedia dan klien penyedia menawarkan antarmuka standar yang konsisten ke data yang juga menangani komunikasi antar-proses dan akses data aman.
Topik ini menerangkan dasar-dasar dari hal-hal berikut:
Penyedia konten menyajikan data ke aplikasi eksternal sebagai satu atau beberapa tabel yang serupa dengan tabel-tabel yang ditemukan dalam database relasional. Sebuah baris mewakili instance beberapa tipe data yang dikumpulkan penyedia, dan tiap kolom dalam baris mewakili sepotong data yang dikumpulkan untuk sebuah instance.
Misalnya, salah satu penyedia bawaan di platform Android adalah kamus pengguna, yang menyimpan ejaan kata-kata tidak-standar yang ingin disimpan pengguna. Tabel 1 mengilustrasikan wujud data yang mungkin ada dalam tabel penyedia ini:
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 |
Dalam tabel 1, tiap baris mewakili instance sebuah kata yang mungkin tidak
ditemukan dalam kamus standar. Tiap kolom mewakili beberapa data untuk kata itu, misalnya
bahasa lokal tempat kata itu ditemukan kali pertama. Header kolom adalah nama kolom yang disimpan dalam
penyedia. Untuk mengacu ke bahasa lokal suatu baris, Anda mengacu ke kolom locale
-nya. Untuk
penyedia ini, kolom _ID
berfungsi sebagai "kunci utama" kolom yang
dipelihara oleh penyedia secara otomatis.
Catatan: Penyedia tidak diharuskan memiliki kunci utama, dan tidak diharuskan
menggunakan _ID
sebagai nama kolom kunci utama jika kunci itu ada. Akan tetapi,
jika Anda ingin mengikat data dari penyedia ke {@link android.widget.ListView}, salah satu
nama kolom harus _ID
. Ketentuan ini dijelaskan secara lebih detail di bagian
Menampilkan hasil query.
Aplikasi mengakses data dari penyedia konten dengan sebuah objek klien {@link android.content.ContentResolver}. Objek ini memiliki metode yang memanggil metode dengan nama identik dalam objek penyedia, instance salah satu subkelas konkret dari {@link android.content.ContentProvider}. Metode-metode {@link android.content.ContentResolver} menyediakan fungsi-fungsi dasar "CRUD" (create, retrieve, update, dan delete) pada penyimpanan yang persisten.
Objek {@link android.content.ContentResolver} dalam proses aplikasi klien dan objek {@link android.content.ContentProvider} dalam aplikasi yang memiliki penyedia itu secara otomatis akan menangani komunikasi antar-proses. {@link android.content.ContentProvider} juga berfungsi sebagai lapisan abstraksi antara repository datanya dan penampilan eksternal data sebagai tabel.
Catatan: Untuk mengakses penyedia, aplikasi Anda biasanya harus meminta izin tertentu dalam file manifesnya. Hal ini dijelaskan lebih detail di bagian Izin Penyedia Konten
Misalnya, untuk mendapatkan daftar kata dan lokalnya dari Penyedia Kamus Pengguna, Anda memanggil {@link android.content.ContentResolver#query ContentResolver.query()}. Metode {@link android.content.ContentResolver#query query()} memanggil metode {@link android.content.ContentProvider#query ContentProvider.query()} yang didefinisikan oleh Penyedia Kamus Pengguna. Baris-baris kode berikut menunjukkan sebuah panggilan {@link android.content.ContentResolver#query ContentResolver.query()}:
// Queries the user dictionary and returns results mCursor = getContentResolver().query( UserDictionary.Words.CONTENT_URI, // The content URI of the words table mProjection, // The columns to return for each row mSelectionClause // Selection criteria mSelectionArgs, // Selection criteria mSortOrder); // The sort order for the returned rows
Tabel 2 menampilkan cara argumen untuk {@link android.content.ContentResolver#query query(Uri,projection,selection,selectionArgs,sortOrder)} cocok dengan sebuah pernyataan SELECT di SQL:
Argumen query() | Kata kunci/parameter SELECT | Catatan |
---|---|---|
Uri |
FROM table_name |
Uri memetakan ke tabel dalam penyedia yang bernama table_name. |
projection |
col,col,col,... |
projection adalah satu larik kolom yang harus disertakan untuk tiap baris
yang diambil.
|
selection |
WHERE col = value |
selection menetapkan kriteria untuk memilih baris. |
selectionArgs |
(Tidak ada padanan persis. Argumen pemilihan mengganti ? placeholder dalam
klausa pemilihan.)
|
|
sortOrder |
ORDER BY col,col,... |
sortOrder menetapkan urutan munculnya baris dalam
{@link android.database.Cursor} yang dihasilkan.
|
URI konten adalah URI yang mengidentifikasi data dalam penyedia. URI Konten menyertakan nama simbolis seluruh penyedia (otoritasnya) dan sebuah nama yang menunjuk ke tabel (path). Bila Anda memanggil metode klien untuk mengakses tabel dalam penyedia, URI konten untuk tabel itu adalah salah satu argumennya.
Dalam baris kode sebelumnya, konstanta {@link android.provider.UserDictionary.Words#CONTENT_URI} mengandung URI konten dari tabel "words" kamus pengguna. Objek {@link android.content.ContentResolver} akan mengurai otoritas URI, dan menggunakannya untuk "mengetahui" penyedia dengan membandingkan otoritas tersebut dengan sebuah tabel sistem berisi penyedia yang dikenal. {@link android.content.ContentResolver} kemudian bisa mengirim argumen query ke penyedia yang benar.
{@link android.content.ContentProvider} menggunakan bagian path dari URI konten untuk memilih tabel yang akan diakses. Penyedia biasanya memiliki path untuk tiap tabel yang dieksposnya.
Dalam baris kode sebelumnya, URI lengkap untuk tabel "words" adalah:
content://user_dictionary/words
dalam hal ini string user_dictionary
adalah otoritas penyedia, dan string
words
adalah path tabel. String
content://
(skema) selalu ada,
dan mengidentifikasinya sebagai URI konten.
Banyak penyedia yang memperbolehkan Anda mengakses satu baris dalam tabel dengan menambahkan sebuah ID nilai
ke akhir URI. Misalnya, untuk mengambil sebuah baris yang _ID
-nya adalah
4
dari kamus pengguna, Anda bisa menggunakan URI konten ini:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
Anda akan sering menggunakan nilai-nilai ID bila telah mengambil satu set baris kemudian ingin memperbarui atau menghapus salah satunya.
Catatan: Kelas-kelas {@link android.net.Uri} dan {@link android.net.Uri.Builder} berisi metode praktis untuk membangun objek dari string URI yang tersusun dengan baik. {@link android.content.ContentUris} berisi metode praktis untuk menambahkan nilai ID ke URI. Cuplikan kode sebelumnya menggunakan {@link android.content.ContentUris#withAppendedId withAppendedId()} untuk menambahkan id ke URI konten User Dictionary.
Bagian ini menerangkan cara mengambil data dari penyedia, dengan menggunakan Penyedia Kamus Pengguna sebagai contoh.
Demi kejelasan, cuplikan kode di bagian ini memanggil {@link android.content.ContentResolver#query ContentResolver.query()} pada "UI thread"". Akan tetapi, dalam kode sesungguhnya, Anda harus melakukan query secara asinkron pada sebuah thread terpisah. Satu cara melakukannya adalah menggunakan kelas {@link android.content.CursorLoader}, yang dijelaskan lebih detail dalam panduan Loader. Juga, baris-baris kode tersebut hanyalah cuplikan; tidak menunjukkan sebuah aplikasi lengkap.
Untuk mengambil data dari penyedia, ikutilah langkah-langkah dasar ini:
Untuk mengambil data dari penyedia, aplikasi Anda memerlukan "izin akses baca" untuk
penyedia itu. Anda tidak bisa meminta izin ini saat runtime; sebagai gantinya, Anda harus menetapkan bahwa
Anda memerlukan izin ini dalam manifes, dengan menggunakan elemen
<uses-permission>
dan nama persis izin yang didefinisikan oleh
penyedia itu. Bila menetapkan elemen ini dalam manifes, Anda secara efektif "meminta"
izin ini untuk aplikasi Anda. Bila pengguna menginstal aplikasi Anda, mereka secara implisit akan memberikan
permintaan ini.
Untuk menemukan nama persis dari izin akses baca untuk penyedia yang sedang Anda gunakan, serta nama-nama izin akses lain yang digunakan oleh penyedia, lihatlah dalam dokumentasi penyedia.
Peran izin dalam yang mengakses penyedia dijelaskan lebih detail di bagian Izin Penyedia Konten.
Penyedia Kamus Pengguna mendefinisikan izin
android.permission.READ_USER_DICTIONARY
dalam file manifesnya, sehingga
aplikasi yang ingin membaca dari penyedia itu harus meminta izin ini.
Langkah berikutnya dalam mengambil data penyedia adalah membuat query. Cuplikan kode pertama ini mendefinisikan beberapa variabel untuk mengakses Penyedia Kamus Pengguna:
// A "projection" defines the columns that will be 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 mSelectionClause = null; // Initializes an array to contain selection arguments String[] mSelectionArgs = {""};
Cuplikan berikutnya menampilkan cara menggunakan {@link android.content.ContentResolver#query ContentResolver.query()}, dengan menggunakan Penyedia Kamus Pengguna sebagai contoh. Query klien penyedia serupa dengan query SQL, dan berisi satu set kolom yang akan dihasilkan, satu set kriteria pemilihan, dan urutan sortir.
Set kolom yang harus dikembalikan query disebut dengan proyeksi
(variabel mProjection
).
Ekspresi yang menetapkan baris yang harus diambil dipecah menjadi klausa pemilihan dan
argumen pemilihan. Klausa pemilihan adalah kombinasi ekspresi logis dan boolean,
nama kolom, dan nilai (variabel mSelectionClause
). Jika Anda menetapkan
parameter ?
yang bisa diganti, sebagai ganti nilai, metode query akan mengambil nilai
dari larik argumen pemilihan (variabel mSelectionArgs
).
Dalam cuplikan berikutnya, jika pengguna tidak memasukkan sebuah kata, klausa pemilihan akan diatur ke
null
, dan query menghasilkan semua kata dalam penyedia. Jika pengguna memasukkan
sebuah kata, klausa pemilihan akan diatur ke UserDictionary.Words.WORD + " = ?"
dan
elemen pertama larik argumen pemilihan diatur ke kata yang dimasukkan pengguna.
/* * This defines a one-element String array to contain the selection argument. */ String[] mSelectionArgs = {""}; // Gets a word from the UI mSearchString = mSearchWord.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(mSearchString)) { // Setting the selection clause to null will return all words mSelectionClause = null; mSelectionArgs[0] = ""; } else { // Constructs a selection clause that matches the word that the user entered. mSelectionClause = UserDictionary.Words.WORD + " = ?"; // Moves the user's input string to the selection arguments. mSelectionArgs[0] = mSearchString; } // 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 mProjection, // The columns to return for each row mSelectionClause // Either null, or the word the user entered mSelectionArgs, // Either empty, or the string the user entered mSortOrder); // 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 may want to * 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 was unsuccessful. This isn't necessarily * an error. You may 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 }
Query ini analog dengan pernyataan SQL:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
Dalam pernyataan SQL ini, nama kolom yang sesungguhnya digunakan sebagai ganti konstanta kelas kontrak.
Jika data dikelola oleh penyedia konten berada dalam database SQL, memasukkan data tak dipercaya eksternal ke dalam pernyataan SQL mentah bisa menyebabkan injeksi SQL.
Perhatikan klausa pemilihan ini:
// Constructs a selection clause by concatenating the user's input to the column name String mSelectionClause = "var = " + mUserInput;
Jika melakukannya, Anda akan membuat pengguna menyambungkan SQL merusak ke pernyataan SQL Anda.
Misalnya, pengguna bisa memasukkan "nothing; DROP TABLE *;" untuk mUserInput
, yang
akan menghasilkan klausa pemilihan var = nothing; DROP TABLE *;
. Karena
klausa pemilihan diperlakukan sebagai pernyataan SQL, hal ini bisa menyebabkan penyedia itu menghapus semua
tabel dalam database SQLite yang mendasarinya (kecuali penyedia disiapkan untuk menangkap upaya
injeksi SQL).
Untuk menghindari masalah ini, gunakan klausa pemilihan yang menggunakan ?
sebagai
parameter yang bisa diganti dan larik argumen pemilihan yang terpisah. Bila Anda melakukannya, input pengguna
akan dibatasi secara langsung pada query agar tidak ditafsirkan sebagai bagian dari pernyataan SQL.
Karena tidak diperlakukan sebagai SQL, input pengguna tidak bisa menyuntikkan SQL merusak. Sebagai ganti menggunakan
penyambungan untuk menyertakan input pengguna, gunakan klausa pemilihan ini:
// Constructs a selection clause with a replaceable parameter String mSelectionClause = "var = ?";
Buat larik argumen pemilihan seperti ini:
// Defines an array to contain the selection arguments String[] selectionArgs = {""};
Masukkan nilai dalam larik argumen pemilihan seperti ini:
// Sets the selection argument to the user's input selectionArgs[0] = mUserInput;
Sebuah klausa pemilihan yang menggunakan ?
sebagai parameter yang bisa diganti dan sebuah larik
argumen pemilihan adalah cara yang lebih disukai untuk menyebutkan pemilihan, sekalipun penyedia tidak
dibuat berdasarkan database SQL.
Metode klien {@link android.content.ContentResolver#query ContentResolver.query()} selalu menghasilkan {@link android.database.Cursor} berisi kolom-kolom yang ditetapkan oleh proyeksi query untuk baris yang cocok dengan kriteria pemilihan query. Objek {@link android.database.Cursor} menyediakan akses baca acak ke baris dan kolom yang dimuatnya. Dengan metode {@link android.database.Cursor}, Anda bisa mengulang baris-baris dalam hasil, menentukan tipe data tiap kolom, mengambil data dari kolom, dan memeriksa properti lain dari hasil. Beberapa implementasi {@link android.database.Cursor} akan memperbarui objek secara otomatis bila data penyedia berubah, atau memicu metode dalam objek pengamat bila {@link android.database.Cursor} berubah, atau keduanya.
Catatan: Penyedia bisa membatasi akses ke kolom berdasarkan sifat objek yang membuat query. Misalnya, Penyedia Kontak membatasi akses untuk beberapa kolom pada adaptor sinkronisasi, sehingga tidak akan mengembalikannya ke aktivitas atau layanan.
Jika tidak ada baris yang cocok dengan kriteria pemilihan, penyedia akan mengembalikan objek {@link android.database.Cursor} dengan {@link android.database.Cursor#getCount Cursor.getCount()} adalah 0 (kursor kosong).
Jika terjadi kesalahan internal, hasil query akan bergantung pada penyedia tertentu. Penyedia bisa
memilih untuk menghasilkan null
, atau melontarkan {@link java.lang.Exception}.
Karena {@link android.database.Cursor} adalah "daftar" baris, cara yang cocok untuk menampilkan konten {@link android.database.Cursor} adalah mengaitkannya dengan {@link android.widget.ListView} melalui {@link android.widget.SimpleCursorAdapter}.
Cuplikan berikut melanjutkan kode dari cuplikan sebelumnya. Cuplikan ini membuat objek {@link android.widget.SimpleCursorAdapter} berisi {@link android.database.Cursor} yang diambil oleh query, dan mengatur objek ini menjadi adaptor bagi {@link android.widget.ListView}:
// Defines a list of columns to retrieve from the Cursor and load into an output row String[] mWordListColumns = { 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 will receive the Cursor columns for each row int[] mWordListItems = { R.id.dictWord, R.id.locale}; // Creates a new SimpleCursorAdapter mCursorAdapter = 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 mWordListColumns, // A string array of column names in the cursor mWordListItems, // An integer array of view IDs in the row layout 0); // Flags (usually none are needed) // Sets the adapter for the ListView mWordList.setAdapter(mCursorAdapter);
Catatan: Untuk mendukung {@link android.widget.ListView} dengan
{@link android.database.Cursor}, kursor harus berisi kolom bernama _ID
.
Karena itu, query yang ditampilkan sebelumnya mengambil kolom _ID
untuk
tabel "words", walaupun {@link android.widget.ListView} tidak menampilkannya.
Pembatasan ini juga menjelaskan mengapa sebagian besar penyedia memiliki kolom _ID
untuk masing-masing
tabelnya.
Daripada sekadar menampilkan hasil query, Anda bisa menggunakannya untuk tugas-tugas lain. Misalnya, Anda bisa mengambil ejaan dari kamus pengguna kemudian mencarinya dalam penyedia lain. Caranya, ulangi baris-baris dalam {@link android.database.Cursor}:
// 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 may 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 will 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. }
Implementasi {@link android.database.Cursor} berisi beberapa metode "get" untuk mengambil berbagai tipe data dari objek. Misalnya, cuplikan sebelumnya menggunakan {@link android.database.Cursor#getString getString()}. Implementasi juga memiliki metode {@link android.database.Cursor#getType getType()} yang menghasilkan nilai yang menunjukkan tipe data kolom.
Aplikasi penyedia bisa menetapkan izin yang harus dimiliki aplikasi lain untuk mengakses data penyedia. Izin ini akan memastikan bahwa pengguna mengetahui data yang coba diakses oleh aplikasi. Berdasarkan ketentuan penyedia, aplikasi lain meminta izin yang diperlukannya untuk mengakses penyedia. Pengguna akhir akan melihat izin yang diminta saat menginstal aplikasi.
Jika aplikasi penyedia tidak menetapkan izin apa pun, maka aplikasi lain tidak memiliki akses ke data penyedia. Akan tetapi, komponen-komponen dalam aplikasi penyedia selalu memiliki akses penuh untuk baca dan tulis, izin apa pun yang ditetapkan.
Seperti disebutkan sebelumnya, Penyedia Kamus Pengguna mensyaratkan izin
android.permission.READ_USER_DICTIONARY
untuk mengambil data darinya.
Penyedia memiliki izin android.permission.WRITE_USER_DICTIONARY
yang terpisah untuk menyisipkan, memperbarui, atau menghapus data.
Untuk mendapatkan izin yang diperlukan untuk mengakses penyedia, aplikasi memintanya dengan elemen
<uses-permission>
dalam file manifesnya. Bila Android Package Manager memasang aplikasi, pengguna
harus menyetujui semua izin yang diminta aplikasi. Jika pengguna menyetujui semuanya,
Package Manager akan melanjutkan instalasi; jika pengguna tidak menyetujui, Package Manager
akan membatalkan instalasi.
Elemen
<uses-permission>
berikut meminta akses baca ke Penyedia Kamus Pengguna:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Dampak izin pada akses penyedia dijelaskan secara lebih detail dalam panduan Keamanan dan Izin.
Lewat cara yang sama dengan cara mengambil data dari penyedia, Anda juga menggunakan interaksi antara klien penyedia dan {@link android.content.ContentProvider} penyedia untuk memodifikasi data. Anda memanggil metode {@link android.content.ContentResolver} dengan argumen yang diteruskan ke metode {@link android.content.ContentProvider} yang sesuai. Penyedia dan klien penyedia menangani secara otomatis keamanan dan komunikasi antar-proses.
Untuk menyisipkan data ke penyedia, Anda memanggil metode {@link android.content.ContentResolver#insert ContentResolver.insert()}. Metode ini menyisipkan sebuah baris baru ke penyedia itu dan menghasilkan URI konten untuk baris itu. Cuplikan ini menampilkan cara menyisipkan sebuah kata baru ke Penyedia Kamus Pengguna:
// Defines a new Uri object that receives the result of the insertion Uri mNewUri; ... // Defines an object to contain the new values to insert ContentValues mNewValues = new ContentValues(); /* * Sets the values of each column and inserts the word. The arguments to the "put" * method are "column name" and "value" */ mNewValues.put(UserDictionary.Words.APP_ID, "example.user"); mNewValues.put(UserDictionary.Words.LOCALE, "en_US"); mNewValues.put(UserDictionary.Words.WORD, "insert"); mNewValues.put(UserDictionary.Words.FREQUENCY, "100"); mNewUri = getContentResolver().insert( UserDictionary.Word.CONTENT_URI, // the user dictionary content URI mNewValues // the values to insert );
Data untuk baris baru masuk ke dalam satu objek {@link android.content.ContentValues}, yang
serupa bentuknya dengan kursor satu-baris. Kolom dalam objek ini tidak perlu memiliki
tipe data yang sama, dan jika Anda tidak ingin menetapkan nilai sama sekali, Anda bisa mengatur kolom
ke null
dengan menggunakan {@link android.content.ContentValues#putNull ContentValues.putNull()}.
Cuplikan ini tidak menambahkan kolom _ID
, karena kolom ini dipelihara
secara otomatis. Penyedia menetapkan sebuah nilai unik _ID
ke setiap baris yang
ditambahkan. Penyedia biasanya menggunakan nilai ini sebagai kunci utama tabel.
URI konten yang dihasilkan dalam newUri
akan mengidentifikasi baris yang baru ditambahkan, dengan
format berikut:
content://user_dictionary/words/<id_value>
<id_value>
adalah konten _ID
untuk baris baru.
Kebanyakan penyedia bisa mendeteksi bentuk URI konten ini secara otomatis kemudian melakukan
operasi yang diminta pada baris tersebut.
Untuk mendapatkan nilai _ID
dari {@link android.net.Uri} yang dihasilkan, panggil
{@link android.content.ContentUris#parseId ContentUris.parseId()}.
Untuk memperbarui sebuah baris, gunakan objek {@link android.content.ContentValues} dengan
nilai-nilai yang diperbarui, persis seperti yang Anda lakukan pada penyisipan, dan kriteria pemilihan persis seperti yang Anda lakukan pada query.
Metode klien yang Anda gunakan adalah
{@link android.content.ContentResolver#update ContentResolver.update()}. Anda hanya perlu menambahkan
nilai-nilai ke objek {@link android.content.ContentValues} untuk kolom yang sedang Anda perbarui. Jika Anda
ingin membersihkan konten kolom, aturlah nilai ke null
.
Cuplikan berikut mengubah semua baris yang kolom lokalnya memiliki bahasa "en" ke
lokal null
. Nilai hasil adalah jumlah baris yang diperbarui:
// Defines an object to contain the updated values ContentValues mUpdateValues = new ContentValues(); // Defines selection criteria for the rows you want to update String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?"; String[] mSelectionArgs = {"en_%"}; // Defines a variable to contain the number of updated rows int mRowsUpdated = 0; ... /* * Sets the updated value and updates the selected words. */ mUpdateValues.putNull(UserDictionary.Words.LOCALE); mRowsUpdated = getContentResolver().update( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI mUpdateValues // the columns to update mSelectionClause // the column to select on mSelectionArgs // the value to compare to );
Anda juga harus membersihkan input pengguna bila memanggil {@link android.content.ContentResolver#update ContentResolver.update()}. Untuk mengetahui selengkapnya tentang hal ini, bacalah bagian Melindungi dari input merusak.
Menghapus baris serupa dengan mengambil baris data: Anda menetapkan kriteria pemilihan untuk baris yang ingin Anda hapus dan metode klien akan menghasilkan jumlah baris yang dihapus. Cuplikan berikut menghapus baris yang appid-nya sama dengan "user". Metode menghasilkan jumlah baris yang dihapus.
// Defines selection criteria for the rows you want to delete String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; String[] mSelectionArgs = {"user"}; // Defines a variable to contain the number of rows deleted int mRowsDeleted = 0; ... // Deletes the words that match the selection criteria mRowsDeleted = getContentResolver().delete( UserDictionary.Words.CONTENT_URI, // the user dictionary content URI mSelectionClause // the column to select on mSelectionArgs // the value to compare to );
Anda juga harus membersihkan input pengguna bila memanggil {@link android.content.ContentResolver#delete ContentResolver.delete()}. Untuk mengetahui selengkapnya tentang hal ini, bacalah bagian Melindungi dari input merusak.
Penyedia konten bisa menawarkan berbagai tipe data. Penyedia Kamus Pengguna hanya menawarkan teks, namun penyedia juga bisa menawarkan format berikut:
Tipe data lain yang sering digunakan penyedia adalah Binary Large OBject (BLOB) yang diimplementasikan sebagai larik byte 64 KB. Anda bisa melihat tipe data yang tersedia dengan memperhatikan metode "get" kelas {@link android.database.Cursor}.
Tipe data tiap kolom dalam penyedia biasanya tercantum dalam dokumentasinya. Tipe data untuk Penyedia Kamus Pengguna tercantum dalam dokumentasi acuan untuk kelas kontraknya {@link android.provider.UserDictionary.Words} (kelas kontrak dijelaskan di bagian Kelas-kelas Kontrak). Anda juga bisa menentukan tipe data dengan memanggil {@link android.database.Cursor#getType Cursor.getType()}.
Penyedia juga memelihara informasi tipe data MIME untuk tiap URI konten yang didefinisikannya. Anda bisa menggunakan informasi tipe MIME untuk mengetahui apakah aplikasi Anda bisa menangani data yang disediakan penyedia, atau memilih tipe penanganan berdasarkan tipe MIME. Anda biasanya memerlukan tipe MIME saat menggunakan penyedia yang berisi struktur atau file data yang kompleks. Misalnya, tabel {@link android.provider.ContactsContract.Data} dalam Penyedia Kontak menggunakan tipe MIME untuk memberi label tipe data kontak yang disimpan di tiap baris. Untuk mendapatkan tipe MIME yang sesuai dengan URI konten, panggil {@link android.content.ContentResolver#getType ContentResolver.getType()}.
Bagian Acuan Tipe MIME menerangkan sintaks tipe MIME baik yang standar maupun custom.
Tiga bentuk alternatif akses penyedia adalah penting dalam pengembangan aplikasi:
Akses batch dan modifikasi melalui intent dijelaskan dalam bagian-bagian berikut.
Akses batch ke penyedia berguna untuk menyisipkan baris dalam jumlah besar, atau menyisipkan baris ke dalam beberapa tabel dalam panggilan metode yang sama, atau biasanya melakukan satu set operasi lintas batas proses sebagai transaksi (operasi atomik).
Untuk mengakses penyedia dalam "mode batch", buat satu larik objek {@link android.content.ContentProviderOperation}, kemudian kirim larik itu ke penyedia konten dengan {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. Anda meneruskan otoritas penyedia konten ke metode ini, daripada URI konten tertentu. Ini memungkinkan tiap objek {@link android.content.ContentProviderOperation} dalam larik untuk bekerja terhadap tabel yang berbeda. Panggilan ke {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} menghasilkan satu larik hasil.
Keterangan kelas kontrak {@link android.provider.ContactsContract.RawContacts}
menyertakan cuplikan kode yang memperagakan penyisipan batch. Contoh aplikasi
Contacts Manager
berisi contoh akses batch dalam file sumber ContactAdder.java
-nya
.
Jika aplikasi Anda memang memiliki izin akses, Anda masih mungkin perlu menggunakan intent untuk menampilkan data dalam aplikasi lain. Misalnya, aplikasi Kalender menerima intent {@link android.content.Intent#ACTION_VIEW}, yang menampilkan tanggal atau kejadian tertentu. Hal ini memungkinkan Anda menampilkan informasi kalender tanpa harus membuat UI sendiri. Untuk mengetahui selengkapnya tentang fitur ini, lihat panduan Penyedia Kalender.
Aplikasi yang Anda kirimi intent tidak harus aplikasi yang terkait dengan penyedia. Misalnya, Anda bisa mengambil satu kontak dari Penyedia Kontak, kemudian mengirim intent {@link android.content.Intent#ACTION_VIEW} berisi URI konten untuk gambar kontak itu ke penampil gambar.
Intent bisa menyediakan akses tidak langsung ke penyedia konten. Anda memperbolehkan pengguna mengakses data dalam penyedia sekalipun aplikasi Anda tidak memiliki izin akses, baik dengan mendapatkan intent yang dihasilkan aplikasi yang memiliki izin, atau dengan mengaktifkan aplikasi yang memiliki izin dan membiarkan pengguna melakukan pekerjaan di dalamnya.
Anda bisa mengakses data dalam penyedia konten, sekalipun tidak memiliki izin akses yang sesuai, dengan mengirimkan intent ke aplikasi yang memang memiliki izin dan menerima hasil berupa intent berisi izin "URI". Inilah izin untuk URI konten tertentu yang berlaku hingga aktivitas yang menerima izin selesai. Aplikasi yang memiliki izin tetap akan memberikan izin sementara dengan mengatur flag dalam intent yang dihasilkan:
Catatan: Flag ini tidak memberikan akses baca atau tulis umum ke penyedia yang otoritasnya dimuat dalam URI konten. Aksesnya hanya untuk URI itu sendiri.
Penyedia mendefinisikan izin URI untuk URI konten dalam manifesnya, dengan menggunakan atribut
android:grantUriPermission
dari elemen
<provider>
, serta elemen anak
<grant-uri-permission>
dari elemen
<provider>
.
Mekanisme izin URI dijelaskan secara lebih detail dalam panduan
Keamanan dan Izin,
di bagian "Izin URI".
Misalnya, Anda bisa mengambil data untuk satu kontak di Penyedia Kontak, sekalipun tidak memiliki izin {@link android.Manifest.permission#READ_CONTACTS}. Anda mungkin ingin melakukan ini dalam aplikasi yang mengirim kartu ucapan elektronik ke seorang kenalan pada hari ulang tahunnya. Sebagai ganti meminta {@link android.Manifest.permission#READ_CONTACTS}, yang memberi Anda akses ke semua kontak pengguna dan semua informasinya, Anda lebih baik membiarkan pengguna mengontrol kontak-kontak yang akan digunakan oleh aplikasi Anda. Caranya, gunakan proses berikut:
Satu cara mudah agar pengguna bisa memodifikasi data yang izin aksesnya tidak Anda miliki adalah mengaktifkan aplikasi yang memiliki izin dan membiarkan pengguna melakukan pekerjaannya di sana.
Misalnya, aplikasi Kalender menerima intent {@link android.content.Intent#ACTION_INSERT}, yang memungkinkan Anda mengaktifkan UI penyisipan aplikasi itu. Anda bisa meneruskan data "extras" dalam intent ini, yang digunakan aplikasi untuk mengisi dahulu UI-nya. Karena kejadian berulang memiliki sintaks yang rumit, cara yang lebih disukai untuk menyisipkan kejadian ke dalam Penyedia Kalender adalah mengaktifkan aplikasi Kalender dengan {@link android.content.Intent#ACTION_INSERT}, kemudian membiarkan pengguna menyisipkan kejadian di sana.
Kelas kontrak mendefinisikan konstanta yang membantu aplikasi menggunakan URI konten, nama kolom, tindakan intent, dan fitur lain pada penyedia konten. Kelas kontrak tidak disertakan secara otomatis bersama penyedia; pengembang penyedia harus mendefinisikannya kemudian membuatnya tersedia bagi pengembang lain. Banyak penyedia yang disertakan pada platform Android memiliki kelas kontrak yang sesuai dalam {@link android.provider} paketnya.
Misalnya, Penyedia Kamus Pengguna memiliki kelas kontrak {@link android.provider.UserDictionary} yang berisi URI konten dan konstanta nama kolom. URI konten untuk tabel "words" didefinisikan dalam konstanta {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}. Kelas {@link android.provider.UserDictionary.Words} juga berisi konstanta nama kolom, yang digunakan dalam cuplikan contoh pada panduan ini. Misalnya, sebuah proyeksi query bisa didefinisikan sebagai:
String[] mProjection = { UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.LOCALE };
Kelas kontrak lain adalah {@link android.provider.ContactsContract} untuk Penyedia Kontak. Dokumentasi acuan untuk kelas ini menyertakan contoh cuplikan kode. Salah satu subkelasnya, {@link android.provider.ContactsContract.Intents.Insert}, adalah kelas kontrak yang berisi konstanta untuk intent dan data intent.
Penyedia konten bisa menghasilkan tipe media MIME standar, atau string tipe MIME custom, atau keduanya.
Tipe MIME memiliki format
type/subtype
Misalnya, tipe MIME text/html
yang dikenal luas memiliki tipe text
dan
subtipe html
. Jika penyedia menghasilkan tipe ini untuk sebuah URI, artinya
query dengan URI itu akan menghasilkan teks berisi tag HTML.
String tipe MIME custom, yang juga disebut dengan tipe MIME "khusus vendor", memiliki nilai-nilai tipe dan subtipe yang lebih kompleks. Nilai tipe selalu
vnd.android.cursor.dir
untuk beberapa baris, atau
vnd.android.cursor.item
untuk satu baris.
Subtipe adalah khusus penyedia. Penyedia bawaan Android biasanya memiliki subtipe sederhana. Misalnya, bila aplikasi Contacts membuat satu baris untuk nomor telepon, aplikasi akan mengatur tipe MIME berikut di baris itu:
vnd.android.cursor.item/phone_v2
Perhatikan bahwa nilai subtipe adalah sekadar phone_v2
.
Pengembang penyedia lain bisa membuat pola subtipe sendiri berdasarkan
otoritas dan nama-nama tabel penyedia. Misalnya, perhatikan penyedia yang berisi jadwal kereta api.
Otoritas penyedia adalah com.example.trains
, dan berisi tabel-tabel
Line1, Line2, dan Line3. Untuk merespons URI konten
content://com.example.trains/Line1
untuk tabel Line1, penyedia menghasilkan tipe MIME
vnd.android.cursor.dir/vnd.example.line1
Untuk merespons URI konten
content://com.example.trains/Line2/5
untuk baris 5 di tabel Line2, penyedia menghasilkan tipe MIME
vnd.android.cursor.item/vnd.example.line2
Kebanyakan penyedia konten mendefinisikan konstanta kelas kontrak untuk tipe MIME yang digunakannya. Kelas kontrak {@link android.provider.ContactsContract.RawContacts} pada Penyedia Kontak misalnya, mendefinisikan konstanta {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} untuk tipe MIME baris kontak mentah tunggal.
URI konten untuk baris-baris tunggal dijelaskan di bagian URI Konten.