Penyedia konten mengelola akses ke repositori data pusat. Anda mengimplementasikan sebuah
penyedia layanan sebagai satu atau beberapa class dalam aplikasi Android, beserta elemen dalam
file manifes. Salah satu class Anda mengimplementasikan subclass dari
ContentProvider
, yang merupakan antarmuka antara penyedia Anda dan
aplikasi lain.
Meskipun penyedia konten dimaksudkan untuk membuat data tersedia bagi Anda dapat memiliki aktivitas di aplikasi yang memungkinkan pengguna dan mengubah data yang dikelola oleh penyedia Anda.
Halaman ini berisi proses dasar untuk membuat penyedia konten dan daftar API yang akan digunakan.
Sebelum Anda mulai membuat
Sebelum Anda mulai membuat penyedia, perhatikan hal-hal berikut:
-
Tentukan apakah Anda memerlukan penyedia konten. Anda perlu membangun konten
jika Anda ingin menyediakan satu atau beberapa fitur berikut:
- Anda ingin menawarkan data atau file yang kompleks ke aplikasi lain.
- Anda ingin mengizinkan pengguna menyalin data yang kompleks dari aplikasi Anda ke aplikasi lain.
- Anda ingin menyediakan saran penelusuran khusus dengan menggunakan kerangka kerja penelusuran.
- Anda ingin mengekspos aplikasi Anda data ke widget.
- Anda ingin mengimplementasikan
AbstractThreadedSyncAdapter
,CursorAdapter
, atauCursorLoader
Google Cloud Platform.
Anda tidak memerlukan penyedia untuk menggunakan database atau jenis penyimpanan persisten jika penggunaan sepenuhnya berada dalam aplikasi Anda sendiri dan Anda tidak memerlukan fitur sebelumnya yang tercantum. Sebagai gantinya, Anda dapat menggunakan salah satu sistem penyimpanan yang dijelaskan dalam Ringkasan penyimpanan data dan file.
- Jika Anda belum melakukannya, baca Dasar-dasar penyedia konten untuk mempelajari lebih lanjut penyedia dan cara kerjanya.
Berikutnya, ikuti langkah-langkah ini untuk membangun penyedia:
-
Desain storage mentah untuk data Anda. Penyedia konten menawarkan data dengan dua cara:
- Data file
- Data yang biasanya masuk ke dalam file, seperti foto, audio, atau video. Simpan file dalam direktori pribadi aplikasi spasi. Sebagai respons terhadap permintaan file dari aplikasi lain, penyedia bisa menawarkan {i> handle<i} ke file tersebut.
- "Terstruktur" data
- Data yang biasanya masuk ke dalam {i>database<i}, {i>array<i}, atau struktur serupa. Simpan data dalam bentuk yang kompatibel dengan tabel baris dan kolom. Baris mewakili entitas, seperti orang atau item dalam inventaris. Kolom mewakili data untuk entitas, seperti nama orang atau harga barang. Cara umum untuk jenis data ini disimpan dalam database SQLite, namun Anda bisa menggunakan dan penyimpanan persisten. Untuk mempelajari lebih lanjut jenis penyimpanan yang tersedia di sistem Android, lihat Bagian penyimpanan data desain.
-
Tentukan implementasi konkret dari class
ContentProvider
dan metode-metode yang diperlukannya. Class ini adalah antarmuka antara data Anda dan elemen Android. Untuk informasi selengkapnya tentang kelas ini, lihat Implementasikan class ContentProvider. - Definisikan string otoritas, URI konten, dan nama kolom penyedia. Jika Anda ingin aplikasi penyedia untuk menangani intent, juga mendefinisikan tindakan intent, data tambahan, dan flag. Definisikan juga izin yang Anda butuhkan untuk aplikasi yang ingin untuk mengakses data Anda. Pertimbangkan untuk mendefinisikan semua nilai ini sebagai konstanta dalam kelas kontrak terpisah. Nanti, Anda dapat mengekspos class ini kepada developer lain. Untuk selengkapnya informasi tentang URI konten, lihat Bagian Mendesain URI konten. Untuk informasi selengkapnya tentang intent, lihat class Bagian Intent dan akses data.
-
Tambahkan bagian opsional lainnya, seperti data sampel atau penerapan
dari
AbstractThreadedSyncAdapter
yang dapat menyinkronkan data antara penyedia dan data berbasis cloud.
Mendesain penyimpanan data
Penyedia materi adalah antarmuka ke data yang disimpan dalam format terstruktur. Sebelum membuat antarmuka, memutuskan bagaimana menyimpan data. Anda dapat menyimpan data dalam bentuk apa pun seperti, lalu rancang antarmuka untuk membaca dan menulis data yang diperlukan.
Berikut adalah beberapa teknologi penyimpanan data yang tersedia di Android:
- Jika Anda bekerja dengan data terstruktur, maka pertimbangkan salah satu {i>database<i} relasional seperti seperti SQLite atau datastore nilai kunci non-relasional seperti LevelDB. Jika Anda bekerja dengan data tidak terstruktur seperti audio, gambar, atau media video, maka pertimbangkan untuk data sebagai file. Anda dapat mengombinasikan beberapa jenis penyimpanan dan mengeksposnya menggunakan satu penyedia konten jika diperlukan.
-
Sistem Android dapat berinteraksi dengan library persistensi Room, yang
memberikan akses ke SQLite database API yang disediakan oleh penyedia Android
gunakan untuk menyimpan data
berorientasi tabel. Untuk membuat {i>database <i}menggunakan
membuat instance subclass dari
RoomDatabase
, sebagaimana dijelaskan di Menyimpan data di dalam database lokal menggunakan Room.Anda tidak harus menggunakan database untuk mengimplementasikan repositori. Penyedia muncul secara eksternal sebagai satu set tabel, mirip dengan {i>database<i} relasional, tetapi ini bukan persyaratan untuk implementasi internal penyedia.
- Untuk menyimpan file data, Android memiliki beragam API berorientasi file. Untuk mempelajari penyimpanan file lebih lanjut, baca Ringkasan penyimpanan data dan file. Jika Anda merancang penyedia yang menawarkan data terkait media seperti musik atau video, Anda dapat memiliki penyedia yang menggabungkan data tabel dan file.
- Dalam kasus yang jarang terjadi, Anda mungkin mendapat manfaat dari menerapkan lebih dari satu penyedia konten untuk aplikasi tunggal. Misalnya, Anda mungkin ingin berbagi beberapa data dengan widget menggunakan satu penyedia konten, dan mengekspos set data yang berbeda untuk dibagikan menggunakan berbagai aplikasi obrolan.
-
Untuk menggunakan data berbasis jaringan, gunakan class di
java.net
danandroid.net
. Anda juga dapat menyinkronkan data berbasis jaringan ke data lokal menyimpan seperti {i>database<i}, dan kemudian menawarkan data itu sebagai tabel atau file.
Catatan: Jika Anda membuat perubahan pada repositori yang tidak agar kompatibel dengan versi sebelumnya, Anda perlu menandai repositori dengan versi baru angka Anda juga perlu meningkatkan nomor versi untuk aplikasi Anda yang menerapkan penyedia konten baru. Melakukan perubahan ini akan mencegah sistem menurunkan versi agar sistem macet saat mencoba menginstal ulang yang memiliki penyedia konten yang tidak kompatibel.
Pertimbangan desain data
Berikut ini beberapa tips untuk mendesain struktur data penyedia Anda:
-
Data tabel harus selalu memiliki "kunci utama" yang dikelola penyedia
sebagai nilai numerik
unik untuk setiap baris. Anda dapat menggunakan nilai ini untuk menautkan baris ke baris terkait
baris di tabel lain (dengan menggunakannya sebagai "foreign key"). Meskipun Anda dapat menggunakan nama apa pun
untuk kolom ini, menggunakan
BaseColumns._ID
adalah cara terbaik pilihan, karena menautkan hasil kueri penyedia keListView
memerlukan salah satu kolom yang diambil memiliki nama_ID
. -
Jika Anda ingin menyediakan gambar bitmap atau potongan data berorientasi file lainnya yang sangat besar, simpan
data dalam file dan kemudian menyediakannya secara tidak langsung
alih-alih menyimpannya secara langsung di
tabel sementara. Jika Anda melakukannya, Anda perlu memberi tahu pengguna penyedia Anda bahwa mereka perlu menggunakan
Metode file
ContentResolver
untuk mengakses data. -
Gunakan jenis data objek besar biner (BLOB) untuk menyimpan data yang ukurannya bervariasi atau memiliki
struktur yang bervariasi. Misalnya, Anda dapat menggunakan kolom BLOB untuk menyimpan
buffering protokol atau
Struktur JSON.
Anda juga dapat menggunakan BLOB untuk menerapkan tabel yang tidak bergantung skema. Di beberapa jenis tabel ini, Anda mendefinisikan kolom {i>primary key<i}, kolom jenis MIME, dan satu atau lebih banyak kolom umum sebagai BLOB. Arti data dalam kolom BLOB ditunjukkan dengan nilai dalam kolom jenis MIME. Ini memungkinkan Anda menyimpan berbagai tipe baris di tabel yang sama. "Data" Penyedia Kontak meja
ContactsContract.Data
adalah contoh project yang tidak bergantung pada skema tabel sementara.
URI konten desain
URI konten adalah URI yang mengidentifikasi data dalam penyedia. URI konten mencakup
nama simbolis seluruh penyedia (otoritasnya) dan
nama yang mengarah ke tabel atau file (jalur). Bagian ID opsional menunjuk ke
baris individu
dalam tabel. Setiap metode akses data dari
ContentProvider
memiliki URI konten sebagai argumen. Hal ini memungkinkan Anda
menentukan tabel, baris, atau file yang akan diakses.
Untuk informasi tentang URI konten, lihat Dasar-dasar penyedia konten.
Merancang otoritas
Penyedia biasanya memiliki otoritas tunggal, yang berfungsi sebagai nama internal Android-nya. Kepada menghindari konflik dengan penyedia lain, menggunakan kepemilikan domain internet (secara terbalik) sebagai dasar otoritas penyedia Anda. Karena rekomendasi ini juga berlaku untuk Android nama paket, Anda dapat menentukan otoritas penyedia Anda sebagai perpanjangan dari nama dari paket yang berisi penyedia.
Misalnya, jika nama paket Android Anda adalah
com.example.<appname>
, beri penyedia Anda
otoritas com.example.<appname>.provider
.
Mendesain struktur jalur
Developer biasanya membuat URI konten dari otoritas dengan menambahkan jalur yang menunjuk ke
tabel individual. Misalnya, jika Anda memiliki dua tabel, table1 dan
table2, Anda dapat menggabungkannya dengan otoritas dari contoh sebelumnya untuk menghasilkan
URI konten
com.example.<appname>.provider/table1
dan
com.example.<appname>.provider/table2
. Jalur tidak
terbatas pada satu segmen, dan tidak perlu
memiliki tabel untuk setiap tingkat jalur.
Menangani ID URI konten
Berdasarkan konvensi, penyedia menawarkan akses ke satu baris dalam tabel dengan menerima URI konten
dengan nilai ID untuk baris di akhir URI. Juga berdasarkan konvensi, penyedia mencocokkan
ke kolom _ID
tabel dan melakukan akses yang diminta terhadap
baris yang sesuai.
Standar ini memudahkan pola desain umum untuk aplikasi yang mengakses penyedia. Aplikasi
melakukan kueri terhadap penyedia dan menampilkan Cursor
yang dihasilkan
di ListView
menggunakan CursorAdapter
.
Definisi CursorAdapter
memerlukan salah satu kolom di
Cursor
menjadi _ID
Pengguna kemudian mengambil salah satu baris yang ditampilkan dari UI untuk melihat atau memodifikasi
layanan otomatis dan data skalabel. Aplikasi mendapatkan baris yang sesuai dari Cursor
yang mendukung
ListView
, dapatkan nilai _ID
untuk baris ini, menambahkannya ke
URI materi, dan mengirimkan permintaan akses ke penyedia. Penyedia kemudian dapat melakukan
terhadap baris yang tepat
yang dipilih pengguna.
Pola URI materi
Untuk membantu Anda memilih tindakan yang harus diambil untuk URI konten yang masuk, API penyedia menyertakan
class praktis UriMatcher
, yang memetakan pola URI konten ke
nilai bilangan bulat. Anda dapat menggunakan nilai bilangan bulat dalam pernyataan switch
yang
memilih tindakan yang diinginkan untuk URI konten atau URI yang cocok dengan pola tertentu.
Pola URI materi mencocokkan dengan URI materi menggunakan karakter pengganti:
-
*
cocok dengan string dengan karakter valid apa pun dengan panjang berapa pun. -
#
cocok dengan string karakter numerik dengan panjang berapa pun.
Sebagai contoh perancangan dan pengkodean penanganan URI konten, pertimbangkan penyedia dengan
com.example.app.provider
otoritas yang mengenali URI konten berikut
menunjuk ke tabel:
-
content://com.example.app.provider/table1
: tabel yang disebuttable1
. -
content://com.example.app.provider/table2/dataset1
: tabel yang disebutdataset1
. -
content://com.example.app.provider/table2/dataset2
: tabel yang disebutdataset2
. -
content://com.example.app.provider/table3
: tabel yang disebuttable3
.
Penyedia juga mengenali URI konten ini jika memiliki ID baris yang ditambahkan, seperti content://com.example.app.provider/table3/1
untuk baris yang diidentifikasi oleh
1
dalam table3
.
Pola URI konten berikut mungkin:
-
content://com.example.app.provider/*
- Mencocokkan URI konten di penyedia.
-
content://com.example.app.provider/table2/*
-
Cocok dengan URI konten untuk tabel
dataset1
dandataset2
, namun tidak cocok dengan URI konten untuktable1
atautable3
. -
content://com.example.app.provider/table3/#
-
Cocok dengan URI konten
untuk baris tunggal dalam
table3
, seperticontent://com.example.app.provider/table3/6
untuk baris yang diidentifikasi oleh6
.
Cuplikan kode berikut menunjukkan cara kerja metode di UriMatcher
.
Kode ini menangani URI untuk seluruh tabel secara berbeda dari URI untuk
satu baris dengan menggunakan pola URI konten
content://<authority>/<path>
untuk tabel dan
content://<authority>/<path>/<id>
untuk baris tunggal.
Metode addURI()
memetakan
otoritas dan jalur ke nilai bilangan bulat. Metode match()
menampilkan nilai bilangan bulat untuk URI. Pernyataan switch
memilih antara melakukan kueri seluruh tabel dan
melakukan kueri untuk satu {i>record<i}.
Kotlin
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used * in the path. */ addURI("com.example.app.provider", "table3", 1) /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ addURI("com.example.app.provider", "table3/#", 2) } ... class ExampleProvider : ContentProvider() { ... // Implements ContentProvider.query() override fun query( uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { var localSortOrder: String = sortOrder ?: "" var localSelection: String = selection ?: "" when (sUriMatcher.match(uri)) { 1 -> { // If the incoming URI was for all of table3 if (localSortOrder.isEmpty()) { localSortOrder = "_ID ASC" } } 2 -> { // If the incoming URI was for a single row /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ localSelection += "_ID ${uri?.lastPathSegment}" } else -> { // If the URI isn't recognized, // do some error handling here } } // Call the code to actually do the query } }
Java
public class ExampleProvider extends ContentProvider { ... // Creates a UriMatcher object. private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to one. No wildcard is used * in the path. */ uriMatcher.addURI("com.example.app.provider", "table3", 1); /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ uriMatcher.addURI("com.example.app.provider", "table3/#", 2); } ... // Implements ContentProvider.query() public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... /* * Choose the table to query and a sort order based on the code returned for the incoming * URI. Here, too, only the statements for table 3 are shown. */ switch (uriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ selection = selection + "_ID = " + uri.getLastPathSegment(); break; default: ... // If the URI isn't recognized, do some error handling here } // Call the code to actually do the query }
Class lain, ContentUris
, menyediakan metode praktis untuk bekerja
dengan bagian id
dari URI konten. Class Uri
dan
Uri.Builder
menyertakan metode praktis untuk mengurai yang ada
Uri
objek dan membuat objek baru.
Mengimplementasikan class ContentProvider
Instance ContentProvider
mengelola akses
ke satu set data terstruktur dengan menangani permintaan dari aplikasi lain. Semua formulir
akses akhirnya memanggil ContentResolver
, yang kemudian memanggil
dari ContentProvider
untuk mendapatkan akses.
Metode yang diperlukan
Class abstrak ContentProvider
menentukan enam metode abstrak yang
yang Anda terapkan sebagai bagian dari subclass konkret Anda. Semua metode ini kecuali
onCreate()
dipanggil oleh aplikasi klien
yang mencoba mengakses penyedia konten Anda.
-
query()
-
Ambil data dari penyedia Anda. Gunakan argumen untuk memilih tabel
kueri, baris dan kolom yang akan dikembalikan, dan tata urutan hasilnya.
Tampilkan data sebagai objek
Cursor
. -
insert()
- Masukkan baris baru ke penyedia Anda. Gunakan argumen untuk memilih tabel tujuan dan untuk mendapatkan nilai-nilai kolom yang akan digunakan. Menampilkan URI konten untuk baris yang baru disisipkan.
-
update()
- Perbarui baris yang ada di penyedia Anda. Menggunakan argumen untuk memilih tabel dan baris untuk memperbarui dan mendapatkan nilai kolom yang diperbarui. Mengembalikan jumlah baris yang diperbarui.
-
delete()
- Hapus baris dari penyedia Anda. Gunakan argumen untuk memilih tabel dan baris yang akan hapus. Mengembalikan jumlah baris yang dihapus.
-
getType()
- Menampilkan jenis MIME yang sesuai dengan URI konten. Metode ini dijelaskan lebih lanjut di bagian Mengimplementasikan jenis MIME penyedia konten.
-
onCreate()
-
Lakukan inisialisasi pada penyedia. Sistem Android memanggil metode ini segera setelahnya
membuat penyedia Anda. Penyedia Anda tidak dibuat sampai
Objek
ContentResolver
mencoba mengaksesnya.
Metode ini memiliki tanda tangan yang
sama dengan metode yang diberi nama identik
Metode ContentResolver
.
Penerapan metode ini harus memperhitungkan hal-hal berikut:
-
Semua metode ini kecuali
onCreate()
dapat dipanggil oleh beberapa thread sekaligus, jadi harus aman untuk thread. Untuk mempelajari selengkapnya tentang beberapa thread, lihat Ringkasan proses dan thread. -
Hindari melakukan operasi yang lama di
onCreate()
. Tunda inisialisasi tugas hingga benar-benar diperlukan. Bagian tentang mengimplementasikan metode onCreate() membahas hal ini secara lebih rinci. -
Walaupun harus mengimplementasikan metode-metode ini, kode Anda tidak perlu melakukan apa pun kecuali
menampilkan jenis data yang diharapkan. Misalnya, Anda dapat mencegah aplikasi lain
agar tidak memasukkan data ke dalam beberapa tabel
dengan mengabaikan panggilan ke
insert()
dan kembali 0.
Mengimplementasikan metode query()
Tujuan
Metode ContentProvider.query()
harus menampilkan objek Cursor
atau, jika
gagal, tampilkan Exception
. Jika menggunakan database SQLite sebagai data
penyimpanan, Anda dapat mengembalikan Cursor
yang ditampilkan oleh salah satu
Metode query()
dari class SQLiteDatabase
.
Jika kueri tidak cocok dengan baris mana pun, tampilkan Cursor
instance yang metode getCount()
-nya menampilkan 0.
Tampilkan null
hanya jika terjadi error internal selama proses kueri.
Jika Anda tidak menggunakan database SQLite sebagai penyimpanan data, gunakan salah satu subclass konkret
dari Cursor
. Misalnya, class MatrixCursor
menerapkan kursor dengan setiap baris merupakan array instance Object
. Dengan kelas ini,
gunakan addRow()
untuk menambahkan baris baru.
Sistem Android harus dapat menyampaikan Exception
lintas batas proses. Android dapat melakukannya untuk pengecualian berikut yang berguna
dalam menangani error kueri:
-
IllegalArgumentException
. Anda dapat memilih untuk menampilkan ini jika penyedia menerima URI konten yang tidak valid. -
NullPointerException
Mengimplementasikan metode insert()
Metode insert()
menambahkan
baris baru ke tabel yang sesuai, menggunakan nilai dalam ContentValues
argumen. Jika nama kolom tidak ada dalam argumen ContentValues
, Anda
mungkin ingin memberikan nilai default untuknya, baik di kode penyedia maupun di database Anda
skema.
Metode ini menampilkan URI konten untuk baris baru. Untuk membangun ini, tambahkan
kunci utama baris, biasanya nilai _ID
, ke URI konten tabel, menggunakan
withAppendedId()
.
Mengimplementasikan metode delete()
Metode delete()
tidak perlu menghapus baris
dari penyimpanan data Anda. Jika Anda menggunakan adaptor sinkronisasi
dengan penyedia Anda, pertimbangkan untuk menandai baris yang telah dihapus
dengan kata "delete" bukan menghapus baris secara keseluruhan. Adaptor sinkronisasi dapat
periksa baris yang telah dihapus dan membuangnya dari server sebelum menghapusnya dari penyedia.
Mengimplementasikan metode update()
Metode update()
mengambil argumen ContentValues
yang sama dengan yang digunakan oleh
insert()
dan
argumen selection
dan selectionArgs
yang sama yang digunakan oleh
delete()
dan
ContentProvider.query()
.
Hal ini memungkinkan Anda menggunakan kembali kode di antara metode ini.
Mengimplementasikan metode onCreate()
Sistem Android memanggil onCreate()
pada saat memulai penyedia. Hanya melakukan inisialisasi yang berjalan cepat
tugas dalam metode ini dan menunda pembuatan database dan pemuatan data hingga penyedia benar-benar
menerima permintaan data. Jika Anda melakukan
tugas yang panjang di
onCreate()
, Anda memperlambat
penyedia layanan startup. Pada akhirnya, hal ini akan memperlambat
respons dari penyedia terhadap
menggunakan berbagai aplikasi obrolan.
Dua cuplikan berikut menunjukkan interaksi antara
ContentProvider.onCreate()
dan
Room.databaseBuilder()
. Yang pertama
cuplikan yang menunjukkan implementasi
ContentProvider.onCreate()
di mana
objek database dibuat dan menangani objek akses data dibuat:
Kotlin
// Defines the database name private const val DBNAME = "mydb" ... class ExampleProvider : ContentProvider() { // Defines a handle to the Room database private lateinit var appDatabase: AppDatabase // Defines a Data Access Object to perform the database operations private var userDao: UserDao? = null override fun onCreate(): Boolean { // Creates a new database object appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build() // Gets a Data Access Object to perform the database operations userDao = appDatabase.userDao return true } ... // Implements the provider's insert method override fun insert(uri: Uri, values: ContentValues?): Uri? { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Java
public class ExampleProvider extends ContentProvider // Defines a handle to the Room database private AppDatabase appDatabase; // Defines a Data Access Object to perform the database operations private UserDao userDao; // Defines the database name private static final String DBNAME = "mydb"; public boolean onCreate() { // Creates a new database object appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build(); // Gets a Data Access Object to perform the database operations userDao = appDatabase.getUserDao(); return true; } ... // Implements the provider's insert method public Cursor insert(Uri uri, ContentValues values) { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Mengimplementasikan jenis MIME ContentProvider
Class ContentProvider
memiliki dua metode untuk menampilkan jenis MIME:
-
getType()
- Salah satu metode wajib yang Anda terapkan untuk penyedia mana pun.
-
getStreamTypes()
- Metode yang diharapkan untuk Anda implementasikan jika penyedia Anda menawarkan file.
Tipe MIME untuk tabel
Metode getType()
akan menampilkan
String
dalam format MIME yang menjelaskan jenis data yang ditampilkan oleh isi
Argumen URI. Argumen Uri
dapat berupa pola, bukan URI tertentu.
Dalam hal ini, tampilkan jenis data yang dikaitkan dengan URI konten yang cocok dengan
pola.
Untuk jenis data umum seperti teks, HTML, atau JPEG,
getType()
menampilkan standar
Jenis MIME untuk data tersebut. Daftar lengkap jenis standar ini tersedia di
Jenis Media MIME IANA
situs Anda.
Untuk URI konten yang menunjuk ke satu baris atau beberapa baris data tabel,
Pengembalian dengan biaya getType()
jenis MIME dalam format MIME khusus vendor Android:
-
Bagian jenis:
vnd
-
Bagian subjenis:
-
Jika pola URI adalah untuk satu baris:
android.cursor.item/
-
Jika pola URI adalah untuk lebih dari satu baris:
android.cursor.dir/
-
Jika pola URI adalah untuk satu baris:
-
Bagian khusus penyedia:
vnd.<name>
.<type>
Anda menyediakan
<name>
dan<type>
. Nilai<name>
bersifat unik secara global, dan nilai<type>
bersifat unik untuk URI yang sesuai pola. Pilihan yang tepat untuk<name>
adalah nama perusahaan Anda atau beberapa bagian dari nama paket Android aplikasi Anda. Pilihan yang bagus untuk<type>
adalah string yang mengidentifikasi tabel yang terkait dengan URI.
Misalnya, jika otoritas
penyedia melakukan
com.example.app.provider
, dan mengekspos tabel bernama
table1
, jenis MIME untuk beberapa baris di table1
adalah:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Untuk satu baris table1
, jenis MIME adalah:
vnd.android.cursor.item/vnd.com.example.provider.table1
Tipe MIME untuk file
Jika penyedia Anda menawarkan
file, implementasikan
getStreamTypes()
.
Metode ini menampilkan array String
jenis MIME untuk file penyedia Anda
dapat ditampilkan untuk URI konten tertentu. Filter jenis MIME yang Anda tawarkan berdasarkan jenis MIME
filter, sehingga Anda hanya menampilkan jenis MIME yang ingin ditangani klien.
Misalnya, pertimbangkan penyedia yang menawarkan
gambar foto sebagai {i>file<i} dalam format JPG,
PNG, dan GIF.
Jika aplikasi memanggil ContentResolver.getStreamTypes()
dengan string filter image/*
, untuk sesuatu yang
adalah "gambar",
maka metode ContentProvider.getStreamTypes()
akan menampilkan array:
{ "image/jpeg", "image/png", "image/gif"}
Jika aplikasi hanya tertarik dengan file JPG, maka aplikasi dapat memanggil
ContentResolver.getStreamTypes()
dengan string filter *\/jpeg
, dan
getStreamTypes()
akan menampilkan:
{"image/jpeg"}
Jika penyedia Anda tidak menawarkan
tipe MIME apa pun yang diminta dalam string filter,
getStreamTypes()
akan menampilkan null
.
Mengimplementasikan kelas kontrak
Kelas kontrak adalah class public final
yang berisi definisi konstan untuk
URI, nama kolom, jenis MIME, dan metadata lain yang berhubungan dengan penyedia. Kelas
membuat kontrak antara penyedia dan aplikasi lain dengan memastikan bahwa penyedia
dapat diakses dengan benar meskipun ada perubahan pada nilai aktual URI, nama kolom,
dan sebagainya.
Kelas kontrak juga membantu pengembang karena biasanya memiliki nama-nama simbolik untuk konstantanya, sehingga developer cenderung tidak menggunakan nilai yang salah untuk nama kolom atau URI. Karena ini adalah , dapat berisi dokumentasi Javadoc. {i>Integrated Development Environment<i} seperti Android Studio bisa melengkapi nama konstanta dari kelas kontrak secara otomatis dan menampilkan Javadoc untuk konstanta tersebut.
Developer tidak dapat mengakses file class kelas kontrak dari aplikasi Anda, tetapi mereka dapat mengompilasinya secara statis ke dalam aplikasi mereka dari file JAR yang Anda sediakan.
Class ContactsContract
dan class bertingkatnya adalah contoh
kelas kontrak.
Mengimplementasikan izin penyedia konten
Izin dan akses untuk semua aspek sistem Android dijelaskan secara rinci dalam Tips keamanan. Ringkasan penyimpanan data dan file juga menjelaskan keamanan dan izin yang berlaku untuk berbagai jenis penyimpanan. Secara singkat, poin-poin pentingnya adalah sebagai berikut:
- Secara default, file data yang disimpan di penyimpanan internal perangkat bersifat pribadi untuk Anda aplikasi dan penyedia layanan.
-
Database
SQLiteDatabase
yang Anda buat bersifat pribadi untuk aplikasi dan penyedia layanan. - Secara default, file data yang Anda simpan ke penyimpanan eksternal bersifat publik dan dapat dibaca secara global. Anda tidak dapat menggunakan penyedia konten untuk membatasi akses ke file di penyimpanan eksternal, karena aplikasi lain bisa menggunakan panggilan API lain untuk membaca dan menulisnya.
- Panggilan metode ini untuk membuka atau membuat file atau database SQLite pada server penyimpanan data dapat memberikan akses baca dan tulis ke semua aplikasi lain. Jika Anda menggunakan file atau database internal sebagai repositori penyedia Anda dan Anda memberikannya "dapat dibaca oleh seluruh dunia" atau "world-writeable" (dapat ditulis oleh publik) izin akses, izin akses yang Anda tetapkan untuk penyedia. manifesnya tidak melindungi data Anda. Akses {i>default<i} untuk file dan {i>database<i} di penyimpanan internal bersifat "pribadi"; jangan ubah ini untuk repositori penyedia Anda.
Jika Anda ingin menggunakan izin penyedia konten untuk mengontrol akses ke data Anda, maka menyimpan data Anda dalam file internal, database SQLite, atau cloud, seperti pada server jarak jauh, dan menjaga file dan database tetap pribadi untuk aplikasi Anda.
Mengimplementasikan izin
Secara default, semua aplikasi bisa membaca dari atau menulis ke penyedia Anda, bahkan jika data yang mendasarinya
{i>private<i}, karena secara {i>default<i} penyedia Anda tidak mengatur izin akses. Untuk mengubahnya,
mengatur izin bagi penyedia dalam file manifes, menggunakan atribut atau
elemen dari elemen
<provider>
. Anda dapat mengatur izin akses
yang berlaku untuk seluruh penyedia,
ke tabel tertentu, ke {i>record
<i}tertentu, atau ketiganya.
Anda menentukan izin untuk penyedia dengan satu atau beberapa
<permission>
dalam file manifes Anda. Untuk membuat
izin unik untuk penyedia, gunakan pencakupan bergaya Java untuk
Atribut
android:name
. Misalnya, beri nama izin akses baca
com.example.app.provider.permission.READ_PROVIDER
.
Daftar berikut ini menjelaskan cakupan izin penyedia, dimulai dengan izin akses yang berlaku untuk seluruh penyedia dan kemudian menjadi lebih terperinci. Izin yang lebih terperinci akan didahulukan daripada izin dengan cakupan yang lebih luas.
- Izin baca-tulis tunggal tingkat penyedia
-
Satu izin yang mengontrol akses baca dan tulis ke seluruh penyedia, yang ditetapkan
dengan atribut
android:permission
<provider>
. - Pisahkan izin baca dan tulis tingkat penyedia
-
Satu izin baca dan satu izin tulis untuk seluruh penyedia. Anda yang menentukannya
dengan
android:readPermission
danandroid:writePermission
atribut<provider>
. Izin akses akan didahulukan daripada izin yang disyaratkan olehandroid:permission
. - Izin tingkat path
-
Izin membaca, menulis, atau membaca/menulis untuk URI konten di penyedia Anda. Anda yang menentukan
setiap URI yang ingin Anda kontrol dengan
Elemen turunan
<path-permission>
dari elemen<provider>
. Untuk setiap URI konten yang ditetapkan, Anda dapat menentukan izin akses baca/tulis, izin akses membaca, izin akses tulis, atau ketiganya. Fungsi baca dan izin akses tulis akan didahulukan daripada izin akses baca/tulis. Selain itu, tingkat jalur lebih diprioritaskan daripada izin tingkat penyedia. - Izin sementara
-
Tingkat izin yang memberikan akses sementara ke aplikasi, bahkan jika aplikasi tersebut
tidak memiliki izin yang biasanya diperlukan. Atribut sementara
fitur akses mengurangi jumlah izin yang harus diminta aplikasi
manifesnya. Ketika Anda mengaktifkan izin akses sementara, satu-satunya aplikasi yang memerlukan
izin akses permanen untuk penyedia Anda
adalah izin akses yang terus-menerus
memahami
data Anda.
Misalnya, pertimbangkan izin yang diperlukan jika Anda mengimplementasikan penyedia dan aplikasi email dan Anda ingin mengizinkan aplikasi penampil gambar luar menampilkan lampiran foto dari penyedia layanan. Untuk memberikan akses yang diperlukan ke penampil gambar tanpa memerlukan izin, Anda dapat menyiapkan izin sementara untuk URI konten bagi foto.
Rancang aplikasi email Anda agar sehingga saat pengguna ingin menampilkan foto, aplikasi akan mengirimkan intent yang berisi URI materi foto dan tanda izin ke penampil gambar. Penampil gambar dapat kemudian kueri penyedia email Anda untuk mengambil foto, meskipun pemirsa tidak memiliki izin baca normal untuk penyedia Anda.
Untuk mengaktifkan izin sementara, setel atribut Atribut
android:grantUriPermissions
dari Elemen<provider>
atau tambahkan satu atau beberapa elemen<grant-uri-permission>
elemen turunan ke elemen<provider>
. TeleponContext.revokeUriPermission()
setiap kali Anda menghapus dukungan untuk URI konten yang terkait dengan izin sementara dari penyedia layanan.Nilai atribut menentukan seberapa banyak penyedia Anda yang dapat diakses. Jika atribut ditetapkan ke
"true"
, sistem akan memberikan izin sementara izin akses ke seluruh penyedia, menggantikan izin lain yang diperlukan berdasarkan izin tingkat penyedia atau tingkat jalur Anda.Jika tanda ini disetel ke
"false"
, tambahkan<grant-uri-permission>
elemen turunan ke<provider>
. Setiap elemen turunan menetapkan URI konten atau URI yang memberikan akses sementara.Untuk mendelegasikan akses sementara ke aplikasi, intent harus berisi flag
FLAG_GRANT_READ_URI_PERMISSION
, flag tandaFLAG_GRANT_WRITE_URI_PERMISSION
, atau keduanya. Ini ditetapkan dengan metodesetFlags()
.Jika atribut
android:grantUriPermissions
tidak ada, atribut tersebut diasumsikan"false"
.
<provider> elemen
Seperti komponen Activity
dan Service
,
subclass dari ContentProvider
didefinisikan dalam file manifes untuk aplikasinya, dengan menggunakan
elemen
<provider>
. Sistem Android mendapatkan
informasi berikut dari
elemen:
-
Kewenangan
(
android:authorities
) - Nama-nama simbolis yang mengidentifikasi seluruh penyedia dalam sistem. Ini dijelaskan secara lebih mendetail dalam Bagian Mendesain URI konten.
-
Nama class penyedia
(
android:name
) -
Class yang mengimplementasikan
ContentProvider
. Kelas ini yang dijelaskan secara lebih mendetail dalam Implementasikan class ContentProvider. - Izin
-
Atribut yang menentukan izin yang harus dimiliki aplikasi lain untuk mengakses
data penyedia:
-
android:grantUriPermissions
: tanda izin sementara -
android:permission
: izin baca/tulis tunggal tingkat penyedia -
android:readPermission
: izin baca seluruh penyedia -
android:writePermission
: izin tulis tingkat penyedia
Izin dan atribut yang sesuai dijelaskan lebih lanjut secara mendetail di Bagian Menerapkan izin penyedia konten.
-
- Atribut-atribut startup dan kontrol
-
Atribut ini menentukan bagaimana dan kapan sistem Android memulai penyedia,
karakteristik proses dari penyedia, dan
pengaturan runtime lainnya:
-
android:enabled
: flag yang memungkinkan sistem memulai penyedia -
android:exported
: flag yang mengizinkan aplikasi lain menggunakan penyedia ini -
android:initOrder
: urutan memulai penyedia ini, dibandingkan dengan penyedia lain dalam proses yang sama -
android:multiProcess
: flag yang memungkinkan sistem memulai penyedia dalam proses yang sama dengan klien pemanggil -
android:process
: nama proses tempat penyedia menjalankan -
android:syncable
: flag yang menunjukkan bahwa data penyedia akan disinkronkan dengan data di server
Atribut ini didokumentasikan secara lengkap dalam panduan untuk Elemen
<provider>
. -
- Atribut-atribut informatif
-
Ikon dan label opsional untuk penyedia:
-
android:icon
: resource drawable yang berisi ikon untuk penyedia. Ikon muncul di sebelah label penyedia dalam daftar aplikasi di Setelan > Aplikasi > Semua. -
android:label
: label informasi yang mendeskripsikan penyedia, data, atau keduanya. Label muncul dalam daftar aplikasi di Setelan > Aplikasi > Semua.
Atribut ini didokumentasikan secara lengkap dalam panduan untuk Elemen
<provider>
. -
Catatan: Jika Anda menargetkan Android 11 atau yang lebih tinggi, lihat dokumentasi visibilitas paket untuk kebutuhan konfigurasi lebih lanjut.
Intent dan akses data
Aplikasi dapat mengakses penyedia konten secara tidak langsung dengan Intent
.
Aplikasi tidak memanggil metode ContentResolver
atau
ContentProvider
. Sebagai gantinya, ia mengirim intent yang memulai aktivitas,
yang sering kali merupakan bagian
dari aplikasi penyedia itu sendiri. Aktivitas tujuan bertanggung jawab atas
mengambil dan menampilkan data dalam UI-nya.
Bergantung pada tindakan dalam intent, aktivitas tujuan juga dapat meminta pengguna untuk melakukan modifikasi pada data penyedia. Intent juga dapat berisi "tambahan" data yang ditampilkan oleh aktivitas tujuan di UI. Pengguna kemudian memiliki opsi untuk mengubah data ini sebelum menggunakannya untuk mengubah data dalam penyedia.
Anda dapat menggunakan akses intent untuk membantu integritas data. Penyedia Anda mungkin bergantung data disisipkan, diperbarui, dan dihapus sesuai dengan logika bisnis yang didefinisikan dengan ketat. Jika dalam hal ini, membiarkan aplikasi lain secara langsung mengubah data Anda dapat menyebabkan data tidak valid.
Jika Anda ingin developer menggunakan akses intent, pastikan untuk mendokumentasikannya secara saksama. Jelaskan mengapa akses intent yang menggunakan UI aplikasi Anda lebih baik daripada mencoba memodifikasi data dengan kode mereka.
Menangani intent yang masuk yang ingin memodifikasi data penyedia Anda tidak berbeda dengan menangani intent lainnya. Anda bisa mempelajari lebih lanjut tentang penggunaan intent dengan membaca Intent dan Filter Intent.
Untuk informasi terkait lainnya, lihat Ringkasan penyedia kalender.