İçerik sağlayıcı, merkezi bir veri deposuna erişimi yönetir. Bir sağlayıcıyı, manifest dosyasındaki öğelerle birlikte bir Android uygulamasında bir veya daha fazla sınıf olarak uygularsınız. Sınıflarınızdan birinde, sağlayıcınız ile diğer uygulamalar arasındaki arayüz olan ContentProvider
alt sınıfını uyguluyorsunuz.
İçerik sağlayıcıların amacı diğer uygulamaların veri kullanımına sunmak olsa da uygulamanızda kullanıcıların sağlayıcınız tarafından yönetilen verileri sorgulamasına ve değiştirmesine olanak tanıyan etkinlikler bulunabilir.
Bu sayfada, içerik sağlayıcı oluşturmak için temel süreç ve kullanılacak API'lerin listesi bulunmaktadır.
Yapı oluşturmaya başlamadan önce
Sağlayıcı oluşturmaya başlamadan önce aşağıdakileri göz önünde bulundurun:
-
İçerik sağlayıcıya ihtiyacınız olup olmadığına karar verin. Aşağıdaki özelliklerden birini veya daha fazlasını sunmak istiyorsanız bir içerik sağlayıcı oluşturmanız gerekir:
- Karmaşık verileri veya dosyaları diğer uygulamalara sunmak istiyorsanız.
- Kullanıcıların uygulamanızdaki karmaşık verileri diğer uygulamalara kopyalamasına izin vermek istiyorsunuz.
- Arama çerçevesini kullanarak özel arama önerileri sunmak istiyorsunuz.
- Uygulama verilerinizi widget'lara göstermek istiyorsunuz.
AbstractThreadedSyncAdapter
,CursorAdapter
veyaCursorLoader
sınıflarını uygulamak istiyorsunuz.
Kullanım tamamen kendi uygulamanızdaysa ve yukarıda listelenen özelliklerden hiçbirine ihtiyacınız yoksa veritabanlarını veya diğer kalıcı depolama türlerini kullanmak için bir sağlayıcıya ihtiyacınız yoktur. Bunun yerine, Veri ve dosya depolamaya genel bakış başlıklı makalede açıklanan depolama sistemlerinden birini kullanabilirsiniz.
- Henüz yapmadıysanız sağlayıcılar ve nasıl çalıştıkları hakkında daha fazla bilgi edinmek için İçerik sağlayıcı ile ilgili temel bilgileri okuyun.
Ardından, sağlayıcınızı oluşturmak için aşağıdaki adımları uygulayın:
-
Verileriniz için ham depolama alanını tasarlayın. İçerik sağlayıcı, verileri iki şekilde sunar:
- Dosya verileri
- Fotoğraf, ses veya video gibi normalde dosyalara giren veriler. Dosyaları uygulamanızın özel alanında depolayın. Sağlayıcınız, başka bir uygulamadan gelen dosya isteğine yanıt olarak dosya için bir herkese açık kullanıcı adı sunabilir.
- "Yapılandırılmış" veri
- Normalde bir veritabanına, diziye veya benzer bir yapıya giden veriler. Verileri, satır ve sütun tablolarıyla uyumlu bir formda depolayın. Satır, envanterdeki kişi veya öğe gibi bir varlığı temsil eder. Bir sütun, varlıkla ilgili kişi adı veya bir öğenin fiyatı gibi bazı verileri temsil eder. Bu tür verileri depolamak için yaygın olarak kullanılan bir yöntem SQLite veritabanındadır ancak herhangi bir kalıcı depolama türünü kullanabilirsiniz. Android sisteminde kullanılabilen depolama alanı türleri hakkında daha fazla bilgi edinmek için Veri depolamayı tasarlama bölümüne göz atın.
-
ContentProvider
sınıfının ve gerekli yöntemlerinin somut bir uygulamasını tanımlayın. Bu sınıf, verilerinizle 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ının uygulamasının amaçları işlemesini istiyorsanız amaç işlemleri, ekstra veriler ve işaretler de tanımlayın. Ayrıca, verilerinize erişmek isteyen uygulamalar için ihtiyaç duyduğunuz izinleri tanımlayın. Tüm bu değerleri ayrı bir sözleşme sınıfında sabit 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'leri 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.
-
Örnek veri veya sağlayıcı ile bulut tabanlı veriler arasında verileri senkronize edebilen
AbstractThreadedSyncAdapter
uygulaması gibi isteğe bağlı diğer parçaları ekleyin.
Veri depolamayı tasarlayın
İçerik sağlayıcı, verilerin yapılandırılmış bir biçimde kaydedilmesi için kullanılan arayüzdür. Arayüzü oluşturmadan önce verilerin nasıl depolanacağına karar verin. Verileri istediğiniz biçimde depolayabilirsiniz. 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 bir ilişkisel veritabanı veya LevelDB gibi ilişkisel olmayan bir anahtar/değer veri deposu kullanmayı düşünün. Ses, görüntü veya video medyası gibi yapılandırılmamış verilerle çalışıyorsanız bu verileri dosya olarak depolamayı düşünebilirsiniz. Birkaç farklı depolama türünü birlikte kullanabilir ve gerekirse tek bir içerik sağlayıcı kullanarak kullanıma sunabilirsiniz.
-
Android sistemi, Android'in kendi sağlayıcıların tablo tabanlı verileri depolamak için kullandığı SQLite veritabanı API'sine erişim sağlayan Room kalıcılık kitaplığıyla etkileşim kurabilir. Bu kitaplığı kullanarak veritabanı oluşturmak için Odayı kullanarak verileri yerel veritabanına kaydetme bölümünde açıklandığı gibi bir
RoomDatabase
alt sınıfı örneklendirin.Deponuzu uygulamak için veritabanı kullanmanız gerekmez. Sağlayıcı, harici olarak ilişkisel veritabanına benzer şekilde bir tablo grubu olarak görünür ancak bu, sağlayıcının dahili uygulaması için bir gereklilik değildir.
- Android'de dosya verilerini depolamak için çeşitli dosya odaklı API'ler bulunur. Dosya depolama hakkında daha fazla bilgi edinmek 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 verilerini ve dosyaları birleştiren bir sağlayıcı tasarlayabilirsiniz.
- Nadir durumlarda, tek bir uygulama için birden fazla içerik sağlayıcı uygulamadan yararlanabilirsiniz. Örneğin, bir içerik sağlayıcı kullanarak bir widget'la bazı verileri paylaşmak ve diğer uygulamalarla paylaşmak için 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 edip tablo veya dosya biçiminde 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ı artırmanız gerekir. Bu değişikliğin yapılması, uyumlu olmayan bir içerik sağlayıcısına sahip bir uygulamayı yeniden yüklemeyi denediğinde sistemin eski sürüme geçirilmesinin ve sistemin kilitlenmesine neden olmasını önler.
Veri tasarımıyla ilgili dikkat edilmesi gereken noktalar
Aşağıda sağlayıcınızın veri yapısını tasarlamaya yönelik bazı ipuçları 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. Satırı diğer tablolardaki ilgili satırlara bağlamak için bu değeri kullanabilirsiniz ("yabancı anahtar" olarak kullanabilirsiniz). Bu sütun için herhangi bir adı kullanabilirsiniz, ancak sağlayıcı sorgusunun sonuçlarını
ListView
ile bağlamak, alınan sütunlardan birinin_ID
adına sahip olmasını gerektirdiğindenBaseColumns._ID
kullanmak en iyi seçimdir. -
Bit eşlem görüntüleri veya diğer çok büyük dosya odaklı verileri sağlamak istiyorsanız verileri bir dosyada depolayın. Ardından, verileri 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
ContentResolver
dosya yöntemi kullanmaları gerektiğini söylemeniz gerekir. -
Boyutları değişen veya yapısı değişen verileri depolamak için ikili büyük nesne (BLOB) veri türünü kullanın. Örneğin, protokol arabelleği veya JSON yapısı depolamak için BLOB sütunu kullanabilirsiniz.
BLOB'u, şemadan bağımsız bir tablo uygulamak için de kullanabilirsiniz. Bu tablo türünde bir birincil anahtar sütunu, MIME türü sütunu ve bir veya daha fazla genel sütunu BLOB olarak tanımlarsınız. BLOB sütunlarındaki verilerin anlamı, MIME türü sütunundaki değerle gösterilir. Böylece farklı satır türlerini aynı tabloda depolayabilirsiniz. Kişi Sağlayıcı'nın "veri" tablosu
ContactsContract.Data
, şemadan bağımsız bir tablo örneğidir.
İçerik URI'lerini tasarlama
İçerik URI'si, bir sağlayıcıdaki verileri tanımlayan bir URI'dir. İçerik URI'leri arasında tüm sağlayıcının (yetkisi) sembolik adı ile bir tabloya veya dosyaya (yol) işaret eden bir ad bulunur. İsteğe bağlı kimlik bölümü, tablodaki bağımsız bir satırı işaret eder. ContentProvider
işlevinin her 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ıyla ilgili temel bilgiler konusuna bakın.
Bir otorite tasarlayın
Bir sağlayıcının genellikle Android dahili adı olarak işlev gören tek bir yetkilisi vardır. Diğer sağlayıcılarla çakışma olmaması için sağlayıcı yetkinizin temeli olarak internet alan adı sahipliğini (ters yönde) kullanın. Bu öneri Android paket adları için de geçerli olduğundan, sağlayıcı yetkilinizi, 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 tablolara yönlendiren yolları ekleyerek yetkiliden içerik URI'leri oluşturur. Örneğin, table1 ve table2 olmak üzere 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 düzeyi için bir tablo olması gerekmez.
İçerik URI'si kimliklerini işleme
Kurallarına göre, sağlayıcılar URI'nın sonundaki satır için kimlik değerine sahip bir içerik URI'sini kabul ederek tablodaki tek bir satıra erişim sunar. Ayrıca, kurallara göre sağlayıcılar, kimlik değerini tablonun _ID
sütunuyla eşleştirir ve eşleşen satıra göre istenen erişimi gerçekleştirir.
Bu kural, sağlayıcıya erişen uygulamalar için ortak bir tasarım kalıbını basitleştirir. Uygulama, sağlayıcıya karşı bir sorgu yapar ve sonuç olarak elde edilen Cursor
öğesini CursorAdapter
kullanarak ListView
içinde gösterir.
CursorAdapter
tanımı, Cursor
içindeki sütunlardan birinin _ID
olmasını gerektirir
Daha sonra kullanıcı, verileri incelemek veya değiştirmek için kullanıcı arayüzünde görüntülenen satırlardan birini seçer. Uygulama, ilgili satırı ListView
destekleyen Cursor
öğesinden 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ı
Sağlayıcı API'si, gelen içerik URI'si için hangi işlemi gerçekleştireceğinizi seçmenize yardımcı olmak amacıyla içerik URI'si 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 switch
ifadesinde tam sayı değerlerini kullanabilirsiniz.
İçerik URI'si kalıbı, joker karakterler kullanarak içerik URI'lerini eşleştirir:
-
*
, herhangi bir uzunluktaki 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 işlemesini tasarlama ve kodlamaya örnek olarak, tablolara işaret eden aşağıdaki 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ı, eklenmiş bir satır kimliği varsa bu içerik URI'lerini de tanır. Örneğin, table3
içinde 1
ile 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ında bir içerik URI'siyle eşleşir ancaktable1
veyatable3
için içerik URI'leriyle eşleşmez. -
content://com.example.app.provider/table3/#
-
table3
alanındaki tek satırlar için bir içerik URI'siyle 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>
ve tek satırlar için content://<authority>/<path>/<id>
içerik URI kalıbını kullanarak tüm tablonun tamamı için URI'leri tek bir satırdaki URI'lerden farklı şekilde işler.
addURI()
yöntemi, bir yetkiliyi ve yolu tam sayı değeriyle eşler. match()
yöntemi, bir URI için tam sayı değerini döndürür. switch
ifadesi, tüm tabloyu sorgulama ile tek bir kayıt için 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 (ContentUris
), içerik URI'lerinin id
bölümüyle çalışmak için kolaylık yöntemleri sağlar. Uri
ve Uri.Builder
sınıfları, mevcut Uri
nesneleri ayrıştırmak ve yenilerini oluşturmak için kolaylık yöntemleri içerir.
ContentProvider sınıfını uygulama
ContentProvider
örneği, diğer uygulamalardan gelen istekleri işleyerek yapılandırılmış bir veri kümesine erişimi yönetir. Tüm erişim biçimleri sonunda ContentResolver
yöntemini çağırır. Bu çağrı, erişim elde etmek için somut bir ContentProvider
yöntemini çağırır.
Gerekli yöntemler
ContentProvider
soyut sınıfı, soyut 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 bu 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 veri alın. Sorgulanacak tabloyu, döndürülecek satırları, 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 almak için bağımsız değişkenleri kullanın. Yeni eklenen satır için 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ızdaki 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ün. Bu yöntem, İçerik sağlayıcı MIME türlerini uygulama bölümünde daha ayrıntılı olarak açıklanmaktadır.
-
onCreate()
-
Sağlayıcınızı ilk kullanıma hazırlayı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ğıdakileri göz önünde bulundurmanız gerekir:
-
onCreate()
hariç tüm bu yöntemler aynı anda birden çok iş parçacığı tarafından çağrılabilir, bu nedenle iş parçacığı açısından güvenli olmalıdır. Birden fazla iş parçacığı hakkında daha fazla bilgi edinmek için İşlemlere ve ileti dizilerine genel bakış sayfasını inceleyin. -
onCreate()
öğesinde 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 konu 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 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 depolamanız olarak bir SQLite veritabanı kullanıyorsanız SQLiteDatabase
sınıfının query()
yöntemlerinden biri tarafından döndürülen Cursor
değerini döndürebilirsiniz.
Sorgu hiçbir satırla eşleşmezse getCount()
yöntemi 0 değerini 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ştuysa döndürün.
Veri depolamanız olarak SQLite veritabanı kullanmıyorsanız Cursor
somut alt sınıflarından birini kullanın. Örneğin, MatrixCursor
sınıfı, her satırın Object
örnek dizisi olduğu bir imleç uygular. Bu sınıfta yeni bir satır eklemek için addRow()
değerini kullanın.
Android sisteminin, Exception
öğesini işlem sınırları arasında iletebilmesi gerekir. 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 içerik URI'si alırsa bunu göndermeyi 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 sütun için varsayılan bir değer sağlayabilirsiniz.
Bu yöntem, yeni satırın içerik URI'sini döndürür. Bunu oluşturmak için withAppendedId()
kullanarak yeni satırın birincil anahtarını (genellikle _ID
değerini) tablonun içerik URI'sine ekleyin.
delete() yöntemini uygulama
delete()
yönteminin veri depolama alanınızdaki satırları silmesi gerekmez. Sağlayıcınızla senkronizasyon bağdaştırıcısı kullanıyorsanız silinmiş bir satırı tamamen kaldırmak yerine "sil" işaretiyle işaretlemenizi öneririz. Senkronizasyon bağdaştırıcısı, silinmiş satırları kontrol edip sağlayıcıdan silmeden önce bu satırları sunucudan kaldırabilir.
Update() yöntemini uygulama
update()
yöntemi, insert()
tarafından kullanılan aynı ContentValues
bağımsız değişkeninin yanı sıra delete()
ve ContentProvider.query()
tarafından kullanılan aynı selection
ve selectionArgs
bağımsız değişkenlerini alır.
Bu sayede kodu bu yöntemler arasında yeniden kullanabilirsiniz.
onCreate() yöntemini uygulama
Android sistemi, sağlayıcıyı başlattığında onCreate()
adlı kuruluşu çağırır. Bu yöntemde yalnızca hızlı çalışan başlatma görevlerini gerçekleştirin ve sağlayıcı gerçekten veriler için istek alana kadar veritabanı oluşturma ve veri yüklemeyi erteleyin. onCreate()
uygulamasında uzun görevler 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 verdiği yanıtı yavaşlatır.
Aşağıdaki iki snippet, ContentProvider.onCreate()
ile
Room.databaseBuilder()
arasındaki etkileşimi göstermektedir. İlk snippet, veritabanı nesnesinin oluşturulduğu ve veri erişimi nesnelerinin oluşturulduğu yerde ContentProvider.onCreate()
uygulaması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()
- Her 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öntem.
Tablolar için MIME türleri
getType()
yöntemi, içerik URI 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'larıyla ilişkili verilerin türünü döndürün.
Metin, HTML veya JPEG gibi yaygın veri türleri için getType()
, söz konusu verilere yönelik standart MIME türünü döndürür. Bu standart türlerin tam listesini IANA MIME Medya Türleri web sitesinde bulabilirsiniz.
getType()
, bir tablo verisi satırına veya satırlarına işaret eden içerik URI'leri için Android'in tedarikçiye özel MIME biçiminde bir MIME türü döndürür:
-
Parçayı 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>
sizin tarafınızdan sağlanır.<name>
değeri genel olarak,<type>
değeri ise karşılık gelen URI kalıbına özeldir. Şirketinizin adı veya uygulamanızın Android paket adının bir kısmı<name>
için iyi bir seçimdir. URI ile ilişkili tabloyu tanımlayan bir dize,<type>
için iyi bir seçenektir.
Örneğin, bir sağlayıcının yetkilisi com.example.app.provider
ise ve table1
adlı bir tabloyu ortaya çıkarıyorsa table1
içindeki birden fazla satırın MIME türü:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Tek bir table1
satırı için MIME türü:
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, MIME türü filtre bağımsız değişkenine göre filtreleyin. Böylece yalnızca istemcinin işlemek istediği MIME türlerini döndürürsünüz.
Örneğin, fotoğraf resimlerini JPG, PNG ve GIF biçiminde dosya olarak sunan bir sağlayıcı düşünün.
Bir uygulama, "image" (resim) olan bir öğe için image/*
filtre dizesiyle ContentResolver.getStreamTypes()
öğesini ç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()
öğesini çağırabilir ve getStreamTypes()
şunları 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, sütun adlarının vb. gerçek değerlerinde değişiklikler 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 hafızalı adlar içerdiğinden geliştiricilere de yardımcı olur. Böylece, geliştiricilerin sütun adları veya URI'lar için yanlış değerler kullanma ihtimali daha düşüktür. Bu bir sınıf olduğundan Javadoc dokümanlarını içerebilir. Android Studio gibi entegre geliştirme ortamları, sözleşme sınıfındaki sabit adları otomatik olarak tamamlayabilir ve sabit değerler için Javadoc'u görüntüleyebilir.
Geliştiriciler, sözleşme sınıfının sınıf dosyasına uygulamanızdan erişemezler ancak bu dosyayı, sağladığınız bir JAR dosyasından uygulamalarında statik olarak derleyebilirler.
ContactsContract
sınıfı ve iç içe yerleştirilmiş sınıfları, sözleşme sınıflarına örnek olarak verilebilir.
İçerik sağlayıcı izinlerini uygulama
Android sisteminin tüm özellikleriyle ilgili izinler ve erişim, Güvenlik ipuçları bölümünde ayrıntılı olarak açıklanmaktadır. Veri ve dosya depolamaya genel bakış bölümünde, çeşitli depolama türleri için geçerli olan güvenlik ve izinler de 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. - Harici depolama alanına kaydettiğiniz veri dosyaları varsayılan olarak herkese açıktır ve dünya genelinde okunabilir. Diğer uygulamalar harici depolamadaki dosyaları okumak ve yazmak için başka API çağrılarını kullanabileceğinden bu dosyalara erişimi kısıtlamak için içerik sağlayıcı kullanamazsınız.
- Cihazınızın dahili depolama alanında dosya veya SQLite veritabanlarının açılması veya oluşturulması için yapılan yöntem, diğer tüm uygulamalara hem okuma hem de yazma erişimi sağlayabilir. Sağlayıcınızın deposu olarak dahili bir dosya veya veritabanı kullanır ve bu dosyaya "dünyada okunabilir" veya "dünyada yazılabilir" erişim izni verirseniz manifest dosyasında sağlayıcınız için belirlediğiniz izinler, verilerinizi korumaz. Dahili depolama alanındaki dosyalara ve veritabanlarına varsayılan erişim "gizli"dir. Sağlayıcınızın deposu için bu ayarı 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 bulutta (ör. uzak bir sunucu) depolayın, dosyalar ile veritabanlarını uygulamanıza özel tutun.
İzinleri uygulama
Sağlayıcınızın varsayılan olarak ayarlanmış izinleri olmadığı için temel veriler gizli olsa bile tüm uygulamalar, varsayılan olarak sağlayıcınızdan okuma veya yazma işlemi yapabilir. Bunu değiştirmek için
<provider>
öğesinin özelliklerini veya alt öğelerini kullanarak manifest dosyanızda sağlayıcınız için izinleri ayarlayın. Sağlayıcının tamamı, belirli tablolar, belirli kayıtlar veya üçü için de geçerli olacak izinler ayarlayabilirsiniz.
Sağlayıcınızın izinlerini, manifest dosyanızdaki bir veya daha fazla
<permission>
öğesiyle tanımlarsınız. İzni
sağlayıcınıza özel hale getirmek amacıyla
android:name
özelliği için Java tarzı kapsam oluşturma kullanın. Örneğin, okuma iznini com.example.app.provider.permission.READ_PROVIDER
olarak adlandırın.
Aşağıdaki listede, sağlayıcının tamamı için geçerli olan izinlerden başlayarak daha ayrıntılı hale gelen izinlerin kapsamı açıklanmaktadır. Daha ayrıntılı izinler, daha geniş kapsamlı izinlere göre önceliklidir.
- Okuma-yazma sağlayıcısı düzeyinde tek izin
-
Sağlayıcının tamamına hem okuma hem de yazma erişimini kontrol eden bir izin. Bu izin,
<provider>
öğesininandroid:permission
özelliğiyle belirtilir. - Okuma ve yazma sağlayıcı düzeyinde ayrı izinler
-
Sağlayıcının tamamı için okuma ve yazma izni. Bunları,
<provider>
öğesininandroid:readPermission
veandroid:writePermission
özellikleriyle belirtirsiniz. Bunlar,android:permission
tarafından gerekli kılınan 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'yı
<provider>
öğesinin bir<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 izin, sağlayıcı düzeyindeki izinlere göre önceliklidir. - Geçici izin
-
Bir uygulamaya normalde gereken izinlere sahip olmasa bile geçici erişim izni veren izin düzeyi. 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, yalnızca tüm verilerinize sürekli olarak erişen uygulamalar için sağlayıcınızın kalıcı izinlerine ihtiyacı vardır.
Örneğin, bir e-posta sağlayıcısı ve uygulaması uyguluyorsanız ve harici 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, resim görüntüleyiciye fotoğrafın içerik URI'sini ve izin işaretlerini içeren bir intent gönderecek şekilde tasarlayın. Ardından, görüntüleyici, sağlayıcınızın normal okuma iznine sahip olmasa bile fotoğrafı almak için e-posta sağlayıcınıza sorgu gönderebilir.
Geçici izinleri açmak için
<provider>
öğesininandroid:grantUriPermissions
özelliğini ayarlayın veya<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'si desteğini kaldırdığınızdaContext.revokeUriPermission()
çağrısı yapın.Bu özelliğin değeri, sağlayıcınızın ne kadarının erişilebilir olduğunu belirler. Özellik
"true"
olarak ayarlanırsa sistem, sağlayıcınızın tamamına geçici izin vererek sağlayıcı veya yol düzeyindeki izinlerinizin gerektirdiği diğer izinleri geçersiz kılar.Bu işaret
"false"
değerine 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ı belirtir.Bir uygulamaya geçici erişim yetkisi vermek için intent'in
FLAG_GRANT_READ_URI_PERMISSION
işaretini,FLAG_GRANT_WRITE_URI_PERMISSION
işaretini veya her ikisini de içermesi gerekir. Bunlar,setFlags()
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, uygulamanın manifest dosyasında bir ContentProvider
alt sınıfı
<provider>
öğesi kullanılarak tanımlanır. Android sistemi, öğeden aşağıdaki bilgileri alır:
-
Yetkili
(
android:authorities
) - Sistem içindeki tüm sağlayıcıyı tanımlayan sembolik adlar. Bu özellik, İçerik URI'leri tasarlama bölümünde daha ayrıntılı olarak açıklanmaktadır.
-
Sağlayıcı sınıf adı
(
android:name
) -
ContentProvider
uygulayan sınıf. Bu sınıf, ContentProvider sınıfını uygulama bölümünde daha ayrıntılı olarak açıklanmaktadı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
: sağlayıcı genelinde tek okuma/yazma izni -
android:readPermission
: Sağlayıcı genelinde okuma izni -
android:writePermission
: Sağlayıcı genelinde yazma izni
İzinler ve bunlarla ilgili özellikler, İçerik sağlayıcı izinlerini uygulama bölümünde daha ayrıntılı olarak açıklanmaktadır.
-
- Başlangıç ve kontrol özellikleri
-
Bu özellikler, Android sisteminin sağlayıcıyı nasıl ve ne zaman başlatacağı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şaretleyici -
android:exported
: diğer uygulamaların bu sağlayıcıyı kullanmasına izin verdiğini işaretle -
android:initOrder
: aynı süreçteki diğer sağlayıcılara kıyasla bu sağlayıcının başlatılma sırası -
android:multiProcess
: Sistemin, arayan istemciyle aynı işlemde sağlayıcıyı başlatmasına izin veren bir işaret -
android:process
: Sağlayıcının çalıştırıldığı işlemin adıdır. -
android:syncable
: Sağlayıcıya ait verilerin sunucudaki verilerle senkronize edileceğini belirten işaret
Bu özellikler,
<provider>
öğesi kılavuzunda eksiksiz bir şekilde belgelenmiştir. -
- 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. Bu simge, Ayarlar > Uygulamalar > Tümü'ndeki uygulama listesinde sağlayıcı etiketinin yanında görünür. -
android:label
: Sağlayıcıyı, sağlayıcıyı veya her ikisini de açıklayan bir bilgi etiketi. Bu etiket, Ayarlar > Uygulamalar > Tümü'ndeki uygulama listesinde görünür.
Bu özellikler,
<provider>
öğesi kılavuzunda eksiksiz bir şekilde belgelenmiştir. -
Not: Android 11 veya sonraki sürümleri hedefliyorsanız diğer yapılandırma ihtiyaçları için paket görünürlüğü belgelerine göz atın.
Amaçlar ve veri erişimi
Uygulamalar, Intent
ile dolaylı olarak içerik sağlayıcıya erişebilir.
Uygulama, ContentResolver
veya ContentProvider
yöntemlerinin hiçbirini çağırmıyor. Bunun yerine, genellikle sağlayıcının kendi uygulamasının parçası olan bir etkinlik başlatan bir intent gönderir. Hedef etkinlik, kullanıcı arayüzünde verilerin alınması ve görüntülenmesinden sorumludur.
Amaçtaki işleme bağlı olarak, hedef etkinlik de kullanıcıdan sağlayıcının verilerinde değişiklik yapmasını isteyebilir. Amaç, hedef etkinliğin kullanıcı arayüzünde görüntülediği "ekstralar" verileri de içerebilir. Böylece kullanıcı, sağlayıcıdaki verileri değiştirmek için kullanmadan önce bu verileri değiştirme seçeneğine sahiptir.
Veri bütünlüğüne yardımcı olmak için intent erişimini kullanabilirsiniz. Sağlayıcınız, kesin olarak tanımlanmış iş mantığına göre verilerin eklenmesine, güncellenmesine ve silinmesine bağlı olabilir. Bu durumda, diğer uygulamaların verilerinizi doğrudan değiştirmesine izin vermek geçersiz verilere yol açabilir.
Geliştiricilerin amaç erişimini kullanmasını istiyorsanız bunu ayrıntılı bir şekilde belgelediğinizden emin olun. Uygulamanızın kullanıcı arayüzünü kullanarak intent erişiminin verileri kodlarla 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 bir niyetin ele alınması, diğer amaçların işlenmesinden farklı değildir. Amaçlar hakkında daha fazla bilgi edinmek için Niyetler ve Niyet Filtreleri bölümünü okuyabilirsiniz.
Daha fazla bilgi için Takvim sağlayıcısına genel bakış konusuna bakın.