İçerik sağlayıcı, merkezi bir veri havuzuna erişimi yönetir. Sağlayıcıyı, manifest dosyasındaki öğelerle birlikte bir Android uygulamasında bir veya daha fazla sınıf olarak uygulayabilirsiniz. Sınıflarınızdan biri, sağlayıcınız ile diğer uygulamalar arasındaki arayüz olan ContentProvider
alt sınıfını uyguluyor.
İçerik sağlayıcıların amacı, verileri diğer uygulamaların kullanımına sunmak olsa da uygulamanızda kullanıcının sağlayıcınız tarafından yönetilen verileri sorgulamasına ve değiştirmesine olanak tanıyan etkinlikler olabilir.
Bu sayfada, içerik sağlayıcı oluşturma temel süreci ve kullanılacak API'lerin listesi yer alır.
Derlemeye başlamadan önce
Bir sağlayıcı oluşturmaya başlamadan önce aşağıdakileri göz önünde bulundurun:
-
Bir içerik sağlayıcıya ihtiyacınız olup olmadığına karar verin. Aşağıdaki özelliklerden birini veya daha fazlasını sağlamak istiyorsanız içerik sağlayıcı oluşturmanız gerekir:
- Diğer uygulamalara karmaşık veriler veya dosyalar sunmak istiyorsanız.
- Kullanıcıların uygulamanızdaki karmaşık verileri başka uygulamalara kopyalamasına izin vermek istiyorsunuz.
- Arama çerçevesini kullanarak özel arama önerileri sağlamak istiyorsunuz.
- Uygulama verilerinizi widget'lara göstermek istiyorsunuz.
AbstractThreadedSyncAdapter
,CursorAdapter
veyaCursorLoader
sınıflarını uygulamak istiyorsunuz.
Kullanım tamamen kendi uygulamanızın içindeyse ve listelenen özelliklerin hiçbirine ihtiyacınız yoksa veritabanlarını veya diğer kalıcı depolama alanı türlerini kullanmak için sağlayıcıya ihtiyacınız yoktur. Bunun yerine, Veri ve dosya depolamaya genel bakış bölümünde açıklanan depolama sistemlerinden birini kullanabilirsiniz.
- Henüz yapmadıysanız sağlayıcılar ve işleyiş şekilleri hakkında daha fazla bilgi edinmek için İçerik sağlayıcılarla ilgili temel bilgileri okuyun.
Sağlayıcınızı oluşturmak için aşağıdaki adımları uygulayın:
-
Verileriniz için ham depolama tasarlayın. İçerik sağlayıcı, verileri iki şekilde sunar:
- Dosya verileri
- Normalde dosyalara yerleştirilen fotoğraf, ses veya video gibi veriler. Dosyaları uygulamanızın özel alanında depolayın. Başka bir uygulamadan dosya için yapılan isteğe yanıt olarak sağlayıcınız, dosya için bir herkese açık kullanıcı adı sunabilir.
- "Yapılandırılmış" veri
- Normalde bir veritabanına, diziye veya benzer yapıya giden veriler. Verileri, satır ve sütun tablolarıyla uyumlu bir biçimde depolayın. Satır, kişi veya envanterdeki bir öğe gibi bir varlığı temsil eder. Sütun, varlıkla ilgili bazı verileri (ör. kişinin adı veya öğenin fiyatı) temsil eder. Bu tür verileri SQLite veritabanında depolamak için yaygın olarak kullanılan yöntemlerden biridir. Bununla birlikte, dilediğiniz kalıcı depolama türünü de kullanabilirsiniz. Android sisteminde kullanılabilen depolama türleri hakkında daha fazla bilgi edinmek için Veri depolama tasarlama bölümüne bakın.
-
ContentProvider
sınıfının somut bir uygulamasını ve gerekli yöntemlerini tanımlayın. Bu sınıf, verileriniz ile Android sisteminin geri kalanı arasındaki arayüzdür. Bu sınıf hakkında daha fazla bilgi için ContentProvider sınıfını uygulama bölümüne bakın. - Sağlayıcının yetki dizesini, içerik URI'lerini ve sütun adlarını tanımlayın. Sağlayıcı uygulamasının amaçları işlemesini istiyorsanız intent işlemlerini, ekstra verileri ve işaretleri de tanımlayın. Ayrıca, verilerinize erişmek isteyen uygulamalar için gereken izinleri de tanımlayın. Tüm bu değerleri ayrı bir sözleşme sınıfında sabit değerler olarak tanımlayabilirsiniz. Daha sonra, bu sınıfı diğer geliştiricilere sunabilirsiniz. İçerik URI'leri hakkında daha fazla bilgi için İçerik URI'ları tasarlama bölümüne bakın. Amaçlar hakkında daha fazla bilgi için Amaçlar ve veri erişimi bölümüne bakın.
-
Sağlayıcı ile bulut tabanlı veriler arasında veri senkronizasyonu yapabilen örnek veriler veya
AbstractThreadedSyncAdapter
uygulaması gibi isteğe bağlı başka parçalar ekleyin.
Veri depolamayı tasarlayın
İçerik sağlayıcı, yapılandırılmış bir biçimde kaydedilen verilerin arayüzüdür. Arayüzü oluşturmadan önce verilerin nasıl depolanacağına karar verin. Verileri istediğiniz herhangi bir biçimde depolayabilir ve ardından arayüzü, verileri gerektiği gibi okuyup yazacak şekilde tasarlayabilirsiniz.
Android'de kullanılabilen veri depolama teknolojilerinden bazıları şunlardır:
- Yapılandırılmış verilerle çalışıyorsanız SQLite gibi ilişkisel bir veritabanı veya LevelDB gibi ilişkisel olmayan bir anahtar/değer veri deposu olarak kullanabilirsiniz. Ses, görüntü veya video medyası gibi yapılandırılmamış verilerle çalışıyorsanız verileri dosya olarak depolamayı düşünebilirsiniz. Birkaç farklı depolama türünü karıştırıp eşleştirebilir ve gerekirse tek bir içerik sağlayıcı kullanarak bunları kullanıma sunabilirsiniz.
-
Android sistemi, Oda kalıcılık kitaplığı ile etkileşim kurabilir. Bu kitaplık, Android'in kendi sağlayıcılarının tablo tabanlı verileri depolamak için kullandığı SQLite veritabanı API'sine erişim sağlar. Bu kitaplığı kullanarak veritabanı oluşturmak için Odayı kullanarak verileri yerel bir veritabanına kaydetme bölümünde açıklandığı şekilde
RoomDatabase
alt sınıfını örneklendirin.Deponuzu uygulamak için veritabanı kullanmanız gerekmez. Sağlayıcılar, ilişkisel veritabanına benzer şekilde harici olarak bir tablo grubu olarak görünür ancak bu, sağlayıcının dahili uygulaması için bir gereklilik değildir.
- Android'in dosya verilerini depolamak için dosya tabanlı çeşitli API'leri vardır. Dosya depolama hakkında daha fazla bilgi için Veri ve dosya depolamaya genel bakış bölümünü okuyun. Müzik veya video gibi medyayla ilgili veriler sunan bir sağlayıcı tasarlıyorsanız tablo verileri ile dosyaları birleştiren bir sağlayıcınız olabilir.
- Nadir durumlarda tek bir uygulama için birden fazla içerik sağlayıcı uygulamaktan yararlanabilirsiniz. Örneğin, bir içerik sağlayıcısı kullanarak bazı verileri bir widget'la paylaşmak ve diğer uygulamalarla paylaşmak üzere farklı bir veri kümesi göstermek isteyebilirsiniz.
-
Ağ tabanlı verilerle çalışmak için
java.net
veandroid.net
sınıflarını kullanın. Ayrıca ağ tabanlı verileri veritabanı gibi bir yerel veri deposuyla senkronize edebilir ve ardından verileri tablo ya da dosya halinde sunabilirsiniz.
Not: Deponuzda geriye dönük uyumlu olmayan bir değişiklik yaparsanız depoyu yeni bir sürüm numarasıyla işaretlemeniz gerekir. Ayrıca, yeni içerik sağlayıcının kullanıldığı uygulamanızın sürüm numarasını da artırmanız gerekir. Bu değişikliğin yapılması, sistemdeki eski sürüme geçişlerin, uyumlu olmayan içerik sağlayıcısına sahip bir uygulamayı yeniden yüklemeye çalıştığında sistemin kilitlenmesine neden olmasını önler.
Veri tasarımında dikkat edilmesi gereken noktalar
Sağlayıcınızın veri yapısını tasarlamaya yönelik bazı ipuçları aşağıda verilmiştir:
-
Tablo verilerinde her zaman, sağlayıcının her satır için benzersiz bir sayısal değer olarak tuttuğu bir "birincil anahtar" sütunu olmalıdır. Bu değeri, satırı diğer tablolardaki ilgili satırlara bağlamak için kullanabilirsiniz ("yabancı anahtar" olarak). Bu sütun için herhangi bir ad kullanabilirsiniz ancak sağlayıcı sorgusunun sonuçlarının
ListView
ile bağlanması, alınan sütunlardan birinin_ID
adını gerektirdiğinden en iyi seçenekBaseColumns._ID
kullanmaktır. -
Bit eşlem görüntüleri veya dosya odaklı başka çok büyük veri parçaları sağlamak istiyorsanız verileri bir dosyada depolayın ve doğrudan bir tabloda depolamak yerine dolaylı olarak sağlayın. Bunu yaparsanız, sağlayıcınızın kullanıcılarına, verilere erişmek için bir
ContentResolver
dosyası yöntemi kullanmaları gerektiğini bildirmeniz gerekir. -
Boyutu değişiklik gösteren veya yapısı değişen verileri depolamak için ikili büyük nesne (BLOB) veri türünü kullanın. Örneğin, bir protokol arabelleği veya JSON yapısını depolamak için BLOB sütunu kullanabilirsiniz.
Şemadan bağımsız bir tablo uygulamak için BLOB da kullanabilirsiniz. Bu tür tabloda, BLOB olarak birincil anahtar sütunu, MIME türü sütunu ve bir veya daha fazla genel sütun tanımlarsınız. BLOB sütunlarındaki verilerin anlamı, MIME türü sütunundaki değerle belirtilir. Bu sayede, farklı satır türlerini aynı tabloda depolayabilirsiniz. Kişi Sağlayıcı'nın "veri" tablosu
ContactsContract.Data
, şemadan bağımsız bir tabloya örnektir.
İçerik URI'lerini tasarlama
İçerik URI'si, sağlayıcıdaki verileri tanımlayan bir URI'dir. İçerik URI'leri, sağlayıcının tamamının sembolik adını (yetkili) ve bir tabloya veya dosyaya işaret eden bir adı (yol) içerir. İsteğe bağlı kimlik bölümü, bir tablodaki bağımsız bir satırı işaret eder. Her ContentProvider
veri erişimi yöntemi, bağımsız değişken olarak bir içerik URI'sine sahiptir. Bu sayede erişilecek tabloyu, satırı veya dosyayı belirleyebilirsiniz.
İçerik URI'leri hakkında bilgi için İçerik sağlayıcı ile ilgili temel bilgiler konusuna bakın.
Bir otorite tasarlayın
Sağlayıcıların genellikle Android dahili adı olarak işlev gören tek bir yetkilisi vardır. Diğer sağlayıcılarla anlaşmazlık yaşamamak için sağlayıcı yetkilinizin temelinde internet alan adı sahipliğini (bunun tersi) kullanın. Bu öneri Android paket adları için de geçerli olduğundan sağlayıcı yetkinizi, sağlayıcıyı içeren paketin adının bir uzantısı olarak tanımlayabilirsiniz.
Örneğin, Android paketinizin adı com.example.<appname>
ise sağlayıcınıza com.example.<appname>.provider
yetkisi verin.
Yol yapısı tasarlama
Geliştiriciler genellikle bağımsız tabloları gösteren yollar ekleyerek yetkiliden içerik URI'leri oluşturur. Örneğin, table1 (tablo) ve table2 (tablo2) şeklinde iki tablonuz varsa bunları önceki örnekteki yetkiliyle birleştirerek com.example.<appname>.provider/table1
ve com.example.<appname>.provider/table2
içerik URI'lerini elde edebilirsiniz. Yollar tek bir segmentle sınırlı değildir ve yolun her seviyesi için bir tablo olması gerekmez.
İçerik URI'si kimliklerini işleyin
Sağlayıcılar, kural olarak URI'nın sonundaki satır için kimlik değerine sahip içerik URI'sini kabul ederek tablodaki tek bir satıra erişim sunar. Ayrıca kural olarak, sağlayıcılar kimlik değerini tablonun _ID
sütunuyla eşleştirir ve istenen erişimi eşleşen satıra göre gerçekleştirir.
Bu kural, bir sağlayıcıya erişen uygulamalar için ortak bir tasarım kalıbını kolaylaştırır. Uygulama, sağlayıcıya karşı sorgu gerçekleştirir ve sonuç olarak, CursorAdapter
kullanarak ListView
sonucunu veren Cursor
sonucunu gösterir.
CursorAdapter
tanımı, Cursor
içindeki sütunlardan birinin _ID
olmasını gerektirir
Daha sonra kullanıcı, verilere bakmak veya değiştirmek için kullanıcı arayüzünde görüntülenen satırlardan birini seçer. Uygulama, ListView
öğesini destekleyen Cursor
bölümünden ilgili satırı alır, bu satır için _ID
değerini alır, bunu içerik URI'sine ekler ve erişim isteğini sağlayıcıya gönderir. Daha sonra sağlayıcı, kullanıcının seçtiği satıra göre sorguyu veya değişikliği yapabilir.
İçerik URI'si kalıpları
provider API, gelen içerik URI'si için hangi işlemi gerçekleştireceğinizi seçmenize yardımcı olmak için içerik URI kalıplarını tam sayı değerleriyle eşleyen UriMatcher
kolaylık sınıfını içerir. Belirli bir kalıpla eşleşen içerik URI'si veya URI'lar için istenen işlemi seçen bir switch
ifadesinde tam sayı değerlerini kullanabilirsiniz.
İçerik URI'si modeli, joker karakterler kullanarak içerik URI'lerini eşleştirir:
-
*
, uzunluğu ne olursa olsun geçerli herhangi bir karakterden oluşan bir dizeyle eşleşir. -
#
, herhangi bir uzunluktaki sayısal karakterlerden oluşan bir dizeyle eşleşir.
İçerik URI'si işlemesini tasarlama ve kodlamaya örnek olarak, aşağıdaki tablolara işaret eden içerik URI'lerini tanıyan com.example.app.provider
yetkisine sahip bir sağlayıcıyı düşünün:
-
content://com.example.app.provider/table1
:table1
adlı bir tablo. -
content://com.example.app.provider/table2/dataset1
:dataset1
adlı bir tablo. -
content://com.example.app.provider/table2/dataset2
:dataset2
adlı bir tablo. -
content://com.example.app.provider/table3
:table3
adlı bir tablo.
Sağlayıcı, bu içerik URI'lerini, kendilerine satır kimlikleri eklenmişse de tanır. Örneğin, table3
içinde 1
tarafından tanımlanan satır için content://com.example.app.provider/table3/1
.
Aşağıdaki içerik URI'si kalıpları mümkündür:
-
content://com.example.app.provider/*
- Sağlayıcıdaki herhangi bir içerik URI'siyle eşleşir.
-
content://com.example.app.provider/table2/*
-
dataset1
vedataset2
tabloları için bir içerik URI'si ile eşleşir ancaktable1
veyatable3
için içerik URI'leriyle eşleşmez. -
content://com.example.app.provider/table3/#
-
table3
içindeki tek satırlar için bir içerik URI'sıyla eşleşir (örneğin,6
ile tanımlanan satır içincontent://com.example.app.provider/table3/6
).
Aşağıdaki kod snippet'i, UriMatcher
içindeki yöntemlerin nasıl çalıştığını gösterir.
Bu kod, tablolar için content://<authority>/<path>
içerik URI kalıbı ve tek satırlar için content://<authority>/<path>/<id>
kullanılarak tüm tablonun URI'lerini tek bir satırdaki URI'lardan farklı şekilde işler.
addURI()
yöntemi, bir yetkiliyi ve yolu bir tam sayı değeriyle eşler. match()
yöntemi, bir URI için tam sayı değerini döndürür. switch
ifadesi, tablonun tamamını sorgulama veya tek bir kaydı sorgulama arasında seçim yapar.
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 }
Başka bir sınıf olan ContentUris
, içerik URI'lerinin id
kısmıyla çalışmak için pratik yöntemler sunar. Uri
ve Uri.Builder
sınıfları, mevcut Uri
nesnelerini ayrıştırma ve yenilerini oluşturmak için kolay yöntemler içerir.
ContentProvider sınıfını uygulama
ContentProvider
örneği, diğer uygulamalardan gelen istekleri işleyerek yapılandırılmış bir veri grubuna erişimi yönetir. Tüm erişim biçimleri sonunda ContentResolver
yöntemini çağırır ve daha sonra, erişim için somut bir ContentProvider
yöntemi çağrılır.
Gerekli yöntemler
ContentProvider
soyut sınıfı, somut alt sınıfınızın bir parçası olarak uyguladığınız altı soyut yöntemi tanımlar. onCreate()
dışındaki tüm yöntemler, içerik sağlayıcınıza erişmeye çalışan bir istemci uygulaması tarafından çağrılır.
-
query()
-
Sağlayıcınızdan verileri alın. Sorgulanacak tabloyu, döndürülecek satırlarla sütunları ve sonucun sıralama düzenini seçmek için bağımsız değişkenleri kullanın.
Verileri
Cursor
nesnesi olarak döndürün. -
insert()
- Sağlayıcınıza yeni bir satır ekleyin. Hedef tabloyu seçmek ve kullanılacak sütun değerlerini öğrenmek için bağımsız değişkenleri kullanın. Yeni eklenen satır için bir içerik URI'si döndürün.
-
update()
- Sağlayıcınızdaki mevcut satırları güncelleyin. Güncellenecek tabloyu ve satırları seçmek ve güncellenmiş sütun değerlerini almak için bağımsız değişkenleri kullanın. Güncellenen satır sayısını döndürür.
-
delete()
- Sağlayıcınızdan satırları silin. Tabloyu ve silinecek satırları seçmek için bağımsız değişkenleri kullanın. Silinen satır sayısını döndürür.
-
getType()
- İçerik URI'sine karşılık gelen MIME türünü döndürür. Bu yöntem, İçerik sağlayıcı MIME türlerini uygulama bölümünde daha ayrıntılı olarak açıklanmıştır.
-
onCreate()
-
Sağlayıcınızı başlatın. Android sistemi, sağlayıcınızı oluşturduktan hemen sonra bu yöntemi çağırır. Bir
ContentResolver
nesnesi erişmeye çalışana kadar sağlayıcınız oluşturulmaz.
Bu yöntemler, aynı şekilde adlandırılmış ContentResolver
yöntemleriyle aynı imzaya sahiptir.
Bu yöntemleri uygularken aşağıdaki noktaları göz önünde bulundurmanız gerekir:
-
onCreate()
dışındaki tüm yöntemler, aynı anda birden çok iş parçacığı tarafından çağrılabileceğinden iş parçacığı için güvenli olmaları gerekir. Birden çok iş parçacığı hakkında daha fazla bilgi edinmek için İşlemlere ve iş parçacıklarına genel bakış sayfasını inceleyin. -
onCreate()
bölgesinde uzun işlemler yapmaktan kaçının. Başlatma görevlerini, gerçekten ihtiyaç duyulana kadar erteleyin. onCreate() yöntemini uygulama ile ilgili bölümde, bu daha ayrıntılı olarak açıklanmaktadır. -
Bu yöntemleri uygulamanız gerekse de, kodunuzun beklenen veri türünü döndürmek dışında hiçbir şey yapması gerekmez. Örneğin,
insert()
çağrısını yoksayıp 0 değerini döndürerek diğer uygulamaların bazı tablolara veri eklemesini önleyebilirsiniz.
query() yöntemini uygulama
ContentProvider.query()
yöntemi, Cursor
nesnesi döndürmeli veya başarısız olursa Exception
hatası vermelidir. Veri depolama alanınız olarak SQLite veritabanı kullanıyorsanız SQLiteDatabase
sınıfının query()
yöntemlerinden biri tarafından döndürülen Cursor
alanını döndürebilirsiniz.
Sorgu hiçbir satırla eşleşmezse getCount()
yöntemi 0 döndüren bir Cursor
örneği döndürün.
null
değerini yalnızca sorgu işlemi sırasında dahili bir hata oluşursa döndürün.
Veri depolama alanınız olarak SQLite veritabanı kullanmıyorsanız somut Cursor
alt sınıflarından birini kullanın. Örneğin, MatrixCursor
sınıfı, her satırın Object
örnekten oluşan bir dizi olduğu bir imleç uygular. Bu sınıfta, yeni satır eklemek için addRow()
kısayolunu kullanın.
Android sistemi, Exception
öğesini işlem sınırları arasında iletebilmelidir. Android bunu, sorgu hatalarının işlenmesinde yararlı olan aşağıdaki istisnalar için yapabilir:
-
IllegalArgumentException
. Sağlayıcınız geçersiz bir içerik URI'sı alırsa bunu bildirmeyi seçebilirsiniz. -
NullPointerException
insert() yöntemini uygulama
insert()
yöntemi, ContentValues
bağımsız değişkenindeki değerleri kullanarak uygun tabloya yeni bir satır ekler. Bir sütun adı ContentValues
bağımsız değişkeninde yoksa sağlayıcı kodunuzda veya veritabanı şemanızda bu sütun için varsayılan bir değer sağlamak isteyebilirsiniz.
Bu yöntem, yeni satır için içerik URI'sini döndürür. Bunu oluşturmak için yeni satırın birincil anahtarını (genellikle _ID
değerini) tablonun içerik URI'sine withAppendedId()
kullanarak ekleyin.
delete() yöntemini uygulama
delete()
yönteminin, veri depolama alanınızdan satır silmesi gerekmez. Sağlayıcınızla bir senkronizasyon bağdaştırıcısı kullanıyorsanız satırı tamamen kaldırmak yerine silinmiş bir satırı "sil" işaretiyle işaretlemeyi düşünebilirsiniz. Senkronizasyon bağdaştırıcısı, silinen satırları kontrol edebilir ve sağlayıcıdan silmeden önce bunları sunucudan kaldırabilir.
Update() yöntemini uygulama
update()
yöntemi, insert()
tarafından kullanılan aynı ContentValues
bağımsız değişkenini ve delete()
ile ContentProvider.query()
tarafından kullanılan aynı selection
ve selectionArgs
bağımsız değişkenlerini alır.
Böylece, bu yöntemler arasında kodu yeniden kullanabilirsiniz.
onCreate() yöntemini uygulama
Android sistemi, sağlayıcıyı başlattığında
onCreate()
çağırır. Bu yöntemde yalnızca hızlı çalışan başlatma görevlerini gerçekleştirin ve sağlayıcı veriler için gerçekten bir istek alana kadar veritabanı oluşturma ile veri yüklemesini erteleyin. onCreate()
ürününde uzun işlemler yaparsanız sağlayıcınızın başlatılmasını yavaşlatırsınız. Bu da sağlayıcının diğer uygulamalara yanıtını yavaşlatır.
Aşağıdaki iki snippet, ContentProvider.onCreate()
ile
Room.databaseBuilder()
arasındaki etkileşimi göstermektedir. İlk snippet, veritabanı nesnesinin derlendiği ve veri erişimi nesnelerini işlediği durumlarda ContentProvider.onCreate()
kullanımını gösterir:
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. } }
ContentProvider MIME türlerini uygulama
ContentProvider
sınıfının, MIME türlerini döndürmek için iki yöntemi vardır:
-
getType()
- Herhangi bir sağlayıcı için uyguladığınız gerekli yöntemlerden biri.
-
getStreamTypes()
- Sağlayıcınız dosya sunuyorsa uygulamanız beklenen bir yöntemdir.
Tablolar için MIME türleri
getType()
yöntemi, içerik URI'si bağımsız değişkeni tarafından döndürülen veri türünü açıklayan MIME biçiminde bir String
döndürür. Uri
bağımsız değişkeni, belirli bir URI yerine bir kalıp olabilir.
Bu durumda, kalıpla eşleşen içerik URI'leriyle ilişkili veri türünü döndürün.
getType()
; metin, HTML veya JPEG gibi yaygın veri türleri için söz konusu verilerin standart MIME türünü döndürür. Bu standart türlerin tam listesini IANA MIME Medya Türleri web sitesinde bulabilirsiniz.
Tablo verilerinin bir satırını veya satırlarını işaret eden içerik URI'leri için getType()
, Android'in tedarikçi firmaya özel MIME biçiminde bir MIME türü döndürür:
-
Bölümü yazın:
vnd
-
Alt tür bölümü:
-
URI kalıbı tek bir satır içinse:
android.cursor.item/
-
URI kalıbı birden fazla satır içinse:
android.cursor.dir/
-
URI kalıbı tek bir satır içinse:
-
Sağlayıcıya özel bölüm:
vnd.<name>
.<type>
<name>
ve<type>
tedarikçilerini siz sağlarsınız.<name>
değeri genel olarak benzersizdir ve<type>
değeri, karşılık gelen URI kalıbında benzersizdir. Şirketinizin adı veya uygulamanızın Android paketi adının bir kısmı<name>
için iyi bir seçimdir.<type>
için iyi bir seçim, URI ile ilişkili tabloyu tanımlayan bir dizedir.
Örneğin, sağlayıcının yetkisi com.example.app.provider
ise ve table1
adlı bir tabloyu gösterirse table1
içindeki birden fazla satır için MIME türü şu olur:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Tek bir table1
satırı için MIME türü şu şekildedir:
vnd.android.cursor.item/vnd.com.example.provider.table1
Dosyalar için MIME türleri
Sağlayıcınız dosya sunuyorsa getStreamTypes()
öğesini uygulayın.
Yöntem, sağlayıcınızın belirli bir içerik URI'si için döndürebileceği dosyalar için bir String
MIME türü dizisi döndürür. Sunduğunuz MIME türlerini, yalnızca istemcinin işlemek istediği MIME türlerini döndürebilmek için MIME türü filtre bağımsız değişkenine göre filtreleyin.
Örneğin, fotoğraf resimlerini JPG, PNG ve GIF biçimindeki dosya olarak sunan bir sağlayıcıyı düşünün.
Bir uygulama, "resim" olan bir şey için image/*
filtre dizesiyle ContentResolver.getStreamTypes()
yöntemini çağırırsa ContentProvider.getStreamTypes()
yöntemi diziyi döndürür:
{ "image/jpeg", "image/png", "image/gif"}
Uygulama yalnızca JPG dosyalarıyla ilgileniyorsa *\/jpeg
filtre dizesiyle ContentResolver.getStreamTypes()
yöntemini çağırabilir ve getStreamTypes()
şu değeri döndürür:
{"image/jpeg"}
Sağlayıcınız filtre dizesinde istenen MIME türlerinden herhangi birini sunmuyorsa getStreamTypes()
, null
değerini döndürür.
Sözleşme sınıfı uygulama
Sözleşme sınıfı URI'lar, sütun adları, MIME türleri ve sağlayıcıyla ilgili diğer meta veriler için sabit tanımlar içeren bir public final
sınıfıdır. Bu sınıf, URI'ların gerçek değerlerinde ve sütun adlarında değişiklik olsa bile sağlayıcıya doğru şekilde erişilebilmesini sağlayarak sağlayıcı ile diğer uygulamalar arasında bir sözleşme oluşturur.
Sözleşme sınıfı, genellikle sabit değerleri için anımsatıcı adlara sahip olduğundan geliştiricilere yardımcı olur. Bu sayede, geliştiricilerin sütun adları veya URI'ler için yanlış değerler kullanma olasılığı daha düşüktür. Sınıf olduğu için Javadoc belgelerini içerebilir. Android Studio gibi entegre geliştirme ortamları, sözleşme sınıfındaki sabit adları otomatik olarak tamamlayabilir ve sabitler için Javadoc'u görüntüleyebilir.
Geliştiriciler, uygulamanızdan sözleşme sınıfı sınıf dosyasına erişemez ancak dosyayı sağladığınız bir JAR dosyasından statik olarak kendi uygulamalarında derleyebilirler.
ContactsContract
sınıfı ve iç içe yerleştirilmiş sınıfları, sözleşmeli sınıflara örnek olarak verilebilir.
İçerik sağlayıcı izinlerini uygulama
Android sisteminin tüm özelliklerine ilişkin izinler ve erişim izinleri Güvenlik ipuçları bölümünde ayrıntılı olarak açıklanmıştır. Veri ve dosya depolamaya genel bakış bölümünde de çeşitli depolama türleri için geçerli olan güvenlik ve izinler açıklanmaktadır. Özetle, önemli noktalar şunlardır:
- Varsayılan olarak, cihazın dahili depolama alanında depolanan veri dosyaları, uygulamanıza ve sağlayıcınıza özeldir.
-
Oluşturduğunuz
SQLiteDatabase
veritabanları, uygulamanıza ve sağlayıcınıza özeldir. - Varsayılan olarak, harici depolamaya kaydettiğiniz veri dosyaları herkese açık ve herkes tarafından okunabilir olur. Diğer uygulamalar dosyaları okumak ve yazmak için başka API çağrılarını kullanabileceğinden, harici depolama alanındaki dosyalara erişimi kısıtlamak için içerik sağlayıcı kullanamazsınız.
- Bu yöntem, cihazınızın dahili depolama alanındaki dosyaları veya SQLite veritabanlarını açmak veya oluşturmak için yapılan çağrıların diğer tüm uygulamalara hem okuma hem de yazma erişimi verebilir. Sağlayıcınızın deposu olarak dahili bir dosya veya veritabanı kullanıyorsanız ve ona "dünya tarafından okunabilir" ya da "dünyada yazılabilir" erişim verirseniz sağlayıcınız için manifest dosyasında ayarladığınız izinler verilerinizi korumaz. Dahili depolama alanındaki dosyalar ve veritabanları için varsayılan erişim "gizli"dir. Sağlayıcınızın deposu için bunu değiştirmeyin.
Verilerinize erişimi kontrol etmek için içerik sağlayıcı izinlerini kullanmak istiyorsanız verilerinizi dahili dosyalarda, SQLite veritabanlarında veya uzak bir sunucu gibi bulutta depolayın, dosyalar ve veritabanlarını uygulamanız için gizli tutun.
İzinleri uygulama
Sağlayıcınızın varsayılan olarak ayarlanmış izinleri olmaması nedeniyle, temel veriler gizli olsa bile varsayılan olarak tüm uygulamalar sağlayıcınızdan okuma yapabilir veya sağlayıcınıza veri yazabilir. Bunu değiştirmek için özellikleri veya
<provider>
öğesinin alt öğelerini kullanarak manifest dosyanızda sağlayıcınızın izinlerini ayarlayın. Sağlayıcının tamamı, belirli tablolar, belirli kayıtlar veya üçü için de geçerli olacak izinler belirleyebilirsiniz.
Sağlayıcınızın izinlerini, manifest dosyanızda bir veya daha fazla
<permission>
öğesiyle tanımlarsınız. İzni sağlayıcınıza özgü hâle getirmek amacıyla
android:name
özelliği için Java tarzı kapsam oluşturma seçeneğini kullanın. Örneğin, okuma iznini com.example.app.provider.permission.READ_PROVIDER
olarak adlandırın.
Aşağıdaki listede, tüm sağlayıcı için geçerli olan izinlerden başlayarak daha ayrıntılı hale gelen sağlayıcı izinlerinin kapsamı açıklanmaktadır. Daha ayrıntılı izinler, daha geniş kapsamlı izinlere göre önceliklidir.
- Sağlayıcı düzeyinde tek bir okuma-yazma izni
-
<provider>
öğesininandroid:permission
özelliğiyle belirtilen, sağlayıcının tamamına okuma ve yazma erişimini kontrol eden bir izin. - Sağlayıcı düzeyinde ayrı okuma ve yazma izinleri
-
Tüm sağlayıcı için bir okuma ve yazma izni. Bunları
<provider>
öğesininandroid:readPermission
veandroid:writePermission
özellikleriyle belirtirsiniz.android:permission
özelliğinin gerektirdiği izne göre önceliklidir. - Yol düzeyinde izin
-
Sağlayıcınızdaki içerik URI'si için okuma, yazma veya okuma/yazma izni. Kontrol etmek istediğiniz her URI'yi,
<provider>
öğesinin<path-permission>
alt öğesiyle belirtirsiniz. Belirttiğiniz her içerik URI'si için bir okuma/yazma izni, bir okuma izni, bir yazma izni veya üçünü birden belirtebilirsiniz. Okuma ve yazma izinleri, okuma/yazma iznine göre önceliklidir. Ayrıca yol düzeyindeki izinler, sağlayıcı düzeyindeki izinlere göre önceliklidir. - Geçici izin
-
Uygulama normalde gerekli olan izinlere sahip olmasa bile bir uygulamaya geçici erişim sağlayan izin düzeyidir. Geçici erişim özelliği, bir uygulamanın manifest dosyasında istemesi gereken izin sayısını azaltır. Geçici izinleri etkinleştirdiğinizde sağlayıcınız için yalnızca kalıcı izinlere ihtiyacı olan uygulamalar, tüm verilerinize sürekli olarak erişen uygulamalardır.
Örneğin, bir e-posta sağlayıcı ile uygulama kuruyorsanız ve dışarıdan bir resim görüntüleyici uygulamasının sağlayıcınızdan gelen fotoğraf eklerini görüntülemesine izin vermek istiyorsanız ihtiyacınız olan izinleri göz önünde bulundurun. Resim görüntüleyiciye izin gerektirmeden gerekli erişimi vermek için fotoğrafların içerik URI'lerine yönelik geçici izinler ayarlayabilirsiniz.
E-posta uygulamanızı, kullanıcı bir fotoğraf görüntülemek istediğinde uygulama, fotoğrafın içerik URI'sini ve izin bayraklarını içeren bir intent'i resim görüntüleyiciye gönderecek şekilde tasarlayın. Ardından resim görüntüleyici, sağlayıcınız için normal okuma iznine sahip olmasa bile fotoğrafı almak için e-posta sağlayıcınızı sorgulayabilir.
Geçici izinleri açmak için
<provider>
öğesininandroid:grantUriPermissions
özelliğini ayarlayın ya da<provider>
öğenize bir veya daha fazla<grant-uri-permission>
alt öğesi ekleyin. Sağlayıcınızdan geçici bir izinle ilişkili içerik URI'sine yönelik desteği kaldırdığınızdaContext.revokeUriPermission()
yöntemini çağırın.Özelliğin değeri, sağlayıcınızın ne kadarının erişilebilir olacağını belirler. Özellik
"true"
olarak ayarlanırsa sistem, sağlayıcı düzeyindeki veya yol düzeyindeki izinlerin gerektirdiği diğer tüm izinleri geçersiz kılarak tüm sağlayıcınıza geçici izin verir.Bu işaret
"false"
olarak ayarlanırsa<provider>
öğenize<grant-uri-permission>
alt öğeleri ekleyin. Her alt öğe, geçici erişim izni verilen içerik URI'sini veya URI'larını belirtir.Bir uygulamaya geçici erişim yetkisi verebilmek için niyet;
FLAG_GRANT_READ_URI_PERMISSION
işaretini,FLAG_GRANT_WRITE_URI_PERMISSION
işaretini veya her ikisini birden içermelidir. BunlarsetFlags()
yöntemiyle ayarlanır.android:grantUriPermissions
özelliği yoksa"false"
olduğu varsayılır.
<provider> öğesi
Activity
ve Service
bileşenlerinde olduğu gibi, ContentProvider
alt sınıfı da
<provider>
öğesi kullanılarak uygulamasının manifest dosyasında tanımlanır. Android sistemi, öğeden aşağıdaki bilgileri alır:
-
Yetki
(
android:authorities
) - Sistem içinde tüm sağlayıcıyı tanımlayan sembolik adlar. Bu özellik, İçerik URI'lerini tasarlama bölümünde daha ayrıntılı olarak açıklanmıştır.
-
Sağlayıcı sınıfı adı
(
android:name
) -
ContentProvider
yöntemini uygulayan sınıf. Bu sınıf, ContentProvider sınıfını uygulama bölümünde daha ayrıntılı olarak açıklanmıştır. - İzinler
-
Sağlayıcının verilerine erişmek için diğer uygulamaların sahip olması gereken izinleri belirten özellikler:
-
android:grantUriPermissions
: geçici izin işareti -
android:permission
: tek sağlayıcı genelinde okuma/yazma izni -
android:readPermission
: sağlayıcı genelinde okuma izni -
android:writePermission
: sağlayıcı genelinde yazma izni
İzinler ve bunlarla ilişkili özellikler, İçerik sağlayıcı izinlerini uygulama bölümünde daha ayrıntılı olarak açıklanmıştır.
-
- Başlangıç ve kontrol özellikleri
-
Bu özellikler, Android sisteminin sağlayıcıyı nasıl ve ne zaman başlattığını, sağlayıcının işlem özelliklerini ve diğer çalışma zamanı ayarlarını belirler:
-
android:enabled
: Sistemin sağlayıcıyı başlatmasına izin veren işaret -
android:exported
: Diğer uygulamaların bu sağlayıcıyı kullanmasına izin vermeyi işaretle -
android:initOrder
: bu sağlayıcının başlatıldığı sıra (aynı işlemdeki diğer sağlayıcılara göre) -
android:multiProcess
: Sistemin, sağlayıcıyı çağıran istemciyle aynı işlemde başlatmasını sağlayan işaret -
android:process
: sağlayıcının çalıştırıldığı sürecin adı -
android:syncable
: Sağlayıcının verilerinin bir sunucudaki verilerle senkronize edileceğini belirten işaret
Bu özellikler,
<provider>
öğesiyle ilgili kılavuzda eksiksiz bir şekilde açıklanmıştır. -
- Bilgilendirici özellikler
-
Sağlayıcı için isteğe bağlı bir simge ve etiket:
-
android:icon
: Sağlayıcının simgesini içeren çekilebilir bir kaynaktır. Simge, Ayarlar > Uygulamalar > Tümü bölümündeki uygulama listesinde sağlayıcının etiketinin yanında görünür. -
android:label
: Sağlayıcıyı, verilerini veya her ikisini birden açıklayan bir bilgi etiketidir. Etiket, Ayarlar > Uygulamalar > Tümü bölümündeki uygulama listesinde görünür.
Bu özellikler,
<provider>
öğesiyle ilgili kılavuzda eksiksiz bir şekilde açıklanmıştır. -
Amaçlar ve veri erişimi
Uygulamalar bir içerik sağlayıcıya Intent
ile dolaylı olarak erişebilir.
Uygulama, ContentResolver
veya ContentProvider
yöntemlerinin hiçbirini çağırmaz. Bunun yerine, genellikle sağlayıcının kendi uygulamasında yer alan bir etkinlik başlatan bir amaç gönderir. Hedef etkinliği, verileri alıp kullanıcı arayüzünde görüntülemekten sorumludur.
Amaçtaki işleme bağlı olarak, hedef etkinlik kullanıcıdan sağlayıcının verilerinde değişiklik yapmasını da isteyebilir. Bir amaç, hedef etkinliğin kullanıcı arayüzünde görüntülediği "ekstralar" verilerini de içerebilir. Böylece kullanıcı, sağlayıcıdaki verilerde değişiklik yapmak için kullanmadan önce bu verileri değiştirebilir.
Veri bütünlüğüne yardımcı olmak için intent erişimini kullanabilirsiniz. Sağlayıcınız verilerin kesin şekilde tanımlanmış iş mantığına göre eklenmesini, güncellenmesini ve silinmesini isteyebilir. Bu durumda, diğer uygulamaların verilerinizi doğrudan değiştirmesine izin vermek geçersiz verilere neden olabilir.
Geliştiricilerin intent erişimi kullanmasını istiyorsanız bunu kapsamlı bir şekilde belgelediğinizden emin olun. Uygulamanızın kullanıcı arayüzünü kullanarak intent erişiminin, verileri kodlarıyla değiştirmeye çalışmaktan neden daha iyi olduğunu açıklayın.
Sağlayıcınızın verilerini değiştirmek isteyen gelen amaçların işlenmesi, diğer amaçların işlenmesinden farklı değildir. Amaçları kullanma hakkında daha fazla bilgi edinmek için Amaçlar ve Amaç Filtreleri bölümünü okuyabilirsiniz.
İlgili daha fazla bilgi için Takvim sağlayıcısına genel bakış konusuna bakın.