Home | History | Annotate | Download | only in providers
      1 page.title=Dasar-Dasar Penyedia Konten
      2 @jd:body
      3 <div id="qv-wrapper">
      4 <div id="qv">
      5 <!-- In this document -->
      6 <h2>Dalam dokumen ini</h2>
      7 <ol>
      8     <li>
      9         <a href="#Basics">Ikhtisar</a>
     10         <ol>
     11             <li>
     12                 <a href="#ClientProvider">Mengakses penyedia</a>
     13             </li>
     14             <li>
     15                 <a href="#ContentURIs">URI Konten</a>
     16             </li>
     17         </ol>
     18     </li>
     19     <li>
     20         <a href="#SimpleQuery">Mengambil Data dari Penyedia</a>
     21         <ol>
     22             <li>
     23                 <a href="#RequestPermissions">Meminta izin akses baca</a>
     24             </li>
     25             <li>
     26                 <a href="#Query">Membuat query</a>
     27             </li>
     28             <li>
     29                 <a href="#DisplayResults">Menampilkan hasil query</a>
     30             </li>
     31             <li>
     32                 <a href="#GettingResults">Mendapatkan data dari hasil query</a>
     33             </li>
     34         </ol>
     35     </li>
     36     <li>
     37         <a href="#Permissions">Izin Penyedia Konten</a>
     38     </li>
     39     <li>
     40         <a href="#Modifications">Menyisipkan, Memperbarui, dan Menghapus Data</a>
     41         <ol>
     42             <li>
     43                 <a href="#Inserting">Menyisipkan data</a>
     44             </li>
     45             <li>
     46                 <a href="#Updating">Memperbarui data</a>
     47             </li>
     48             <li>
     49                 <a href="#Deleting">Menghapus data</a>
     50             </li>
     51         </ol>
     52     </li>
     53     <li>
     54         <a href="#DataTypes">Tipe Data Penyedia</a>
     55     </li>
     56     <li>
     57         <a href="#AltForms">Bentuk-Bentuk Alternatif Akses Penyedia</a>
     58         <ol>
     59             <li>
     60                 <a href="#Batch">Akses batch</a>
     61             </li>
     62             <li>
     63                 <a href="#Intents">Akses data melalui intent</a>
     64             </li>
     65         </ol>
     66     </li>
     67     <li>
     68         <a href="#ContractClasses">Kelas-kelas Kontrak</a>
     69     </li>
     70     <li>
     71         <a href="#MIMETypeReference">Acuan Tipe MIME</a>
     72     </li>
     73 </ol>
     74 
     75     <!-- Key Classes -->
     76 <h2>Kelas-kelas utama</h2>
     77     <ol>
     78         <li>
     79             {@link android.content.ContentProvider}
     80         </li>
     81         <li>
     82             {@link android.content.ContentResolver}
     83         </li>
     84         <li>
     85             {@link android.database.Cursor}
     86         </li>
     87         <li>
     88             {@link android.net.Uri}
     89         </li>
     90     </ol>
     91 
     92     <!-- Related Samples -->
     93 <h2>Contoh-Contoh Terkait</h2>
     94     <ol>
     95         <li>
     96         <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List2.html">
     97         Kursor (Orang)</a>
     98         </li>
     99         <li>
    100         <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List7.html">
    101         Kursor (Telepon)</a>
    102         </li>
    103     </ol>
    104 
    105     <!-- See also -->
    106 <h2>Lihat juga</h2>
    107     <ol>
    108         <li>
    109             <a href="{@docRoot}guide/topics/providers/content-provider-creating.html">
    110             Membuat Penyedia Konten</a>
    111         </li>
    112         <li>
    113             <a href="{@docRoot}guide/topics/providers/calendar-provider.html">
    114             Penyedia Kalender</a>
    115         </li>
    116     </ol>
    117 </div>
    118 </div>
    119 
    120     <!-- Intro paragraphs -->
    121 <p>
    122     Penyedia konten mengelola akses ke repository data pusat. Penyedia
    123     adalah bagian dari aplikasi Android, yang sering menyediakan UI-nya sendiri untuk menggunakan
    124     data. Akan tetapi, penyedia konten terutama dimaksudkan untuk digunakan oleh
    125     aplikasi lain, yang mengakses penyedia itu melalui objek klien penyedia. Bersama-sama, penyedia
    126     dan klien penyedia menawarkan antarmuka standar yang konsisten ke data yang juga menangani
    127     komunikasi antar-proses dan akses data aman.
    128 </p>
    129 <p>
    130     Topik ini menerangkan dasar-dasar dari hal-hal berikut:
    131 </p>
    132     <ul>
    133         <li>Cara kerja penyedia konten.</li>
    134         <li>API yang Anda gunakan untuk mengambil data dari penyedia konten.</li>
    135         <li>API yang Anda gunakan untuk menyisipkan, memperbarui, atau menghapus data dalam penyedia konten.</li>
    136         <li>Fitur API lainnya yang memudahkan kita menggunakan penyedia.</li>
    137     </ul>
    138 
    139     <!-- Basics -->
    140 <h2 id="Basics">Ikhtisar</h2>
    141 <p>
    142     Penyedia konten menyajikan data ke aplikasi eksternal sebagai satu atau beberapa tabel yang
    143     serupa dengan tabel-tabel yang ditemukan dalam database relasional. Sebuah baris mewakili instance beberapa tipe
    144     data yang dikumpulkan penyedia, dan tiap kolom dalam baris mewakili sepotong
    145     data yang dikumpulkan untuk sebuah instance.
    146 </p>
    147 <p>
    148     Misalnya, salah satu penyedia bawaan di platform Android adalah kamus pengguna, yang
    149     menyimpan ejaan kata-kata tidak-standar yang ingin disimpan pengguna. Tabel 1 mengilustrasikan
    150     wujud data yang mungkin ada dalam tabel penyedia ini:
    151 </p>
    152 <p class="table-caption">
    153     <strong>Tabel 1:</strong> Contoh tabel kamus pengguna.
    154 </p>
    155 <table id="table1" style="width: 50%;">
    156     <tr>
    157         <th style="width:20%" align="center" scope="col">word</th>
    158         <th style="width:20%" align="center" scope="col">app id</th>
    159         <th style="width:20%" align="center" scope="col">frequency</th>
    160         <th style="width:20%" align="center" scope="col">locale</th>
    161         <th style="width:20%" align="center" scope="col">_ID</th>
    162     </tr>
    163     <tr>
    164         <td align="center" scope="row">mapreduce</td>
    165         <td align="center">user1</td>
    166         <td align="center">100</td>
    167         <td align="center">en_US</td>
    168         <td align="center">1</td>
    169     </tr>
    170     <tr>
    171         <td align="center" scope="row">precompiler</td>
    172         <td align="center">user14</td>
    173         <td align="center">200</td>
    174         <td align="center">fr_FR</td>
    175         <td align="center">2</td>
    176     </tr>
    177     <tr>
    178         <td align="center" scope="row">applet</td>
    179         <td align="center">user2</td>
    180         <td align="center">225</td>
    181         <td align="center">fr_CA</td>
    182         <td align="center">3</td>
    183     </tr>
    184     <tr>
    185         <td align="center" scope="row">const</td>
    186         <td align="center">user1</td>
    187         <td align="center">255</td>
    188         <td align="center">pt_BR</td>
    189         <td align="center">4</td>
    190     </tr>
    191     <tr>
    192         <td align="center" scope="row">int</td>
    193         <td align="center">user5</td>
    194         <td align="center">100</td>
    195         <td align="center">en_UK</td>
    196         <td align="center">5</td>
    197     </tr>
    198 </table>
    199 <p>
    200     Dalam tabel 1, tiap baris mewakili instance sebuah kata yang mungkin tidak
    201     ditemukan dalam kamus standar. Tiap kolom mewakili beberapa data untuk kata itu, misalnya
    202     bahasa lokal tempat kata itu ditemukan kali pertama. Header kolom adalah nama kolom yang disimpan dalam
    203     penyedia. Untuk mengacu ke bahasa lokal suatu baris, Anda mengacu ke kolom <code>locale</code>-nya. Untuk
    204     penyedia ini, kolom <code>_ID</code> berfungsi sebagai "kunci utama" kolom yang
    205     dipelihara oleh penyedia secara otomatis.
    206 </p>
    207 <p class="note">
    208     <strong>Catatan:</strong> Penyedia tidak diharuskan memiliki kunci utama, dan tidak diharuskan
    209     menggunakan <code>_ID</code> sebagai nama kolom kunci utama jika kunci itu ada. Akan tetapi,
    210     jika Anda ingin mengikat data dari penyedia ke {@link android.widget.ListView}, salah satu
    211     nama kolom harus <code>_ID</code>. Ketentuan ini dijelaskan secara lebih detail di bagian
    212     <a href="#DisplayResults">Menampilkan hasil query</a>.
    213 </p>
    214 <h3 id="ClientProvider">Mengakses penyedia</h3>
    215 <p>
    216     Aplikasi mengakses data dari penyedia konten dengan
    217     sebuah objek klien {@link android.content.ContentResolver}. Objek ini memiliki metode yang memanggil
    218     metode dengan nama identik dalam objek penyedia, instance salah satu
    219     subkelas konkret dari {@link android.content.ContentProvider}. Metode-metode
    220     {@link android.content.ContentResolver} menyediakan fungsi-fungsi dasar
    221     "CRUD" (create, retrieve, update, dan delete) pada penyimpanan yang persisten.
    222 </p>
    223 <p>
    224     Objek {@link android.content.ContentResolver} dalam
    225     proses aplikasi klien dan objek {@link android.content.ContentProvider} dalam aplikasi yang memiliki
    226     penyedia itu secara otomatis akan menangani komunikasi antar-proses.
    227     {@link android.content.ContentProvider} juga berfungsi sebagai lapisan abstraksi antara
    228     repository datanya dan penampilan eksternal data sebagai tabel.
    229 </p>
    230 <p class="note">
    231     <strong>Catatan:</strong> Untuk mengakses penyedia, aplikasi Anda biasanya harus meminta
    232     izin tertentu dalam file manifesnya. Hal ini dijelaskan lebih detail di bagian
    233     <a href="#Permissions">Izin Penyedia Konten</a>
    234 </p>
    235 <p>
    236     Misalnya, untuk mendapatkan daftar kata dan lokalnya dari Penyedia Kamus Pengguna,
    237     Anda memanggil {@link android.content.ContentResolver#query ContentResolver.query()}.
    238     Metode {@link android.content.ContentResolver#query query()} memanggil
    239     metode {@link android.content.ContentProvider#query ContentProvider.query()} yang didefinisikan oleh
    240     Penyedia Kamus Pengguna. Baris-baris kode berikut menunjukkan sebuah
    241     panggilan {@link android.content.ContentResolver#query ContentResolver.query()}:
    242 <p>
    243 <pre>
    244 // Queries the user dictionary and returns results
    245 mCursor = getContentResolver().query(
    246     UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    247     mProjection,                        // The columns to return for each row
    248     mSelectionClause                    // Selection criteria
    249     mSelectionArgs,                     // Selection criteria
    250     mSortOrder);                        // The sort order for the returned rows
    251 </pre>
    252 <p>
    253     Tabel 2 menampilkan cara argumen untuk
    254     {@link android.content.ContentResolver#query
    255     query(Uri,projection,selection,selectionArgs,sortOrder)} cocok dengan sebuah pernyataan SELECT di SQL:
    256 </p>
    257 <p class="table-caption">
    258     <strong>Tabel 2:</strong> Query() dibandingkan dengan query SQL.
    259 </p>
    260 <table id="table2" style="width: 75%;">
    261     <tr>
    262         <th style="width:25%" align="center" scope="col">Argumen query()</th>
    263         <th style="width:25%" align="center" scope="col">Kata kunci/parameter SELECT</th>
    264         <th style="width:50%" align="center" scope="col">Catatan</th>
    265     </tr>
    266     <tr>
    267         <td align="center"><code>Uri</code></td>
    268         <td align="center"><code>FROM <em>table_name</em></code></td>
    269         <td><code>Uri</code> memetakan ke tabel dalam penyedia yang bernama <em>table_name</em>.</td>
    270     </tr>
    271     <tr>
    272         <td align="center"><code>projection</code></td>
    273         <td align="center"><code><em>col,col,col,...</em></code></td>
    274         <td>
    275             <code>projection</code> adalah satu larik kolom yang harus disertakan untuk tiap baris
    276             yang diambil.
    277         </td>
    278     </tr>
    279     <tr>
    280         <td align="center"><code>selection</code></td>
    281         <td align="center"><code>WHERE <em>col</em> = <em>value</em></code></td>
    282         <td><code>selection</code> menetapkan kriteria untuk memilih baris.</td>
    283     </tr>
    284     <tr>
    285         <td align="center"><code>selectionArgs</code></td>
    286         <td align="center">
    287             (Tidak ada padanan persis. Argumen pemilihan mengganti <code>?</code> placeholder dalam
    288             klausa pemilihan.)
    289         </td>
    290     </tr>
    291     <tr>
    292         <td align="center"><code>sortOrder</code></td>
    293         <td align="center"><code>ORDER BY <em>col,col,...</em></code></td>
    294         <td>
    295             <code>sortOrder</code> menetapkan urutan munculnya baris dalam
    296             {@link android.database.Cursor} yang dihasilkan.
    297         </td>
    298     </tr>
    299 </table>
    300 <h3 id="ContentURIs">URI Konten</h3>
    301 <p>
    302     <strong>URI konten</strong> adalah URI yang mengidentifikasi data dalam penyedia. URI Konten
    303     menyertakan nama simbolis seluruh penyedia (<strong>otoritas</strong>nya) dan sebuah
    304     nama yang menunjuk ke tabel (<strong>path</strong>). Bila Anda memanggil
    305     metode klien untuk mengakses tabel dalam penyedia, URI konten untuk tabel itu adalah salah satu
    306     argumennya.
    307 </p>
    308 <p>
    309     Dalam baris kode sebelumnya, konstanta
    310     {@link android.provider.UserDictionary.Words#CONTENT_URI} mengandung URI konten dari
    311     tabel "words" kamus pengguna. Objek {@link android.content.ContentResolver}
    312     akan mengurai otoritas URI, dan menggunakannya untuk "mengetahui" penyedia dengan
    313     membandingkan otoritas tersebut dengan sebuah tabel sistem berisi penyedia yang dikenal.
    314 {@link android.content.ContentResolver}    kemudian bisa mengirim argumen query ke penyedia
    315     yang benar.
    316 </p>
    317 <p>
    318     {@link android.content.ContentProvider} menggunakan bagian path dari URI konten untuk memilih
    319     tabel yang akan diakses. Penyedia biasanya memiliki <strong>path</strong> untuk tiap tabel yang dieksposnya.
    320 </p>
    321 <p>
    322     Dalam baris kode sebelumnya, URI lengkap untuk tabel "words" adalah:
    323 </p>
    324 <pre>
    325 content://user_dictionary/words
    326 </pre>
    327 <p>
    328     dalam hal ini string <code>user_dictionary</code> adalah otoritas penyedia, dan string
    329     <code>words</code> adalah path tabel. String
    330     <code>content://</code> (<strong>skema</strong>) selalu ada,
    331     dan mengidentifikasinya sebagai URI konten.
    332 </p>
    333 <p>
    334     Banyak penyedia yang memperbolehkan Anda mengakses satu baris dalam tabel dengan menambahkan sebuah ID nilai
    335     ke akhir URI. Misalnya, untuk mengambil sebuah baris yang <code>_ID</code>-nya adalah
    336     <code>4</code> dari kamus pengguna, Anda bisa menggunakan URI konten ini:
    337 </p>
    338 <pre>
    339 Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
    340 </pre>
    341 <p>
    342     Anda akan sering menggunakan nilai-nilai ID bila telah mengambil satu set baris kemudian ingin memperbarui atau menghapus
    343     salah satunya.
    344 </p>
    345 <p class="note">
    346     <strong>Catatan:</strong> Kelas-kelas {@link android.net.Uri} dan {@link android.net.Uri.Builder}
    347     berisi metode praktis untuk membangun objek dari string URI yang tersusun dengan baik.
    348 {@link android.content.ContentUris}    berisi metode praktis untuk menambahkan nilai ID ke
    349     URI. Cuplikan kode sebelumnya menggunakan {@link android.content.ContentUris#withAppendedId
    350     withAppendedId()} untuk menambahkan id ke URI konten User Dictionary.
    351 </p>
    352 
    353 
    354     <!-- Retrieving Data from the Provider -->
    355 <h2 id="SimpleQuery">Mengambil Data dari Penyedia</h2>
    356 <p>
    357     Bagian ini menerangkan cara mengambil data dari penyedia, dengan menggunakan Penyedia Kamus Pengguna
    358     sebagai contoh.
    359 </p>
    360 <p class="note">
    361     Demi kejelasan, cuplikan kode di bagian ini memanggil
    362     {@link android.content.ContentResolver#query ContentResolver.query()} pada "UI thread"". Akan tetapi, dalam
    363     kode sesungguhnya, Anda harus melakukan query secara asinkron pada sebuah thread terpisah. Satu cara melakukannya
    364     adalah menggunakan kelas {@link android.content.CursorLoader}, yang dijelaskan
    365     lebih detail dalam panduan <a href="{@docRoot}guide/components/loaders.html">
    366     Loader</a>. Juga, baris-baris kode tersebut hanyalah cuplikan; tidak menunjukkan sebuah aplikasi
    367      lengkap.
    368 </p>
    369 <p>
    370     Untuk mengambil data dari penyedia, ikutilah langkah-langkah dasar ini:
    371 </p>
    372 <ol>
    373    <li>
    374         Minta izin akses baca untuk penyedia itu.
    375    </li>
    376    <li>
    377         Definisikan kode yang mengirim query ke penyedia.
    378    </li>
    379 </ol>
    380 <h3 id="RequestPermissions">Meminta izin akses baca</h3>
    381 <p>
    382     Untuk mengambil data dari penyedia, aplikasi Anda memerlukan "izin akses baca" untuk
    383     penyedia itu. Anda tidak bisa meminta izin ini saat runtime; sebagai gantinya, Anda harus menetapkan bahwa
    384     Anda memerlukan izin ini dalam manifes, dengan menggunakan elemen
    385 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
    386     dan nama persis izin yang didefinisikan oleh
    387     penyedia itu. Bila menetapkan elemen ini dalam manifes, Anda secara efektif "meminta"
    388     izin ini untuk aplikasi Anda. Bila pengguna menginstal aplikasi Anda, mereka secara implisit akan memberikan
    389     permintaan ini.
    390 </p>
    391 <p>
    392     Untuk menemukan nama persis dari izin akses baca untuk penyedia yang sedang Anda gunakan, serta
    393     nama-nama izin akses lain yang digunakan oleh penyedia, lihatlah dalam
    394     dokumentasi penyedia.
    395 </p>
    396 <p>
    397     Peran izin dalam yang mengakses penyedia dijelaskan lebih detail di bagian
    398     <a href="#Permissions">Izin Penyedia Konten</a>.
    399 </p>
    400 <p>
    401     Penyedia Kamus Pengguna mendefinisikan izin
    402     <code>android.permission.READ_USER_DICTIONARY</code> dalam file manifesnya, sehingga
    403     aplikasi yang ingin membaca dari penyedia itu harus meminta izin ini.
    404 </p>
    405 <!-- Constructing the query -->
    406 <h3 id="Query">Membuat query</h3>
    407 <p>
    408     Langkah berikutnya dalam mengambil data penyedia adalah membuat query. Cuplikan kode pertama ini
    409     mendefinisikan beberapa variabel untuk mengakses Penyedia Kamus Pengguna:
    410 </p>
    411 <pre class="prettyprint">
    412 
    413 // A "projection" defines the columns that will be returned for each row
    414 String[] mProjection =
    415 {
    416     UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    417     UserDictionary.Words.WORD,   // Contract class constant for the word column name
    418     UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
    419 };
    420 
    421 // Defines a string to contain the selection clause
    422 String mSelectionClause = null;
    423 
    424 // Initializes an array to contain selection arguments
    425 String[] mSelectionArgs = {""};
    426 
    427 </pre>
    428 <p>
    429     Cuplikan berikutnya menampilkan cara menggunakan
    430     {@link android.content.ContentResolver#query ContentResolver.query()}, dengan menggunakan Penyedia Kamus Pengguna
    431     sebagai contoh. Query klien penyedia serupa dengan query SQL, dan berisi satu
    432     set kolom yang akan dihasilkan, satu set kriteria pemilihan, dan urutan sortir.
    433 </p>
    434 <p>
    435     Set kolom yang harus dikembalikan query disebut dengan <strong>proyeksi</strong>
    436     (variabel <code>mProjection</code>).
    437 </p>
    438 <p>
    439     Ekspresi yang menetapkan baris yang harus diambil dipecah menjadi klausa pemilihan dan
    440     argumen pemilihan. Klausa pemilihan adalah kombinasi ekspresi logis dan boolean,
    441     nama kolom, dan nilai (variabel <code>mSelectionClause</code>). Jika Anda menetapkan
    442     parameter <code>?</code> yang bisa diganti, sebagai ganti nilai, metode query akan mengambil nilai
    443     dari larik argumen pemilihan (variabel <code>mSelectionArgs</code>).
    444 </p>
    445 <p>
    446     Dalam cuplikan berikutnya, jika pengguna tidak memasukkan sebuah kata, klausa pemilihan akan diatur ke
    447     <code>null</code>, dan query menghasilkan semua kata dalam penyedia. Jika pengguna memasukkan
    448     sebuah kata, klausa pemilihan akan diatur ke <code>UserDictionary.Words.WORD + " = ?"</code> dan
    449     elemen pertama larik argumen pemilihan diatur ke kata yang dimasukkan pengguna.
    450 </p>
    451 <pre class="prettyprint">
    452 /*
    453  * This defines a one-element String array to contain the selection argument.
    454  */
    455 String[] mSelectionArgs = {""};
    456 
    457 // Gets a word from the UI
    458 mSearchString = mSearchWord.getText().toString();
    459 
    460 // Remember to insert code here to check for invalid or malicious input.
    461 
    462 // If the word is the empty string, gets everything
    463 if (TextUtils.isEmpty(mSearchString)) {
    464     // Setting the selection clause to null will return all words
    465     mSelectionClause = null;
    466     mSelectionArgs[0] = "";
    467 
    468 } else {
    469     // Constructs a selection clause that matches the word that the user entered.
    470     mSelectionClause = UserDictionary.Words.WORD + " = ?";
    471 
    472     // Moves the user's input string to the selection arguments.
    473     mSelectionArgs[0] = mSearchString;
    474 
    475 }
    476 
    477 // Does a query against the table and returns a Cursor object
    478 mCursor = getContentResolver().query(
    479     UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    480     mProjection,                       // The columns to return for each row
    481     mSelectionClause                   // Either null, or the word the user entered
    482     mSelectionArgs,                    // Either empty, or the string the user entered
    483     mSortOrder);                       // The sort order for the returned rows
    484 
    485 // Some providers return null if an error occurs, others throw an exception
    486 if (null == mCursor) {
    487     /*
    488      * Insert code here to handle the error. Be sure not to use the cursor! You may want to
    489      * call android.util.Log.e() to log this error.
    490      *
    491      */
    492 // If the Cursor is empty, the provider found no matches
    493 } else if (mCursor.getCount() &lt; 1) {
    494 
    495     /*
    496      * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
    497      * an error. You may want to offer the user the option to insert a new row, or re-type the
    498      * search term.
    499      */
    500 
    501 } else {
    502     // Insert code here to do something with the results
    503 
    504 }
    505 </pre>
    506 <p>
    507     Query ini analog dengan pernyataan SQL:
    508 </p>
    509 <pre>
    510 SELECT _ID, word, locale FROM words WHERE word = &lt;userinput&gt; ORDER BY word ASC;
    511 </pre>
    512 <p>
    513     Dalam pernyataan SQL ini, nama kolom yang sesungguhnya digunakan sebagai ganti konstanta kelas kontrak.
    514 </p>
    515 <h4 id="Injection">Melindungi dari input merusak</h4>
    516 <p>
    517     Jika data dikelola oleh penyedia konten berada dalam database SQL, memasukkan data tak dipercaya eksternal
    518     ke dalam pernyataan SQL mentah bisa menyebabkan injeksi SQL.
    519 </p>
    520 <p>
    521     Perhatikan klausa pemilihan ini:
    522 </p>
    523 <pre>
    524 // Constructs a selection clause by concatenating the user's input to the column name
    525 String mSelectionClause =  "var = " + mUserInput;
    526 </pre>
    527 <p>
    528     Jika melakukannya, Anda akan membuat pengguna menyambungkan SQL merusak ke pernyataan SQL Anda.
    529     Misalnya, pengguna bisa memasukkan "nothing; DROP TABLE *;"  untuk <code>mUserInput</code>, yang
    530     akan menghasilkan klausa pemilihan <code>var = nothing; DROP TABLE *;</code>. Karena
    531     klausa pemilihan diperlakukan sebagai pernyataan SQL, hal ini bisa menyebabkan penyedia itu menghapus semua
    532     tabel dalam database SQLite yang mendasarinya (kecuali penyedia disiapkan untuk menangkap upaya
    533     <a href="http://en.wikipedia.org/wiki/SQL_injection">injeksi SQL</a>).
    534 </p>
    535 <p>
    536     Untuk menghindari masalah ini, gunakan klausa pemilihan yang menggunakan <code>?</code> sebagai
    537     parameter yang bisa diganti dan larik argumen pemilihan yang terpisah. Bila Anda melakukannya, input pengguna
    538     akan dibatasi secara langsung pada query agar tidak ditafsirkan sebagai bagian dari pernyataan SQL.
    539     Karena tidak diperlakukan sebagai SQL, input pengguna tidak bisa menyuntikkan SQL merusak. Sebagai ganti menggunakan
    540     penyambungan untuk menyertakan input pengguna, gunakan klausa pemilihan ini:
    541 </p>
    542 <pre>
    543 // Constructs a selection clause with a replaceable parameter
    544 String mSelectionClause =  "var = ?";
    545 </pre>
    546 <p>
    547     Buat larik argumen pemilihan seperti ini:
    548 </p>
    549 <pre>
    550 // Defines an array to contain the selection arguments
    551 String[] selectionArgs = {""};
    552 </pre>
    553 <p>
    554     Masukkan nilai dalam larik argumen pemilihan seperti ini:
    555 </p>
    556 <pre>
    557 // Sets the selection argument to the user's input
    558 selectionArgs[0] = mUserInput;
    559 </pre>
    560 <p>
    561     Sebuah klausa pemilihan yang menggunakan <code>?</code> sebagai parameter yang bisa diganti dan sebuah larik
    562     argumen pemilihan adalah cara yang lebih disukai untuk menyebutkan pemilihan, sekalipun penyedia tidak
    563     dibuat berdasarkan database SQL.
    564 </p>
    565 <!-- Displaying the results -->
    566 <h3 id="DisplayResults">Menampilkan hasil query</h3>
    567 <p>
    568     Metode klien {@link android.content.ContentResolver#query ContentResolver.query()} selalu
    569     menghasilkan {@link android.database.Cursor} berisi kolom-kolom yang ditetapkan oleh
    570     proyeksi query untuk baris yang cocok dengan kriteria pemilihan query. Objek
    571     {@link android.database.Cursor} menyediakan akses baca acak ke baris dan kolom yang
    572     dimuatnya. Dengan metode {@link android.database.Cursor}, Anda bisa mengulang baris-baris dalam
    573     hasil, menentukan tipe data tiap kolom, mengambil data dari kolom, dan memeriksa
    574     properti lain dari hasil. Beberapa implementasi {@link android.database.Cursor}
    575     akan memperbarui objek secara otomatis bila data penyedia berubah, atau memicu metode dalam objek pengamat
    576     bila {@link android.database.Cursor} berubah, atau keduanya.
    577 </p>
    578 <p class="note">
    579     <strong>Catatan:</strong> Penyedia bisa membatasi akses ke kolom berdasarkan sifat
    580     objek yang membuat query. Misalnya, Penyedia Kontak membatasi akses untuk beberapa kolom pada
    581     adaptor sinkronisasi, sehingga tidak akan mengembalikannya ke aktivitas atau layanan.
    582 </p>
    583 <p>
    584     Jika tidak ada baris yang cocok dengan kriteria pemilihan, penyedia
    585     akan mengembalikan objek {@link android.database.Cursor} dengan
    586     {@link android.database.Cursor#getCount Cursor.getCount()} adalah 0 (kursor kosong).
    587 </p>
    588 <p>
    589     Jika terjadi kesalahan internal, hasil query akan bergantung pada penyedia tertentu. Penyedia bisa
    590     memilih untuk menghasilkan <code>null</code>, atau melontarkan {@link java.lang.Exception}.
    591 </p>
    592 <p>
    593     Karena {@link android.database.Cursor} adalah "daftar" baris, cara yang cocok untuk menampilkan
    594     konten {@link android.database.Cursor} adalah mengaitkannya dengan {@link android.widget.ListView}
    595     melalui {@link android.widget.SimpleCursorAdapter}.
    596 </p>
    597 <p>
    598     Cuplikan berikut melanjutkan kode dari cuplikan sebelumnya. Cuplikan ini membuat
    599     objek {@link android.widget.SimpleCursorAdapter} berisi {@link android.database.Cursor}
    600     yang diambil oleh query, dan mengatur objek ini menjadi adaptor bagi
    601     {@link android.widget.ListView}:
    602 </p>
    603 <pre class="prettyprint">
    604 // Defines a list of columns to retrieve from the Cursor and load into an output row
    605 String[] mWordListColumns =
    606 {
    607     UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    608     UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
    609 };
    610 
    611 // Defines a list of View IDs that will receive the Cursor columns for each row
    612 int[] mWordListItems = { R.id.dictWord, R.id.locale};
    613 
    614 // Creates a new SimpleCursorAdapter
    615 mCursorAdapter = new SimpleCursorAdapter(
    616     getApplicationContext(),               // The application's Context object
    617     R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
    618     mCursor,                               // The result from the query
    619     mWordListColumns,                      // A string array of column names in the cursor
    620     mWordListItems,                        // An integer array of view IDs in the row layout
    621     0);                                    // Flags (usually none are needed)
    622 
    623 // Sets the adapter for the ListView
    624 mWordList.setAdapter(mCursorAdapter);
    625 </pre>
    626 <p class="note">
    627     <strong>Catatan:</strong> Untuk mendukung {@link android.widget.ListView} dengan
    628     {@link android.database.Cursor}, kursor harus berisi kolom bernama <code>_ID</code>.
    629     Karena itu, query yang ditampilkan sebelumnya mengambil kolom <code>_ID</code> untuk
    630     tabel "words", walaupun {@link android.widget.ListView} tidak menampilkannya.
    631     Pembatasan ini juga menjelaskan mengapa sebagian besar penyedia memiliki kolom <code>_ID</code> untuk masing-masing
    632     tabelnya.
    633 </p>
    634 
    635         <!-- Getting data from query results -->
    636 <h3 id="GettingResults">Mendapatkan data dari hasil query</h3>
    637 <p>
    638     Daripada sekadar menampilkan hasil query, Anda bisa menggunakannya untuk tugas-tugas lain. Misalnya,
    639     Anda bisa mengambil ejaan dari kamus pengguna kemudian mencarinya dalam
    640     penyedia lain. Caranya, ulangi baris-baris dalam {@link android.database.Cursor}:
    641 </p>
    642 <pre class="prettyprint">
    643 
    644 // Determine the column index of the column named "word"
    645 int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
    646 
    647 /*
    648  * Only executes if the cursor is valid. The User Dictionary Provider returns null if
    649  * an internal error occurs. Other providers may throw an Exception instead of returning null.
    650  */
    651 
    652 if (mCursor != null) {
    653     /*
    654      * Moves to the next row in the cursor. Before the first movement in the cursor, the
    655      * "row pointer" is -1, and if you try to retrieve data at that position you will get an
    656      * exception.
    657      */
    658     while (mCursor.moveToNext()) {
    659 
    660         // Gets the value from the column.
    661         newWord = mCursor.getString(index);
    662 
    663         // Insert code here to process the retrieved word.
    664 
    665         ...
    666 
    667         // end of while loop
    668     }
    669 } else {
    670 
    671     // Insert code here to report an error if the cursor is null or the provider threw an exception.
    672 }
    673 </pre>
    674 <p>
    675     Implementasi {@link android.database.Cursor} berisi beberapa metode "get" untuk
    676     mengambil berbagai tipe data dari objek. Misalnya, cuplikan sebelumnya
    677     menggunakan {@link android.database.Cursor#getString getString()}. Implementasi juga memiliki
    678     metode {@link android.database.Cursor#getType getType()} yang menghasilkan nilai yang menunjukkan
    679     tipe data kolom.
    680 </p>
    681 
    682 
    683     <!-- Requesting permissions -->
    684 <h2 id="Permissions">Izin Penyedia Konten</h2>
    685 <p>
    686     Aplikasi penyedia bisa menetapkan izin yang harus dimiliki aplikasi lain untuk
    687     mengakses data penyedia. Izin ini akan memastikan bahwa pengguna mengetahui data
    688     yang coba diakses oleh aplikasi. Berdasarkan ketentuan penyedia, aplikasi lain
    689     meminta izin yang diperlukannya untuk mengakses penyedia. Pengguna akhir akan melihat
    690     izin yang diminta saat menginstal aplikasi.
    691 </p>
    692 <p>
    693     Jika aplikasi penyedia tidak menetapkan izin apa pun, maka aplikasi lain tidak memiliki
    694     akses ke data penyedia. Akan tetapi, komponen-komponen dalam aplikasi penyedia selalu memiliki
    695     akses penuh untuk baca dan tulis, izin apa pun yang ditetapkan.
    696 </p>
    697 <p>
    698     Seperti disebutkan sebelumnya, Penyedia Kamus Pengguna mensyaratkan izin
    699     <code>android.permission.READ_USER_DICTIONARY</code> untuk mengambil data darinya.
    700     Penyedia memiliki izin <code>android.permission.WRITE_USER_DICTIONARY</code>
    701     yang terpisah untuk menyisipkan, memperbarui, atau menghapus data.
    702 </p>
    703 <p>
    704     Untuk mendapatkan izin yang diperlukan untuk mengakses penyedia, aplikasi memintanya dengan elemen
    705 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
    706     dalam file manifesnya. Bila Android Package Manager memasang aplikasi, pengguna
    707     harus menyetujui semua izin yang diminta aplikasi. Jika pengguna menyetujui semuanya,
    708     Package Manager akan melanjutkan instalasi; jika pengguna tidak menyetujui, Package Manager
    709     akan membatalkan instalasi.
    710 </p>
    711 <p>
    712     Elemen
    713 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
    714     berikut meminta akses baca ke Penyedia Kamus Pengguna:
    715 </p>
    716 <pre>
    717     &lt;uses-permission android:name="android.permission.READ_USER_DICTIONARY"&gt;
    718 </pre>
    719 <p>
    720     Dampak izin pada akses penyedia dijelaskan secara lebih detail dalam panduan
    721     <a href="{@docRoot}guide/topics/security/security.html">Keamanan dan Izin</a>.
    722 </p>
    723 
    724 
    725 <!-- Inserting, Updating, and Deleting Data -->
    726 <h2 id="Modifications">Menyisipkan, Memperbarui, dan Menghapus Data</h2>
    727 <p>
    728     Lewat cara yang sama dengan cara mengambil data dari penyedia, Anda juga menggunakan interaksi antara
    729     klien penyedia dan {@link android.content.ContentProvider} penyedia untuk memodifikasi data.
    730     Anda memanggil metode {@link android.content.ContentResolver} dengan argumen yang diteruskan ke
    731     metode {@link android.content.ContentProvider} yang sesuai. Penyedia dan klien penyedia
    732     menangani secara otomatis keamanan dan komunikasi antar-proses.
    733 </p>
    734 <h3 id="Inserting">Menyisipkan data</h3>
    735 <p>
    736     Untuk menyisipkan data ke penyedia, Anda memanggil metode
    737     {@link android.content.ContentResolver#insert ContentResolver.insert()}.
    738  Metode ini menyisipkan sebuah baris baru ke penyedia itu dan menghasilkan URI konten untuk baris itu.
    739     Cuplikan ini menampilkan cara menyisipkan sebuah kata baru ke Penyedia Kamus Pengguna:
    740 </p>
    741 <pre class="prettyprint">
    742 // Defines a new Uri object that receives the result of the insertion
    743 Uri mNewUri;
    744 
    745 ...
    746 
    747 // Defines an object to contain the new values to insert
    748 ContentValues mNewValues = new ContentValues();
    749 
    750 /*
    751  * Sets the values of each column and inserts the word. The arguments to the "put"
    752  * method are "column name" and "value"
    753  */
    754 mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
    755 mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
    756 mNewValues.put(UserDictionary.Words.WORD, "insert");
    757 mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
    758 
    759 mNewUri = getContentResolver().insert(
    760     UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
    761     mNewValues                          // the values to insert
    762 );
    763 </pre>
    764 <p>
    765     Data untuk baris baru masuk ke dalam satu objek {@link android.content.ContentValues}, yang
    766     serupa bentuknya dengan kursor satu-baris. Kolom dalam objek ini tidak perlu memiliki
    767     tipe data yang sama, dan jika Anda tidak ingin menetapkan nilai sama sekali, Anda bisa mengatur kolom
    768     ke <code>null</code> dengan menggunakan {@link android.content.ContentValues#putNull ContentValues.putNull()}.
    769 </p>
    770 <p>
    771     Cuplikan ini tidak menambahkan kolom <code>_ID</code>, karena kolom ini dipelihara
    772     secara otomatis. Penyedia menetapkan sebuah nilai unik <code>_ID</code> ke setiap baris yang
    773     ditambahkan. Penyedia biasanya menggunakan nilai ini sebagai kunci utama tabel.
    774 </p>
    775 <p>
    776     URI konten yang dihasilkan dalam <code>newUri</code> akan mengidentifikasi baris yang baru ditambahkan, dengan
    777     format berikut:
    778 </p>
    779 <pre>
    780 content://user_dictionary/words/&lt;id_value&gt;
    781 </pre>
    782 <p>
    783     <code>&lt;id_value&gt;</code> adalah konten <code>_ID</code> untuk baris baru.
    784     Kebanyakan penyedia bisa mendeteksi bentuk URI konten ini secara otomatis kemudian melakukan
    785     operasi yang diminta pada baris tersebut.
    786 </p>
    787 <p>
    788     Untuk mendapatkan nilai <code>_ID</code> dari {@link android.net.Uri} yang dihasilkan, panggil
    789     {@link android.content.ContentUris#parseId ContentUris.parseId()}.
    790 </p>
    791 <h3 id="Updating">Memperbarui data</h3>
    792 <p>
    793     Untuk memperbarui sebuah baris, gunakan objek {@link android.content.ContentValues} dengan
    794     nilai-nilai yang diperbarui, persis seperti yang Anda lakukan pada penyisipan, dan kriteria pemilihan persis seperti yang Anda lakukan pada query.
    795     Metode klien yang Anda gunakan adalah
    796     {@link android.content.ContentResolver#update ContentResolver.update()}. Anda hanya perlu menambahkan
    797     nilai-nilai ke objek {@link android.content.ContentValues} untuk kolom yang sedang Anda perbarui. Jika Anda
    798     ingin membersihkan konten kolom, aturlah nilai ke <code>null</code>.
    799 </p>
    800 <p>
    801     Cuplikan berikut mengubah semua baris yang kolom lokalnya memiliki bahasa "en" ke
    802     lokal <code>null</code>. Nilai hasil adalah jumlah baris yang diperbarui:
    803 </p>
    804 <pre>
    805 // Defines an object to contain the updated values
    806 ContentValues mUpdateValues = new ContentValues();
    807 
    808 // Defines selection criteria for the rows you want to update
    809 String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
    810 String[] mSelectionArgs = {"en_%"};
    811 
    812 // Defines a variable to contain the number of updated rows
    813 int mRowsUpdated = 0;
    814 
    815 ...
    816 
    817 /*
    818  * Sets the updated value and updates the selected words.
    819  */
    820 mUpdateValues.putNull(UserDictionary.Words.LOCALE);
    821 
    822 mRowsUpdated = getContentResolver().update(
    823     UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    824     mUpdateValues                       // the columns to update
    825     mSelectionClause                    // the column to select on
    826     mSelectionArgs                      // the value to compare to
    827 );
    828 </pre>
    829 <p>
    830     Anda juga harus membersihkan input pengguna bila memanggil
    831     {@link android.content.ContentResolver#update ContentResolver.update()}. Untuk mengetahui selengkapnya tentang
    832     hal ini, bacalah bagian <a href="#Injection">Melindungi dari input merusak</a>.
    833 </p>
    834 <h3 id="Deleting">Menghapus data</h3>
    835 <p>
    836     Menghapus baris serupa dengan mengambil baris data: Anda menetapkan kriteria pemilihan untuk baris
    837     yang ingin Anda hapus dan metode klien akan menghasilkan jumlah baris yang dihapus.
    838     Cuplikan berikut menghapus baris yang appid-nya sama dengan "user". Metode menghasilkan
    839     jumlah baris yang dihapus.
    840 </p>
    841 <pre>
    842 
    843 // Defines selection criteria for the rows you want to delete
    844 String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
    845 String[] mSelectionArgs = {"user"};
    846 
    847 // Defines a variable to contain the number of rows deleted
    848 int mRowsDeleted = 0;
    849 
    850 ...
    851 
    852 // Deletes the words that match the selection criteria
    853 mRowsDeleted = getContentResolver().delete(
    854     UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    855     mSelectionClause                    // the column to select on
    856     mSelectionArgs                      // the value to compare to
    857 );
    858 </pre>
    859 <p>
    860     Anda juga harus membersihkan input pengguna bila memanggil
    861     {@link android.content.ContentResolver#delete ContentResolver.delete()}. Untuk mengetahui selengkapnya tentang
    862     hal ini, bacalah bagian <a href="#Injection">Melindungi dari input merusak</a>.
    863 </p>
    864 <!-- Provider Data Types -->
    865 <h2 id="DataTypes">Tipe Data Penyedia</h2>
    866 <p>
    867     Penyedia konten bisa menawarkan berbagai tipe data. Penyedia Kamus Pengguna hanya menawarkan
    868     teks, namun penyedia juga bisa menawarkan format berikut:
    869 </p>
    870     <ul>
    871         <li>
    872             integer
    873         </li>
    874         <li>
    875             long integer (long)
    876         </li>
    877         <li>
    878             floating point
    879         </li>
    880         <li>
    881             long floating point (double)
    882         </li>
    883     </ul>
    884 <p>
    885     Tipe data lain yang sering digunakan penyedia adalah Binary Large OBject (BLOB) yang diimplementasikan sebagai
    886     larik byte 64 KB. Anda bisa melihat tipe data yang tersedia dengan memperhatikan metode "get"
    887     kelas {@link android.database.Cursor}.
    888 </p>
    889 <p>
    890     Tipe data tiap kolom dalam penyedia biasanya tercantum dalam dokumentasinya.
    891     Tipe data untuk Penyedia Kamus Pengguna tercantum dalam dokumentasi acuan
    892     untuk kelas kontraknya {@link android.provider.UserDictionary.Words} (kelas kontrak
    893     dijelaskan di bagian <a href="#ContractClasses">Kelas-kelas Kontrak</a>).
    894     Anda juga bisa menentukan tipe data dengan memanggil {@link android.database.Cursor#getType
    895     Cursor.getType()}.
    896 </p>
    897 <p>
    898     Penyedia juga memelihara informasi tipe data MIME untuk tiap URI konten yang didefinisikannya. Anda bisa
    899     menggunakan informasi tipe MIME untuk mengetahui apakah aplikasi Anda bisa menangani data yang
    900     disediakan penyedia, atau memilih tipe penanganan berdasarkan tipe MIME. Anda biasanya memerlukan
    901     tipe MIME saat menggunakan penyedia yang berisi
    902     struktur atau file data yang kompleks. Misalnya, tabel {@link android.provider.ContactsContract.Data}
    903     dalam Penyedia Kontak menggunakan tipe MIME untuk memberi label tipe data kontak yang disimpan di tiap
    904     baris. Untuk mendapatkan tipe MIME yang sesuai dengan URI konten, panggil
    905     {@link android.content.ContentResolver#getType ContentResolver.getType()}.
    906 </p>
    907 <p>
    908     Bagian <a href="#MIMETypeReference">Acuan Tipe MIME</a> menerangkan
    909     sintaks tipe MIME baik yang standar maupun custom.
    910 </p>
    911 
    912 
    913 <!-- Alternative Forms of Provider Access -->
    914 <h2 id="AltForms">Bentuk-Bentuk Alternatif Akses Penyedia</h2>
    915 <p>
    916     Tiga bentuk alternatif akses penyedia adalah penting dalam pengembangan aplikasi:
    917 </p>
    918 <ul>
    919     <li>
    920         <a href="#Batch">Akses batch</a>: Anda bisa membuat sebuah batch panggilan akses dengan metode-metode dalam
    921         kelas {@link android.content.ContentProviderOperation}, kemudian menerapkannya dengan
    922         {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}.
    923     </li>
    924     <li>
    925         Query asinkron: Anda harus melakukan query dalam thread terpisah. Satu cara melakukannya adalah
    926         menggunakan objek {@link android.content.CursorLoader}. Contoh-contoh dalam panduan
    927         <a href="{@docRoot}guide/components/loaders.html">Loader</a> memperagakan
    928         cara melakukannya.
    929     </li>
    930     <li>
    931         <a href="#Intents">Akses data melalui intent</a>: Walaupun tidak bisa mengirim intent
    932         ke penyedia secara langsung, Anda bisa mengirim intent ke aplikasi penyedia, yang
    933         biasanya paling lengkap dibekali untuk memodifikasi data penyedia.
    934     </li>
    935 </ul>
    936 <p>
    937     Akses batch dan modifikasi melalui intent dijelaskan dalam bagian-bagian berikut.
    938 </p>
    939 <h3 id="Batch">Akses batch</h3>
    940 <p>
    941     Akses batch ke penyedia berguna untuk menyisipkan baris dalam jumlah besar, atau menyisipkan
    942     baris ke dalam beberapa tabel dalam panggilan metode yang sama, atau biasanya melakukan satu set
    943     operasi lintas batas proses sebagai transaksi (operasi atomik).
    944 </p>
    945 <p>
    946     Untuk mengakses penyedia dalam "mode batch",
    947     buat satu larik objek {@link android.content.ContentProviderOperation}, kemudian
    948     kirim larik itu ke penyedia konten dengan
    949     {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. Anda meneruskan
    950     <em>otoritas</em> penyedia konten ke metode ini, daripada URI konten tertentu.
    951     Ini memungkinkan tiap objek {@link android.content.ContentProviderOperation} dalam larik untuk bekerja
    952     terhadap tabel yang berbeda. Panggilan ke {@link android.content.ContentResolver#applyBatch
    953     ContentResolver.applyBatch()} menghasilkan satu larik hasil.
    954 </p>
    955 <p>
    956     Keterangan kelas kontrak {@link android.provider.ContactsContract.RawContacts}
    957     menyertakan cuplikan kode yang memperagakan penyisipan batch. Contoh aplikasi
    958     <a href="{@docRoot}resources/samples/ContactManager/index.html">Contacts Manager</a>
    959     berisi contoh akses batch dalam file sumber <code>ContactAdder.java</code>-nya
    960 .
    961 </p>
    962 <div class="sidebox-wrapper">
    963 <div class="sidebox">
    964 <h2>Menampilkan data dengan aplikasi pembantu</h2>
    965 <p>
    966     Jika aplikasi Anda <em>memang</em> memiliki izin akses, Anda masih mungkin perlu menggunakan
    967     intent untuk menampilkan data dalam aplikasi lain. Misalnya, aplikasi Kalender menerima
    968     intent {@link android.content.Intent#ACTION_VIEW}, yang menampilkan tanggal atau kejadian tertentu.
    969     Hal ini memungkinkan Anda menampilkan informasi kalender tanpa harus membuat UI sendiri.
    970     Untuk mengetahui selengkapnya tentang fitur ini, lihat panduan
    971     <a href="{@docRoot}guide/topics/providers/calendar-provider.html">Penyedia Kalender</a>.
    972 </p>
    973 <p>
    974     Aplikasi yang Anda kirimi intent tidak harus aplikasi
    975     yang terkait dengan penyedia. Misalnya, Anda bisa mengambil satu kontak dari
    976     Penyedia Kontak, kemudian mengirim intent {@link android.content.Intent#ACTION_VIEW}
    977     berisi URI konten untuk gambar kontak itu ke penampil gambar.
    978 </p>
    979 </div>
    980 </div>
    981 <h3 id="Intents">Akses data melalui intent</h3>
    982 <p>
    983     Intent bisa menyediakan akses tidak langsung ke penyedia konten. Anda memperbolehkan pengguna mengakses
    984     data dalam penyedia sekalipun aplikasi Anda tidak memiliki izin akses, baik dengan
    985     mendapatkan intent yang dihasilkan aplikasi yang memiliki izin, atau dengan mengaktifkan
    986     aplikasi yang memiliki izin dan membiarkan pengguna melakukan pekerjaan di dalamnya.
    987 </p>
    988 <h4>Mendapatkan akses dengan izin sementara</h4>
    989 <p>
    990     Anda bisa mengakses data dalam penyedia konten, sekalipun tidak memiliki
    991     izin akses yang sesuai, dengan mengirimkan intent ke aplikasi yang memang memiliki izin dan
    992     menerima hasil berupa intent berisi izin "URI".
    993     Inilah izin untuk URI konten tertentu yang berlaku hingga aktivitas yang menerima
    994     izin selesai. Aplikasi yang memiliki izin tetap akan memberikan
    995     izin sementara dengan mengatur flag dalam intent yang dihasilkan:
    996 </p>
    997 <ul>
    998     <li>
    999         <strong>Izin baca:</strong>
   1000         {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}
   1001     </li>
   1002     <li>
   1003         <strong>Izin tulis:</strong>
   1004         {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}
   1005     </li>
   1006 </ul>
   1007 <p class="note">
   1008     <strong>Catatan:</strong> Flag ini tidak memberikan akses baca atau tulis umum ke penyedia
   1009     yang otoritasnya dimuat dalam URI konten. Aksesnya hanya untuk URI itu sendiri.
   1010 </p>
   1011 <p>
   1012     Penyedia mendefinisikan izin URI untuk URI konten dalam manifesnya, dengan menggunakan atribut
   1013 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">android:grantUriPermission</a></code>
   1014     dari elemen
   1015 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>
   1016 ,   serta elemen anak
   1017 <code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html">&lt;grant-uri-permission&gt;</a></code>
   1018     dari elemen
   1019 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>.
   1020  Mekanisme izin URI dijelaskan secara lebih detail dalam panduan
   1021     <a href="{@docRoot}guide/topics/security/security.html">Keamanan dan Izin</a>,
   1022     di bagian "Izin URI".
   1023 </p>
   1024 <p>
   1025     Misalnya, Anda bisa mengambil data untuk satu kontak di Penyedia Kontak, sekalipun tidak
   1026     memiliki izin {@link android.Manifest.permission#READ_CONTACTS}. Anda mungkin ingin melakukan
   1027     ini dalam aplikasi yang mengirim kartu ucapan elektronik ke seorang kenalan pada hari ulang tahunnya. Sebagai ganti
   1028     meminta {@link android.Manifest.permission#READ_CONTACTS}, yang memberi Anda akses ke semua
   1029     kontak pengguna dan semua informasinya, Anda lebih baik membiarkan pengguna mengontrol
   1030     kontak-kontak yang akan digunakan oleh aplikasi Anda. Caranya, gunakan proses berikut:
   1031 </p>
   1032 <ol>
   1033     <li>
   1034         Aplikasi Anda akan mengirim intent berisi tindakan
   1035         {@link android.content.Intent#ACTION_PICK} dan tipe MIME "contacts"
   1036         {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}, dengan menggunakan
   1037         metode {@link android.app.Activity#startActivityForResult
   1038         startActivityForResult()}.
   1039     </li>
   1040     <li>
   1041         Karena intent ini cocok dengan filter intent untuk
   1042         aktivitas "pemilihan" aplikasi People, aktivitas akan muncul ke latar depan.
   1043     </li>
   1044     <li>
   1045         Dalam aktivitas pemilihan, pengguna memilih sebuah
   1046         kontak untuk diperbarui. Bila ini terjadi, aktivitas pemilihan akan memanggil
   1047         {@link android.app.Activity#setResult setResult(resultcode, intent)}
   1048         untuk membuat intent yang akan diberikan kembali ke aplikasi Anda. Intent itu berisi URI konten
   1049         kontak yang dipilih pengguna, dan flag "extras"
   1050         {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}. Semua flag ini memberikan
   1051         izin URI ke aplikasi Anda untuk membaca data kontak yang ditunjuk oleh
   1052         URI konten. Aktivitas pemilihan kemudian memanggil {@link android.app.Activity#finish()} untuk
   1053         mengembalikan kontrol ke aplikasi Anda.
   1054     </li>
   1055     <li>
   1056         Aktivitas Anda akan kembali ke latar depan, dan sistem memanggil metode
   1057         {@link android.app.Activity#onActivityResult onActivityResult()}
   1058         aktivitas Anda. Metode ini menerima intent yang dihasilkan oleh aktivitas pemilihan dalam
   1059         aplikasi People.
   1060     </li>
   1061     <li>
   1062         Dengan URI konten dari intent yang dihasilkan, Anda bisa membaca data kontak
   1063         dari Penyedia Kontak, sekalipun Anda tidak meminta izin akses baca tetap
   1064         ke penyedia dalam manifes Anda. Anda kemudian bisa mendapatkan informasi hari ulang tahun si kontak
   1065         atau alamat emailnya, kemudian mengirim kartu ucapan elektronik.
   1066     </li>
   1067 </ol>
   1068 <h4>Menggunakan aplikasi lain</h4>
   1069 <p>
   1070     Satu cara mudah agar pengguna bisa memodifikasi data yang izin aksesnya tidak Anda miliki adalah
   1071     mengaktifkan aplikasi yang memiliki izin dan membiarkan pengguna melakukan pekerjaannya di sana.
   1072 </p>
   1073 <p>
   1074     Misalnya, aplikasi Kalender menerima
   1075     intent {@link android.content.Intent#ACTION_INSERT}, yang memungkinkan Anda mengaktifkan
   1076     UI penyisipan aplikasi itu. Anda bisa meneruskan data "extras" dalam intent ini, yang
   1077     digunakan aplikasi untuk mengisi dahulu UI-nya. Karena kejadian berulang memiliki sintaks yang rumit,
   1078     cara yang lebih disukai untuk menyisipkan kejadian ke dalam Penyedia Kalender adalah mengaktifkan aplikasi Kalender dengan
   1079     {@link android.content.Intent#ACTION_INSERT}, kemudian membiarkan pengguna menyisipkan kejadian di sana.
   1080 </p>
   1081 <!-- Contract Classes -->
   1082 <h2 id="ContractClasses">Kelas-kelas Kontrak</h2>
   1083 <p>
   1084     Kelas kontrak mendefinisikan konstanta yang membantu aplikasi menggunakan URI konten, nama
   1085     kolom, tindakan intent, dan fitur lain pada penyedia konten. Kelas kontrak tidak
   1086     disertakan secara otomatis bersama penyedia; pengembang penyedia harus mendefinisikannya kemudian
   1087     membuatnya tersedia bagi pengembang lain. Banyak penyedia yang disertakan pada platform Android
   1088     memiliki kelas kontrak yang sesuai dalam {@link android.provider} paketnya.
   1089 </p>
   1090 <p>
   1091     Misalnya, Penyedia Kamus Pengguna memiliki kelas kontrak
   1092     {@link android.provider.UserDictionary} yang berisi URI konten dan konstanta nama kolom. URI
   1093     konten untuk tabel "words" didefinisikan dalam konstanta
   1094     {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}.
   1095     Kelas {@link android.provider.UserDictionary.Words} juga berisi konstanta nama kolom,
   1096     yang digunakan dalam cuplikan contoh pada panduan ini. Misalnya, sebuah proyeksi query bisa
   1097     didefinisikan sebagai:
   1098 </p>
   1099 <pre>
   1100 String[] mProjection =
   1101 {
   1102     UserDictionary.Words._ID,
   1103     UserDictionary.Words.WORD,
   1104     UserDictionary.Words.LOCALE
   1105 };
   1106 </pre>
   1107 <p>
   1108     Kelas kontrak lain adalah {@link android.provider.ContactsContract} untuk Penyedia Kontak.
   1109     Dokumentasi acuan untuk kelas ini menyertakan contoh cuplikan kode. Salah satu
   1110     subkelasnya, {@link android.provider.ContactsContract.Intents.Insert}, adalah
   1111     kelas kontrak yang berisi konstanta untuk intent dan data intent.
   1112 </p>
   1113 
   1114 
   1115 <!-- MIME Type Reference -->
   1116 <h2 id="MIMETypeReference">Acuan Tipe MIME</h2>
   1117 <p>
   1118     Penyedia konten bisa menghasilkan tipe media MIME standar, atau string tipe MIME custom, atau keduanya.
   1119 </p>
   1120 <p>
   1121     Tipe MIME memiliki format
   1122 </p>
   1123 <pre>
   1124 <em>type</em>/<em>subtype</em>
   1125 </pre>
   1126 <p>
   1127     Misalnya, tipe MIME <code>text/html</code> yang dikenal luas memiliki tipe <code>text</code> dan
   1128     subtipe <code>html</code>. Jika penyedia menghasilkan tipe ini untuk sebuah URI, artinya
   1129     query dengan URI itu akan menghasilkan teks berisi tag HTML.
   1130 </p>
   1131 <p>
   1132     String tipe MIME custom, yang juga disebut dengan tipe MIME "khusus vendor", memiliki nilai-nilai
   1133     <em>tipe</em> dan <em>subtipe</em> yang lebih kompleks. Nilai <em>tipe</em> selalu
   1134 </p>
   1135 <pre>
   1136 vnd.android.cursor.<strong>dir</strong>
   1137 </pre>
   1138 <p>
   1139     untuk beberapa baris, atau
   1140 </p>
   1141 <pre>
   1142 vnd.android.cursor.<strong>item</strong>
   1143 </pre>
   1144 <p>
   1145     untuk satu baris.
   1146 </p>
   1147 <p>
   1148     <em>Subtipe</em> adalah khusus penyedia. Penyedia bawaan Android biasanya memiliki subtipe
   1149     sederhana. Misalnya, bila aplikasi Contacts membuat satu baris untuk nomor telepon,
   1150     aplikasi akan mengatur tipe MIME berikut di baris itu:
   1151 </p>
   1152 <pre>
   1153 vnd.android.cursor.item/phone_v2
   1154 </pre>
   1155 <p>
   1156     Perhatikan bahwa nilai subtipe adalah sekadar <code>phone_v2</code>.
   1157 </p>
   1158 <p>
   1159     Pengembang penyedia lain bisa membuat pola subtipe sendiri berdasarkan
   1160     otoritas dan nama-nama tabel penyedia. Misalnya, perhatikan penyedia yang berisi jadwal kereta api.
   1161     Otoritas penyedia adalah <code>com.example.trains</code>, dan berisi tabel-tabel
   1162     Line1, Line2, dan Line3. Untuk merespons URI konten
   1163 </p>
   1164 <p>
   1165 <pre>
   1166 content://com.example.trains/Line1
   1167 </pre>
   1168 <p>
   1169     untuk tabel Line1, penyedia menghasilkan tipe MIME
   1170 </p>
   1171 <pre>
   1172 vnd.android.cursor.<strong>dir</strong>/vnd.example.line1
   1173 </pre>
   1174 <p>
   1175      Untuk merespons URI konten
   1176 </p>
   1177 <pre>
   1178 content://com.example.trains/Line2/5
   1179 </pre>
   1180 <p>
   1181     untuk baris 5 di tabel Line2, penyedia menghasilkan tipe MIME
   1182 </p>
   1183 <pre>
   1184 vnd.android.cursor.<strong>item</strong>/vnd.example.line2
   1185 </pre>
   1186 <p>
   1187     Kebanyakan penyedia konten mendefinisikan konstanta kelas kontrak untuk tipe MIME yang digunakannya. Kelas kontrak
   1188     {@link android.provider.ContactsContract.RawContacts} pada Penyedia Kontak
   1189     misalnya, mendefinisikan konstanta
   1190     {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} untuk tipe MIME
   1191     baris kontak mentah tunggal.
   1192 </p>
   1193 <p>
   1194     URI konten untuk baris-baris tunggal dijelaskan di bagian
   1195     <a href="#ContentURIs">URI Konten</a>.
   1196 </p>
   1197