lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Dasar-Dasar Penyedia Materi

Penyedia materi mengelola akses ke repositori data pusat. Penyedia adalah bagian dari aplikasi Android, yang sering menyediakan UI-nya sendiri untuk menggunakan data. Akan tetapi, penyedia materi 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:

  • Cara kerja penyedia materi.
  • API yang Anda gunakan untuk mengambil data dari penyedia materi.
  • API yang Anda gunakan untuk menyisipkan, memperbarui, atau menghapus data dalam penyedia materi.
  • Fitur API lainnya yang memudahkan kita menggunakan penyedia.

Ringkasan

Penyedia materi 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:

Tabel 1: Contoh tabel kamus pengguna.

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 ListView, salah satu nama kolom harus _ID. Ketentuan ini dijelaskan secara lebih detail di bagian Menampilkan hasil kueri.

Mengakses penyedia

Aplikasi mengakses data dari penyedia materi dengan sebuah objek klien ContentResolver. Objek ini memiliki metode yang memanggil metode dengan nama identik dalam objek penyedia, instance salah satu subkelas konkret dari ContentProvider. Metode-metode ContentResolver menyediakan fungsi-fungsi dasar "CRUD" (create, retrieve, update, dan delete - buat, ambil, perbarui, dan hapus)) pada storage yang persisten.

Objek ContentResolver dalam proses aplikasi klien dan objek ContentProvider dalam aplikasi yang memiliki penyedia itu secara otomatis akan menangani komunikasi antar-proses. ContentProvider juga berfungsi sebagai layer abstraksi antara repositori 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 Materi

Misalnya, untuk mendapatkan daftar kata dan lokalnya dari Penyedia Kamus Pengguna, Anda memanggil ContentResolver.query(). Metode query() memanggil metode ContentProvider.query() yang didefinisikan oleh Penyedia Kamus Pengguna. Baris-baris kode berikut menunjukkan sebuah panggilan 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 bagaimana argumen ke query(Uri,projection,selection,selectionArgs,sortOrder) cocok dengan sebuah pernyataan SELECT di SQL:

Tabel 2: Query() dibandingkan dengan kueri 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 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 Cursor yang dikembalikan.

URI Materi

URI materi adalah URI yang mengidentifikasi data dalam penyedia. URI Materi menyertakan nama simbolis seluruh penyedia (otoritasnya) dan sebuah nama yang menunjuk ke tabel (jalur). Bila Anda memanggil metode klien untuk mengakses tabel dalam penyedia, URI materi untuk tabel itu adalah salah satu argumennya.

Dalam baris kode sebelumnya, konstanta CONTENT_URI mengandung URI materi dari tabel "words" kamus pengguna. Objek ContentResolver akan mem-parse otoritas URI, dan menggunakannya untuk "mengetahui" penyedia dengan membandingkan otoritas tersebut dengan sebuah tabel sistem berisi penyedia yang dikenal. ContentResolver kemudian bisa mengirim argumen kueri ke penyedia yang benar.

ContentProvider menggunakan bagian jalur dari URI materi untuk memilih tabel yang akan diakses. Penyedia biasanya memiliki jalur untuk tiap tabel yang dieksposnya.

Dalam baris kode sebelumnya, URI lengkap untuk tabel "words" adalah:

content://user_dictionary/words

ketika string user_dictionary adalah otoritas penyedia, dan string words adalah jalur tabel. String content:// (skema) selalu ada, dan mengidentifikasinya sebagai URI materi.

Banyak penyedia yang memungkinkan Anda mengakses satu baris dalam tabel dengan menambahkan sebuah nilai ID ke akhir URI. Misalnya, untuk mengambil sebuah baris yang _ID-nya adalah 4 dari kamus pengguna, Anda bisa menggunakan URI materi 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 Uri dan Uri.Builder berisi metode praktis untuk membangun objek URI yang tersusun dengan baik dari string. Kelas ContentUris berisi metode praktis untuk menambahkan nilai id ke URI. Cuplikan kode sebelumnya menggunakan withAppendedId() untuk menambahkan ID ke URI materi UserDictionary.

Mengambil Data dari Penyedia

Bagian ini menerangkan cara mengambil data dari penyedia, dengan menggunakan Penyedia Kamus Pengguna sebagai contoh.

Demi kejelasan, cuplikan kode di bagian ini memanggil ContentResolver.query() pada "UI thread"". Akan tetapi, dalam kode sesungguhnya, Anda harus melakukan kueri secara asinkron pada sebuah thread terpisah. Satu cara melakukannya adalah menggunakan kelas 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:

  1. Minta izin akses baca untuk penyedia itu.
  2. Definisikan kode yang mengirim kueri ke penyedia.

Meminta izin akses baca

Untuk mengambil data dari penyedia, aplikasi Anda memerlukan "izin akses baca" untuk penyedia itu. Anda tidak bisa meminta izin ini saat waktu proses; 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 memasang 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 Materi.

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.

Membuat kueri

Langkah berikutnya dalam mengambil data dari penyedia adalah melakukan kueri. 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 ContentResolver.query(), dengan menggunakan Penyedia Kamus Pengguna sebagai contoh. Kueri klien penyedia serupa dengan kueri SQL, dan berisi satu set kolom yang akan dikembalikan, satu set kriteria pemilihan, dan urutan sortir.

Set kolom yang harus dikembalikan kueri 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, bukannya nilai, metode kueri 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 kueri mengembalikan 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

}

Kueri ini merupakan 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.

Melindungi dari masukan merusak

Jika data dikelola oleh penyedia materi 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, masukan pengguna akan dibatasi secara langsung pada kueri agar tidak ditafsirkan sebagai bagian dari pernyataan SQL. Karena tidak diperlakukan sebagai SQL, masukan pengguna tidak bisa menyuntikkan SQL merusak. Sebagai ganti menggunakan penyambungan untuk menyertakan masukan 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.

Menampilkan hasil kueri

Metode klien ContentResolver.query() selalu mengembalikan Cursor berisi kolom-kolom yang ditetapkan oleh proyeksi kueri untuk baris yang cocok dengan kriteria pemilihan kueri. Objek Cursor menyediakan akses baca acak ke baris dan kolom yang dimuatnya. Dengan metode 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 Cursor akan memperbarui objek secara otomatis bila data penyedia berubah, atau memicu metode dalam objek pengamat bila Cursor berubah, atau keduanya.

Catatan: Penyedia bisa membatasi akses ke kolom berdasarkan sifat objek yang melakukan kueri. 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 Cursor dengan Cursor.getCount() adalah 0 (kursor kosong).

Jika terjadi kesalahan internal, hasil kueri akan bergantung pada penyedia tertentu. Penyedia bisa memilih untuk mengembalikan null, atau melontarkan Exception.

Karena Cursor adalah "daftar" baris, cara yang cocok untuk menampilkan materi Cursor adalah menautkannya dengan ListView melalui SimpleCursorAdapter.

Cuplikan berikut melanjutkan kode dari cuplikan sebelumnya. Cuplikan ini membuat objek SimpleCursorAdapter berisi Cursor yang diambil oleh kueri, dan mengatur objek ini menjadi adaptor bagi 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 ListView dengan Cursor, kursor harus berisi kolom bernama _ID. Karena itu, kueri yang ditampilkan sebelumnya mengambil kolom _ID untuk tabel "words", walaupun ListView tidak menampilkannya. Pembatasan ini juga menjelaskan mengapa sebagian besar penyedia memiliki kolom _ID untuk masing-masing tabelnya.

Mendapatkan data dari hasil kueri

Daripada sekadar menampilkan hasil kueri, 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 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 Cursor berisi beberapa metode "get" untuk mengambil berbagai tipe data dari objek. Misalnya, cuplikan sebelumnya menggunakan getString(). Implementasi juga memiliki metode getType() yang mengembalikan nilai yang menunjukkan tipe data kolom.

Izin Penyedia Materi

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 memasang 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 pemasangan; jika pengguna tidak menyetujuinya, Package Manager membatalkan pemasangan.

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.

Menyisipkan, Memperbarui, dan Menghapus Data

Lewat cara yang sama dengan cara mengambil data dari penyedia, Anda juga menggunakan interaksi antara klien penyedia dan ContentProvider penyedia untuk memodifikasi data. Anda memanggil metode ContentResolver dengan argumen yang diteruskan ke metode ContentProvider yang sesuai. Penyedia dan klien penyedia menangani secara otomatis keamanan dan komunikasi antar-proses.

Menyisipkan data

Untuk menyisipkan data ke penyedia, Anda memanggil metode ContentResolver.insert(). Metode ini menyisipkan sebuah baris baru ke penyedia itu dan mengembalikan URI materi 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 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 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 materi yang dikembalikan dalam newUri akan mengidentifikasi baris yang baru ditambahkan, dengan format berikut:

content://user_dictionary/words/<id_value>

<id_value> adalah materi _ID untuk baris baru. Kebanyakan penyedia bisa mendeteksi bentuk URI materi ini secara otomatis kemudian melakukan operasi yang diminta pada baris tersebut.

Untuk mendapatkan nilai _ID dari Uri yang dikembalikan, panggil ContentUris.parseId().

Memperbarui data

Untuk memperbarui sebuah baris, gunakan objek ContentValues dengan nilai-nilai yang diperbarui, persis seperti yang Anda lakukan pada penyisipan, dan kriteria pemilihan persis seperti yang Anda lakukan pada kueri. Metode klien yang Anda gunakan adalah ContentResolver.update(). Anda hanya perlu menambahkan nilai-nilai ke objek ContentValues untuk kolom yang sedang Anda perbarui. Jika Anda ingin mengosongkan isi kolom, setel 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 masukan pengguna bila memanggil ContentResolver.update(). Untuk mengetahui selengkapnya tentang hal ini, bacalah bagian Melindungi dari masukan merusak.

Menghapus data

Menghapus baris serupa dengan mengambil baris data: Anda menetapkan kriteria pemilihan untuk baris yang ingin Anda hapus dan metode klien akan mengembalikan jumlah baris yang dihapus. Cuplikan berikut menghapus baris yang appid-nya sama dengan "user". Metode mengembalikan 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 masukan pengguna bila memanggil ContentResolver.delete(). Untuk mengetahui selengkapnya tentang hal ini, bacalah bagian Melindungi dari masukan merusak.

Tipe Data Penyedia

Penyedia materi bisa menawarkan berbagai tipe data. Penyedia Kamus Pengguna hanya menawarkan teks, namun penyedia juga bisa menawarkan format berikut:

  • integer
  • long integer (long)
  • floating point
  • long floating point (double)

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 Cursor.

Tipe data tiap kolom dalam penyedia biasanya tercantum dalam dokumentasinya. Tipe data untuk Penyedia Kamus Pengguna tercantum dalam dokumentasi acuan untuk kelas kontraknya UserDictionary.Words (kelas kontrak dijelaskan di bagian Kelas-kelas Kontrak). Anda juga bisa menentukan tipe data dengan memanggil Cursor.getType().

Penyedia juga memelihara informasi tipe data MIME untuk tiap URI materi 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 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 materi, panggil ContentResolver.getType().

Bagian Acuan Tipe MIME menerangkan sintaks tipe MIME baik yang standar maupun khusus.

Bentuk-Bentuk Alternatif Akses Penyedia

Tiga bentuk alternatif akses penyedia adalah penting dalam development aplikasi:

  • Akses batch: Anda bisa membuat sebuah batch panggilan akses dengan metode-metode dalam kelas ContentProviderOperation, kemudian menerapkannya dengan ContentResolver.applyBatch().
  • Kueri asinkron: Anda harus melakukan kueri dalam thread terpisah. Satu cara melakukannya adalah menggunakan objek CursorLoader. Contoh-contoh dalam panduan Loader memperagakan cara melakukannya.
  • Akses data melalui maksud: Walaupun tidak bisa mengirim maksud ke penyedia secara langsung, Anda bisa mengirim maksud ke aplikasi penyedia, yang biasanya paling lengkap dibekali untuk memodifikasi data penyedia.

Akses batch dan modifikasi melalui maksud dijelaskan dalam bagian-bagian berikut.

Akses batch

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 ContentProviderOperation, kemudian kirim larik itu ke penyedia materi dengan ContentResolver.applyBatch(). Anda meneruskan otoritas penyedia materi ke metode ini, daripada URI materi tertentu. Ini memungkinkan tiap objek ContentProviderOperation dalam larik untuk bekerja terhadap tabel yang berbeda. Panggilan ke ContentResolver.applyBatch() mengembalikan satu larik hasil.

Keterangan kelas kontrak ContactsContract.RawContacts menyertakan cuplikan kode yang memperagakan penyisipan batch. Contoh aplikasi Contacts Manager berisi contoh akses batch dalam file sumber ContactAdder.java-nya .

Akses data melalui maksud

Maksud bisa menyediakan akses tidak langsung ke penyedia materi. Anda memperbolehkan pengguna mengakses data dalam penyedia sekalipun aplikasi Anda tidak memiliki izin akses, baik dengan mendapatkan maksud yang dihasilkan aplikasi yang memiliki izin, atau dengan mengaktifkan aplikasi yang memiliki izin dan membiarkan pengguna melakukan pekerjaan di dalamnya.

Mendapatkan akses dengan izin sementara

Anda bisa mengakses data dalam penyedia materi, sekalipun tidak memiliki izin akses yang sesuai, dengan mengirimkan maksud ke aplikasi yang memang memiliki izin dan menerima hasil berupa maksud berisi izin "URI". Inilah izin untuk URI materi tertentu yang berlaku hingga aktivitas yang menerima izin selesai. Aplikasi yang memiliki izin tetap akan memberikan izin sementara dengan menyetel flag dalam maksud yang dihasilkan:

Catatan: Flag ini tidak memberikan akses baca atau tulis umum ke penyedia yang otoritasnya dimuat dalam URI materi. Aksesnya hanya untuk URI itu sendiri.

Penyedia mendefinisikan izin URI untuk URI materi 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 READ_CONTACTS. Anda mungkin ingin melakukan ini dalam aplikasi yang mengirim kartu ucapan elektronik ke seorang kenalan pada hari ulang tahunnya. Sebagai ganti meminta 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:

  1. Aplikasi Anda akan mengirim maksud berisi aksi ACTION_PICK dan tipe MIME "contacts" CONTENT_ITEM_TYPE, dengan menggunakan metode startActivityForResult().
  2. Karena maksud ini cocok dengan filter maksud untuk aktivitas "pemilihan" Aplikasi Orang, aktivitas akan muncul ke latar depan.
  3. Dalam aktivitas pemilihan, pengguna memilih sebuah kontak untuk diperbarui. Bila ini terjadi, aktivitas pemilihan akan memanggil setResult(resultcode, intent) untuk membuat maksud yang akan diberikan kembali ke aplikasi Anda. Maksud berisi URI materi kontak yang dipilih pengguna, dan flag "tambahan" FLAG_GRANT_READ_URI_PERMISSION. Semua flag ini memberikan izin URI ke aplikasi Anda untuk membaca data kontak yang ditunjuk oleh URI materi. Aktivitas pemilihan kemudian memanggil finish() untuk mengembalikan kontrol ke aplikasi Anda.
  4. Aktivitas Anda akan kembali ke latar depan, dan sistem akan memanggil metode onActivityResult() aktivitas Anda. Metode ini menerima maksud yang dihasilkan oleh aktivitas pemilihan dalam Aplikasi Orang.
  5. Dengan URI materi dari maksud yang dihasilkan, Anda bisa membaca data kontak dari Penyedia Kontak, sekalipun Anda tidak meminta izin akses baca tetap ke penyedia dalam manifes Anda. Anda kemudian bisa mendapatkan informasi hari ulang tahun si kontak atau alamat emailnya, kemudian mengirim kartu ucapan elektronik.

Menggunakan aplikasi lain

Satu cara mudah untuk memungkinkan pengguna 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 maksud ACTION_INSERT, yang memungkinkan Anda mengaktifkan UI penyisipan aplikasi itu. Anda bisa meneruskan data "tambahan" dalam maksud 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 ACTION_INSERT kemudian membiarkan pengguna menyisipkan kejadian di sana.

Kelas-kelas Kontrak

Kelas kontrak mendefinisikan konstanta yang membantu aplikasi menggunakan URI materi, nama kolom, tindakan maksud, dan fitur lain pada penyedia materi. Kelas kontrak tidak disertakan secara otomatis bersama penyedia; developer penyedia harus mendefinisikannya kemudian membuatnya tersedia bagi developer lain. Banyak penyedia yang disertakan pada platform Android memiliki kelas kontrak yang sesuai dalam android.provider paketnya.

Misalnya, Penyedia Kamus Pengguna memiliki kelas kontrak UserDictionary yang berisi URI materi dan konstanta nama kolom. URI materi untuk tabel "words" didefinisikan dalam konstanta UserDictionary.Words.CONTENT_URI. Kelas UserDictionary.Words juga berisi konstanta nama kolom, yang digunakan dalam cuplikan contoh pada panduan ini. Misalnya, sebuah proyeksi kueri bisa didefinisikan sebagai:

String[] mProjection =
{
    UserDictionary.Words._ID,
    UserDictionary.Words.WORD,
    UserDictionary.Words.LOCALE
};

Kelas kontrak lain adalah ContactsContract untuk Penyedia Kontak. Dokumentasi acuan untuk kelas ini menyertakan contoh cuplikan kode. Salah satu subkelasnya, ContactsContract.Intents.Insert, adalah kelas kontrak yang berisi konstanta untuk maksud dan data maksud.

Acuan Tipe MIME

Penyedia materi bisa mengembalikan tipe media MIME standar, atau string tipe MIME khusus, atau keduanya.

Tipe MIME memiliki format

type/subtype

Misalnya, tipe MIME text/html yang dikenal luas memiliki tipe text dan subtipe html. Jika penyedia mengembalikan tipe ini untuk sebuah URI, artinya kueri dengan URI itu akan mengembalikan teks berisi tag HTML.

String tipe MIME khusus, 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 Kontak membuat satu baris untuk nomor ponsel, aplikasi akan menyetel tipe MIME berikut di baris itu:

vnd.android.cursor.item/phone_v2

Perhatikan bahwa nilai subtipe adalah sekadar phone_v2.

Developer 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 materi

content://com.example.trains/Line1

untuk tabel Line1, penyedia mengembalikan tipe MIME

vnd.android.cursor.dir/vnd.example.line1

Untuk merespons URI materi

content://com.example.trains/Line2/5

untuk baris 5 di tabel Line2, penyedia mengembalikan tipe MIME

vnd.android.cursor.item/vnd.example.line2

Kebanyakan penyedia materi mendefinisikan konstanta kelas kontrak untuk tipe MIME yang digunakannya. Kelas kontrak ContactsContract.RawContacts pada Penyedia Kontak misalnya, mendefinisikan konstanta CONTENT_ITEM_TYPE untuk tipe MIME baris kontak mentah tunggal.

URI materi untuk baris-baris tunggal dijelaskan di bagian URI Materi.