İçerik sağlayıcı ile ilgili temel bilgiler

İçerik sağlayıcı, merkezi bir veri havuzuna erişimi yönetir. Sağlayıcı, verilerle çalışmak için genellikle kendi kullanıcı arayüzünü sağlayan Android uygulamasının bir parçasıdır. Bununla birlikte, içerik sağlayıcılar öncelikli olarak sağlayıcıya bir sağlayıcı istemci nesnesi kullanarak erişen diğer uygulamalar tarafından kullanılır. Sağlayıcılar ve sağlayıcı istemcileri, işlemler arası iletişimi ve güvenli veri erişimini de işleyen tutarlı ve standart bir veri arayüzü sunar.

İçerik sağlayıcılarla genellikle şu iki senaryodan birinde çalışırsınız: Başka bir uygulamada mevcut içerik sağlayıcıya erişmek için kod uygulama veya uygulamanızda diğer uygulamalarla veri paylaşmak için yeni içerik sağlayıcı oluşturma.

Bu sayfa, mevcut içerik sağlayıcılarla çalışmaya ilişkin temel bilgileri içerir. İçerik sağlayıcıları kendi uygulamalarınızda kullanma hakkında bilgi edinmek için İçerik sağlayıcı oluşturma konusuna bakın.

Bu bölümde aşağıdaki konular açıklanmaktadır:

  • İçerik sağlayıcıların işleyiş şekli.
  • Bir içerik sağlayıcıdan veri almak için kullandığınız API.
  • İçerik sağlayıcıda veri eklemek, güncellemek veya silmek için kullandığınız API.
  • Sağlayıcılarla çalışmayı kolaylaştıran diğer API özellikleri.

Genel bakış

İçerik sağlayıcı, verileri ilişkisel veritabanında bulunan tablolara benzeyen en az bir tablo olarak harici uygulamalara sunar. Satır, sağlayıcının topladığı bir veri türünün örneğini temsil eder; satırdaki her sütun ise bir örnek için toplanan bağımsız veri parçasını temsil eder.

İçerik sağlayıcı, çeşitli API'ler ve bileşenler için uygulamanızdaki veri depolama katmanına erişimi koordine eder. Şekil 1'de gösterildiği gibi, bunlar aşağıdakileri içerir:

  • Uygulama verilerinize erişimi diğer uygulamalarla paylaşma
  • Widget'a veri gönderme
  • SearchRecentSuggestionsProvider ile arama çerçevesi üzerinden uygulamanız için özel arama önerileri döndürme
  • AbstractThreadedSyncAdapter kullanarak uygulama verilerini sunucunuzla senkronize etme
  • CursorLoader kullanarak kullanıcı arayüzünüze veri yükleme
İçerik sağlayıcı ile diğer bileşenler arasındaki ilişki.

Şekil 1. İçerik sağlayıcı ile diğer bileşenler arasındaki ilişki.

Bir sağlayıcıya erişme

Bir içerik sağlayıcıdaki verilere erişmek istediğinizde, sağlayıcıyla istemci olarak iletişim kurmak için uygulamanızın Context öğesindeki ContentResolver nesnesini kullanırsınız. ContentResolver nesnesi, ContentProvider uygulayan bir sınıfın bir örneği olan sağlayıcı nesnesiyle iletişim kurar.

Sağlayıcı nesnesi istemcilerden veri istekleri alır, istenen işlemi gerçekleştirir ve sonuçları döndürür. Bu nesne, sağlayıcı nesnesinde aynı şekilde adlandırılmış yöntemleri çağıran yöntemlere sahiptir. Bu yöntemler, ContentProvider öğesinin somut alt sınıflarından birinin örneğidir. ContentResolver yöntemleri, kalıcı depolama alanının temel "CRUD" (oluşturma, alma, güncelleme ve silme) işlevlerini sağlar.

Kullanıcı arayüzünüzden bir ContentProvider öğesine erişmek için yaygın olarak kullanılan bir kalıp, arka planda eşzamansız sorgu çalıştırmak için CursorLoader kullanır. Kullanıcı arayüzünüzdeki Activity veya Fragment, sorguya bir CursorLoader çağırır ve bu da ContentResolver ile ContentProvider kodunu alır.

Bu, sorgu çalışırken kullanıcı arayüzünün kullanılabilir olmaya devam etmesini sağlar. Bu kalıp, Şekil 2'de gösterildiği gibi temel depolama mekanizmasının yanı sıra bir dizi farklı nesnenin etkileşimini içerir.

ContentProvider, diğer sınıflar ve depolama alanı arasındaki etkileşim.

2. Şekil. ContentProvider, diğer sınıflar ve depolama alanı arasındaki etkileşim.

Not: Bir sağlayıcıya erişmek için uygulamanızın genellikle manifest dosyasında belirli izinleri istemesi gerekir. Bu geliştirme kalıbı, İçerik sağlayıcı izinleri bölümünde daha ayrıntılı olarak açıklanmıştır.

Android platformundaki yerleşik sağlayıcılardan biri, kullanıcının saklamak istediği standart olmayan kelimeleri depolayan Kullanıcı Sözlüğü Sağlayıcısıdır. Tablo 1'de, bu sağlayıcının tablosundaki verilerin nasıl görünebileceğine dair bir fikir edinebilirsiniz:

Tablo 1: Örnek kullanıcı sözlüğü tablosu.

kelime uygulama kimliği sıklığı yerel ayar _Kimlik
mapreduce kullanici1 100 en_US 1
precompiler kullanıcı14 200 tr_tr 2
applet kullanici2 225 tr_TR 3
const kullanici1 255 pt_BR 4
int kullanıcı5 100 tr_TR 5

Tablo 1'deki her satır, standart bir sözlükte bulunmayan bir kelimenin bir örneğini temsil eder. Her sütun, söz konusu kelimeyle ilgili ilk karşılaşılan yerel ayar gibi bir veri parçasını temsil eder. Sütun başlıkları, sağlayıcıda depolanan sütun adlarıdır. Dolayısıyla, örneğin bir satırın yerel ayarına başvuruda bulunmak için locale sütununa başvuruda bulunursunuz. Bu sağlayıcı için _ID sütunu, sağlayıcının otomatik olarak tuttuğu bir birincil anahtar sütunu görevi görür.

Kullanıcı Sözlüğü Sağlayıcısı'ndan kelimelerin ve yerel ayarlarının listesini almak için ContentResolver.query() yöntemini çağırırsınız. query() yöntemi, Kullanıcı Sözlüğü Sağlayıcısı tarafından tanımlanan ContentProvider.query() yöntemini çağırır. Aşağıdaki kod satırlarında bir ContentResolver.query() çağrısı gösterilmektedir:

Kotlin

// Queries the UserDictionary and returns results
cursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
        projection,                        // The columns to return for each row
        selectionClause,                   // Selection criteria
        selectionArgs.toTypedArray(),      // Selection criteria
        sortOrder                          // The sort order for the returned rows
)

Java

// Queries the UserDictionary and returns results
cursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    projection,                        // The columns to return for each row
    selectionClause,                   // Selection criteria
    selectionArgs,                     // Selection criteria
    sortOrder);                        // The sort order for the returned rows

Tablo 2'de, query(Uri,projection,selection,selectionArgs,sortOrder) bağımsız değişkenlerinin bir SQL SELECT ifadesiyle nasıl eşleştiği gösterilmiştir:

Tablo 2: SQL sorgusuna kıyasla query().

query() bağımsız değişkeni Anahtar kelime/parametre SELECT Notlar
Uri FROM table_name Uri, table_name adlı sağlayıcıdaki tabloyla eşlenir.
projection col,col,col,... projection, alınan her satır için dahil edilen bir sütun dizisidir.
selection WHERE col = value selection, satır seçme ölçütlerini belirtir.
selectionArgs Tam olarak eşdeğeri yoktur. Seçim bağımsız değişkenleri, seçim ifadesinde ? yer tutucularının yerini alır.
sortOrder ORDER BY col,col,... sortOrder, satırların döndürülen Cursor öğesinde göründüğü sırayı belirtir.

İçerik URI'leri

İç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ı (yetkililiğini) ve bir tabloya işaret eden bir yolu içerir. Sağlayıcıdaki bir tabloya erişmek için istemci yöntemini çağırdığınızda tablonun içerik URI'si bağımsız değişkenlerden biridir.

Önceki kod satırlarında CONTENT_URI sabiti, Kullanıcı Sözlük Sağlayıcısı'nın Words tablosunun içerik URI'sini içerir. ContentResolver nesnesi, URI'nin yetkisini ayrıştırır ve yetkiliyi bilinen sağlayıcıların sistem tablosuyla karşılaştırarak sağlayıcıyı çözmek için kullanır. Daha sonra ContentResolver, sorgu bağımsız değişkenlerini doğru sağlayıcıya gönderebilir.

ContentProvider, erişilecek tabloyu seçmek için içerik URI'sinin yol kısmını kullanır. Sağlayıcıların genellikle gösterdiği her tablo için bir yolu vardır.

Önceki kod satırlarında, Words tablosunun tam URI'si şu şekildedir:

content://user_dictionary/words
  • content:// dizesi, her zaman mevcut olan ve bunu içerik URI'si olarak tanımlayan şemadır.
  • user_dictionary dizesi, sağlayıcının yetkisidir.
  • words dizesi, tablonun yoludur.

Birçok sağlayıcı, URI'nın sonuna kimlik değeri ekleyerek bir tablodaki tek bir satıra erişmenize izin verir. Örneğin, Kullanıcı Sözlüğü Sağlayıcısı'ndan _ID değeri 4 olan bir satırı almak için şu içerik URI'sini kullanabilirsiniz:

Kotlin

val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)

Java

Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

Kimlik değerlerini genellikle, bir satır kümesi aldığınızda ve daha sonra bunlardan birini güncellemek veya silmek istediğinizde kullanırsınız.

Not: Uri ve Uri.Builder sınıfları, dizelerden iyi biçimlendirilmiş URI nesneleri oluşturmaya yönelik kolaylık yöntemleri içerir. ContentUris sınıfı, kimlik değerlerini URI'ye eklemek için kolaylık yöntemleri içerir. Önceki snippet, Kullanıcı Sözlüğü Sağlayıcısı içerik URI'sine kimlik eklemek için withAppendedId() değerini kullanır.

Sağlayıcıdan veri alma

Bu bölümde, örnek olarak Kullanıcı Sözlüğü Sağlayıcısı kullanılarak bir sağlayıcıdan nasıl veri alınacağı açıklanmaktadır.

Anlaşılır olması için bu bölümdeki kod snippet'leri, kullanıcı arayüzü iş parçacığında ContentResolver.query() komutunu çağırır. Ancak gerçek kodda, sorguları ayrı bir iş parçacığında eşzamansız olarak gerçekleştirin. Yükleyiciler kılavuzunda daha ayrıntılı olarak açıklanan CursorLoader sınıfını kullanabilirsiniz. Ayrıca, kod satırları yalnızca snippet'lerden oluşur. Bunlar uygulamanın tamamı gösterilmez.

Bir sağlayıcıdan veri almak için aşağıdaki temel adımları uygulayın:

  1. Sağlayıcı için okuma erişimi izni isteyin.
  2. Sağlayıcıya sorgu gönderen kodu tanımlayın.

Okuma erişimi izni iste

Bir sağlayıcıdan veri almak için uygulamanızın ilgili sağlayıcının okuma erişimi iznine ihtiyacı vardır. Çalışma zamanında bu izni isteyemezsiniz. Bunun yerine, <uses-permission> öğesini ve sağlayıcı tarafından tanımlanan tam izin adını kullanarak manifest dosyanızda bu izne ihtiyacınız olduğunu belirtmeniz gerekir.

Bu öğeyi manifest dosyanızda belirttiğinizde uygulamanız için bu izni istersiniz. Kullanıcılar uygulamanızı yüklediğinde dolaylı olarak bu isteği yerine getirirler.

Kullandığınız sağlayıcının okuma erişimi izninin tam adını ve sağlayıcı tarafından kullanılan diğer erişim izinlerinin adlarını öğrenmek için sağlayıcının belgelerine bakın.

İzinlerin sağlayıcılara erişimdeki rolü, İçerik sağlayıcı izinleri bölümünde daha ayrıntılı olarak açıklanmaktadır.

Kullanıcı Sözlüğü Sağlayıcısı, android.permission.READ_USER_DICTIONARY iznini manifest dosyasında tanımladığından, sağlayıcıdan okuma yapmak isteyen bir uygulama bu izni istemelidir.

Sorguyu oluşturma

Sağlayıcıdan veri almanın sonraki adımı sorgu oluşturmaktır. Aşağıdaki snippet, Kullanıcı Sözlüğü Sağlayıcısı'na erişmek için bazı değişkenleri tanımlar:

Kotlin

// A "projection" defines the columns that are returned for each row
private val mProjection: Array<String> = arrayOf(
        UserDictionary.Words._ID,    // Contract class constant for the _ID column name
        UserDictionary.Words.WORD,   // Contract class constant for the word column name
        UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
)

// Defines a string to contain the selection clause
private var selectionClause: String? = null

// Declares an array to contain selection arguments
private lateinit var selectionArgs: Array<String>

Java

// A "projection" defines the columns that are returned for each row
String[] mProjection =
{
    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
};

// Defines a string to contain the selection clause
String selectionClause = null;

// Initializes an array to contain selection arguments
String[] selectionArgs = {""};

Bir sonraki snippet, örnek olarak Kullanıcı Sözlüğü Sağlayıcısı ile ContentResolver.query() işlevinin nasıl kullanılacağını gösterir. Sağlayıcı istemci sorgusu, SQL sorgusuna benzerdir. Döndürülecek bir sütun kümesi, seçim ölçütleri ve sıralama düzeni içerir.

Sorgunun döndürdüğü sütun kümesine projeksiyon denir ve değişken mProjection'dir.

Alınacak satırları belirten ifade, bir seçim ifadesi ve seçim bağımsız değişkenlerine bölünür. Seçim ifadesi mantıksal ve boole ifadelerinin, sütun adlarının ve değerlerinin bir kombinasyonudur. Değişken: mSelectionClause. Bir değer yerine değiştirilebilir ? parametresini belirtirseniz sorgu yöntemi, değeri seçim bağımsız değişkenleri dizisinden (mSelectionArgs değişkeni) alır.

Sonraki snippet'te kullanıcı bir kelime girmezse seçim ifadesi null olarak ayarlanır ve sorgu, sağlayıcıdaki tüm kelimeleri döndürür. Kullanıcı bir kelime girerse seçim ifadesi UserDictionary.Words.WORD + " = ?" olarak ayarlanır ve seçim bağımsız değişkenleri dizisinin ilk öğesi, kullanıcının girdiği kelimeye ayarlanır.

Kotlin

/*
 * This declares a String array to contain the selection arguments.
 */
private lateinit var selectionArgs: Array<String>

// Gets a word from the UI
searchString = searchWord.text.toString()

// Insert code here to check for invalid or malicious input

// If the word is the empty string, gets everything
selectionArgs = searchString?.takeIf { it.isNotEmpty() }?.let {
    selectionClause = "${UserDictionary.Words.WORD} = ?"
    arrayOf(it)
} ?: run {
    selectionClause = null
    emptyArray<String>()
}

// Does a query against the table and returns a Cursor object
mCursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI, // The content URI of the words table
        projection,                       // The columns to return for each row
        selectionClause,                  // Either null or the word the user entered
        selectionArgs,                    // Either empty or the string the user entered
        sortOrder                         // The sort order for the returned rows
)

// Some providers return null if an error occurs, others throw an exception
when (mCursor?.count) {
    null -> {
        /*
         * Insert code here to handle the error. Be sure not to use the cursor!
         * You might want to call android.util.Log.e() to log this error.
         */
    }
    0 -> {
        /*
         * Insert code here to notify the user that the search is unsuccessful. This isn't
         * necessarily an error. You might want to offer the user the option to insert a new
         * row, or re-type the search term.
         */
    }
    else -> {
        // Insert code here to do something with the results
    }
}

Java

/*
 * This defines a one-element String array to contain the selection argument.
 */
String[] selectionArgs = {""};

// Gets a word from the UI
searchString = searchWord.getText().toString();

// Remember to insert code here to check for invalid or malicious input

// If the word is the empty string, gets everything
if (TextUtils.isEmpty(searchString)) {
    // Setting the selection clause to null returns all words
    selectionClause = null;
    selectionArgs[0] = "";

} else {
    // Constructs a selection clause that matches the word that the user entered
    selectionClause = UserDictionary.Words.WORD + " = ?";

    // Moves the user's input string to the selection arguments
    selectionArgs[0] = searchString;

}

// Does a query against the table and returns a Cursor object
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI, // The content URI of the words table
    projection,                       // The columns to return for each row
    selectionClause,                  // Either null or the word the user entered
    selectionArgs,                    // Either empty or the string the user entered
    sortOrder);                       // The sort order for the returned rows

// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
    /*
     * Insert code here to handle the error. Be sure not to use the cursor! You can
     * call android.util.Log.e() to log this error.
     *
     */
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {

    /*
     * Insert code here to notify the user that the search is unsuccessful. This isn't necessarily
     * an error. You can offer the user the option to insert a new row, or re-type the
     * search term.
     */

} else {
    // Insert code here to do something with the results

}

Bu sorgu, aşağıdaki SQL ifadesine benzer:

SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;

Bu SQL deyiminde, sözleşme sınıfı sabit değerleri yerine gerçek sütun adları kullanılır.

Kötü amaçlı girişe karşı koru

İçerik sağlayıcı tarafından yönetilen veriler bir SQL veritabanındaysa harici güvenilmeyen verilerin ham SQL ifadelerine eklenmesi, SQL yerleştirilmesine yol açabilir.

Şu seçim cümlesini dikkate alın:

Kotlin

// Constructs a selection clause by concatenating the user's input to the column name
var selectionClause = "var = $mUserInput"

Java

// Constructs a selection clause by concatenating the user's input to the column name
String selectionClause = "var = " + userInput;

Bunu yaparsanız, kullanıcının potansiyel olarak kötü amaçlı SQL'i SQL ifadenize bağlamasına izin vermiş olursunuz. Örneğin, kullanıcı mUserInput için "nothing; DROP DROP *;" değerini girebilir ve bu durumda var = nothing; DROP TABLE *; seçim ifadesi ortaya çıkar.

Seçim ifadesi SQL ifadesi olarak işlendiğinden, sağlayıcı SQL yerleştirme girişimlerini yakalayacak şekilde ayarlanmamışsa sağlayıcının bu SQLite veritabanındaki tüm tabloları silmesine neden olabilir.

Bu sorunu önlemek için değiştirilebilir parametre olarak ? kullanan bir seçim ifadesi ve ayrı bir seçim bağımsız değişkenleri dizisi kullanın. Bu şekilde, kullanıcı girişi bir SQL ifadesinin parçası olarak yorumlanmak yerine doğrudan sorguya bağlanır. SQL olarak değerlendirilmediği için kullanıcı girişi kötü amaçlı SQL yerleştiremez. Kullanıcı girişini dahil etmek için birleştirme yöntemi kullanmak yerine şu seçim ifadesini kullanın:

Kotlin

// Constructs a selection clause with a replaceable parameter
var selectionClause = "var = ?"

Java

// Constructs a selection clause with a replaceable parameter
String selectionClause =  "var = ?";

Seçim bağımsız değişkenleri dizisini şu şekilde ayarlayın:

Kotlin

// Defines a mutable list to contain the selection arguments
var selectionArgs: MutableList<String> = mutableListOf()

Java

// Defines an array to contain the selection arguments
String[] selectionArgs = {""};

Seçim bağımsız değişkenleri dizisine aşağıdaki gibi bir değer girin:

Kotlin

// Adds the user's input to the selection argument
selectionArgs += userInput

Java

// Sets the selection argument to the user's input
selectionArgs[0] = userInput;

Sağlayıcı SQL veritabanına dayalı olmasa bile, değiştirilebilir parametre olarak ? kullanan bir seçim ifadesi ve bir seçim bağımsız değişkeni dizisi kullanan bir seçim ifadesi, bir seçimi belirtmek için tercih edilen yöntemdir.

Sorgu sonuçlarını görüntüle

ContentResolver.query() istemci yöntemi, her zaman sorgunun seçim ölçütleriyle eşleşen satırlar için sorgunun projeksiyonu tarafından belirtilen sütunları içeren bir Cursor döndürür. Cursor nesnesi, içerdiği satırlara ve sütunlara rastgele okuma erişimi sağlar.

Cursor yöntemlerini kullanarak sonuçlardaki satırlar üzerinde yineleme yapabilir, her sütunun veri türünü belirleyebilir, bir sütundaki verileri alabilir ve sonuçların diğer özelliklerini inceleyebilirsiniz.

Bazı Cursor uygulamaları, sağlayıcının verileri değiştiğinde nesneyi otomatik olarak günceller, Cursor değiştiğinde gözlemci nesnesinde yöntemleri tetikler veya her ikisini de yapar.

Not: Sağlayıcı, sorguyu yapan nesnenin yapısına göre sütunlara erişimi kısıtlayabilir. Örneğin, Kişi Sağlayıcı bazı sütunlar için erişimi bağdaştırıcıların senkronize edilmesiyle kısıtladığından, bu sütunları bir etkinliğe veya hizmete döndürmez.

Seçim ölçütleriyle eşleşen satır yoksa sağlayıcı, Cursor.getCount() için 0 olan bir Cursor nesnesi, yani boş imleç döndürür.

Dahili bir hata oluşursa sorgunun sonuçları belirli sağlayıcıya bağlı olarak değişir. null döndürebilir veya Exception atabilir.

Cursor bir satır listesi olduğundan Cursor içeriğini görüntülemenin iyi bir yolu, bunu SimpleCursorAdapter kullanarak bir ListView öğesine bağlamaktır.

Aşağıdaki snippet'te, önceki snippet'teki kod devam eder. Sorgu tarafından alınan Cursor öğesini içeren bir SimpleCursorAdapter nesnesi oluşturur ve bu nesneyi ListView için bağdaştırıcı olarak ayarlar.

Kotlin

// Defines a list of columns to retrieve from the Cursor and load into an output row
val wordListColumns : Array<String> = arrayOf(
        UserDictionary.Words.WORD,      // Contract class constant containing the word column name
        UserDictionary.Words.LOCALE     // Contract class constant containing the locale column name
)

// Defines a list of View IDs that receive the Cursor columns for each row
val wordListItems = intArrayOf(R.id.dictWord, R.id.locale)

// Creates a new SimpleCursorAdapter
cursorAdapter = SimpleCursorAdapter(
        applicationContext,             // The application's Context object
        R.layout.wordlistrow,           // A layout in XML for one row in the ListView
        mCursor,                        // The result from the query
        wordListColumns,                // A string array of column names in the cursor
        wordListItems,                  // An integer array of view IDs in the row layout
        0                               // Flags (usually none are needed)
)

// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter)

Java

// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] wordListColumns =
{
    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
};

// Defines a list of View IDs that receive the Cursor columns for each row
int[] wordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
cursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(),               // The application's Context object
    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
    mCursor,                               // The result from the query
    wordListColumns,                       // A string array of column names in the cursor
    wordListItems,                         // An integer array of view IDs in the row layout
    0);                                    // Flags (usually none are needed)

// Sets the adapter for the ListView
wordList.setAdapter(cursorAdapter);

Not: Bir Cursor içeren ListView öğesini geri getirmek için imleç _ID adlı bir sütun içermelidir. Bu nedenle, gösterilen sorgu ListView göstermese bile Words tablosu için _ID sütununu alır. Bu kısıtlama, çoğu sağlayıcının her tablosunda neden bir _ID sütunu olduğunu da açıklar.

Sorgu sonuçlarından veri alma

Bu sonuçları sorgu sonuçlarını görüntülemenin yanı sıra başka görevler için de kullanabilirsiniz. Örneğin, yazımları Kullanıcı Sözlüğü Sağlayıcısı'ndan alabilir ve ardından diğer sağlayıcılarda arayabilirsiniz. Bunun için Cursor içindeki satırları aşağıdaki örnekte gösterildiği gibi yineleyin:

Kotlin

/*
* Only executes if the cursor is valid. The User Dictionary Provider returns null if
* an internal error occurs. Other providers might throw an Exception instead of returning null.
*/
mCursor?.apply {
    // Determine the column index of the column named "word"
    val index: Int = getColumnIndex(UserDictionary.Words.WORD)

    /*
     * Moves to the next row in the cursor. Before the first movement in the cursor, the
     * "row pointer" is -1, and if you try to retrieve data at that position you get an
     * exception.
     */
    while (moveToNext()) {
        // Gets the value from the column
        newWord = getString(index)

        // Insert code here to process the retrieved word
        ...
        // End of while loop
    }
}

Java

// Determine the column index of the column named "word"
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);

/*
 * Only executes if the cursor is valid. The User Dictionary Provider returns null if
 * an internal error occurs. Other providers might throw an Exception instead of returning null.
 */

if (mCursor != null) {
    /*
     * Moves to the next row in the cursor. Before the first movement in the cursor, the
     * "row pointer" is -1, and if you try to retrieve data at that position you get an
     * exception.
     */
    while (mCursor.moveToNext()) {

        // Gets the value from the column
        newWord = mCursor.getString(index);

        // Insert code here to process the retrieved word
        ...
        // End of while loop
    }
} else {

    // Insert code here to report an error if the cursor is null or the provider threw an exception
}

Cursor uygulamaları, nesneden farklı veri türlerini almak için çeşitli "get" yöntemleri içerir. Örneğin, önceki snippet getString() değerini kullanır. Bunlar ayrıca, sütunun veri türünü belirten bir değer döndüren getType() yöntemine sahiptir.

İçerik sağlayıcı izinleri

Bir sağlayıcının uygulaması, diğer uygulamaların sağlayıcı verilerine erişmek için sahip olması gereken izinleri belirtebilir. Bu izinler, kullanıcıya bir uygulamanın hangi verilere erişmeye çalıştığını bildirir. Sağlayıcının gereksinimlerine bağlı olarak, diğer uygulamalar sağlayıcıya erişmek için ihtiyaç duydukları izinleri ister. Son kullanıcılar uygulamayı yüklediklerinde istenen izinleri görür.

Bir sağlayıcının uygulamasında herhangi bir izin belirtilmezse sağlayıcı dışa aktarılmadığı sürece diğer uygulamalar, sağlayıcının verilerine erişemez. Ayrıca sağlayıcının uygulamasındaki bileşenler, belirtilen izinlerden bağımsız olarak her zaman tam okuma ve yazma erişimine sahiptir.

Kullanıcı Sözlüğü Sağlayıcısı, kendisinden veri almak için android.permission.READ_USER_DICTIONARY iznini gerektirir. Sağlayıcının veri eklemek, güncellemek veya silmek için ayrı bir android.permission.WRITE_USER_DICTIONARY izni vardır.

Bir uygulama, bir sağlayıcıya erişmek üzere gereken izinleri almak için bu izinleri manifest dosyasında bir <uses-permission> öğesiyle ister. Android Paket Yöneticisi uygulamayı yüklediğinde kullanıcı, uygulamanın istediği tüm izinleri onaylamalıdır. Kullanıcı bunları onaylarsa Paket Yöneticisi yükleme işlemine devam eder. Kullanıcı onaylamazsa Paket Yöneticisi yüklemeyi durdurur.

Aşağıdaki örnek <uses-permission> öğesi, Kullanıcı Sözlüğü Sağlayıcısı'na okuma erişimi istiyor:

<uses-permission android:name="android.permission.READ_USER_DICTIONARY">

İzinlerin sağlayıcı erişimi üzerindeki etkisi, Güvenlik ipuçları bölümünde daha ayrıntılı olarak açıklanmıştır.

Veri ekleme, güncelleme ve silme

Bir sağlayıcıdan veri alırken olduğu gibi, verileri değiştirmek için sağlayıcı istemcisi ile sağlayıcının ContentProvider arasındaki etkileşimi de kullanırsınız. İlgili ContentProvider yöntemine aktarılan bağımsız değişkenlerle bir ContentResolver yöntemini çağırırsınız. Sağlayıcı ve sağlayıcı, güvenlik ve işlemler arası iletişimi otomatik olarak yönetir.

Veri ekle

Bir sağlayıcıya veri eklemek için ContentResolver.insert() yöntemini çağırırsınız. Bu yöntem, sağlayıcıya yeni bir satır ekler ve bu satır için bir içerik URI'si döndürür. Aşağıdaki snippet'te Kullanıcı Sözlüğü Sağlayıcısı'na nasıl yeni kelime ekleneceği gösterilmektedir:

Kotlin

// Defines a new Uri object that receives the result of the insertion
lateinit var newUri: Uri
...
// Defines an object to contain the new values to insert
val newValues = ContentValues().apply {
    /*
     * Sets the values of each column and inserts the word. The arguments to the "put"
     * method are "column name" and "value".
     */
    put(UserDictionary.Words.APP_ID, "example.user")
    put(UserDictionary.Words.LOCALE, "en_US")
    put(UserDictionary.Words.WORD, "insert")
    put(UserDictionary.Words.FREQUENCY, "100")

}

newUri = contentResolver.insert(
        UserDictionary.Words.CONTENT_URI,   // The UserDictionary content URI
        newValues                           // The values to insert
)

Java

// Defines a new Uri object that receives the result of the insertion
Uri newUri;
...
// Defines an object to contain the new values to insert
ContentValues newValues = new ContentValues();

/*
 * Sets the values of each column and inserts the word. The arguments to the "put"
 * method are "column name" and "value".
 */
newValues.put(UserDictionary.Words.APP_ID, "example.user");
newValues.put(UserDictionary.Words.LOCALE, "en_US");
newValues.put(UserDictionary.Words.WORD, "insert");
newValues.put(UserDictionary.Words.FREQUENCY, "100");

newUri = getContentResolver().insert(
    UserDictionary.Words.CONTENT_URI,   // The UserDictionary content URI
    newValues                           // The values to insert
);

Yeni satırla ilgili veriler, tek satırlık imleç biçimine benzer şekilde tek bir ContentValues nesnesine yerleştirilir. Bu nesnedeki sütunların aynı veri türüne sahip olması gerekmez. Hiçbir değer belirtmek istemiyorsanız ContentValues.putNull() kullanarak bir sütunu null olarak ayarlayabilirsiniz.

Bu sütun otomatik olarak korunduğu için önceki snippet _ID sütununu eklemez. Sağlayıcı, eklenen her satıra benzersiz bir _ID değeri atar. Sağlayıcılar genellikle bu değeri tablonun birincil anahtarı olarak kullanır.

newUri içinde döndürülen içerik URI'si, yeni eklenen satırı aşağıdaki biçimde tanımlar:

content://user_dictionary/words/<id_value>

<id_value>, yeni satır için _ID öğesinin içeriğidir. Çoğu sağlayıcı, bu içerik URI'si biçimini otomatik olarak algılayıp istenen işlemi söz konusu satırda gerçekleştirebilir.

Döndürülen Uri öğesinden _ID değerini almak için ContentUris.parseId() çağrısı yapın.

Verileri güncelle

Sorguda olduğu gibi, ekleme ve seçim ölçütlerinde olduğu gibi, bir satırı güncellemek için güncellenen değerlere sahip bir ContentValues nesnesi kullanırsınız. Kullandığınız istemci yöntemi: ContentResolver.update(). Yalnızca güncellediğiniz sütunlar için ContentValues nesnesine değer eklemeniz gerekir. Bir sütunun içeriğini temizlemek istiyorsanız değeri null olarak ayarlayın.

Aşağıdaki snippet, yerel ayarı "en" diline sahip tüm satırları null yerel ayarına sahip olacak şekilde değiştirir. Döndürülen değer, güncellenen satır sayısıdır.

Kotlin

// Defines an object to contain the updated values
val updateValues = ContentValues().apply {
    /*
     * Sets the updated value and updates the selected words.
     */
    putNull(UserDictionary.Words.LOCALE)
}

// Defines selection criteria for the rows you want to update
val selectionClause: String = UserDictionary.Words.LOCALE + "LIKE ?"
val selectionArgs: Array<String> = arrayOf("en_%")

// Defines a variable to contain the number of updated rows
var rowsUpdated: Int = 0
...
rowsUpdated = contentResolver.update(
        UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
        updateValues,                      // The columns to update
        selectionClause,                   // The column to select on
        selectionArgs                      // The value to compare to
)

Java

// Defines an object to contain the updated values
ContentValues updateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
String selectionClause = UserDictionary.Words.LOCALE +  " LIKE ?";
String[] selectionArgs = {"en_%"};

// Defines a variable to contain the number of updated rows
int rowsUpdated = 0;
...
/*
 * Sets the updated value and updates the selected words.
 */
updateValues.putNull(UserDictionary.Words.LOCALE);

rowsUpdated = getContentResolver().update(
    UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
    updateValues,                      // The columns to update
    selectionClause,                   // The column to select on
    selectionArgs                      // The value to compare to
);

ContentResolver.update() çağırırken kullanıcı girişini temizleyin. Bu konuda daha fazla bilgi edinmek için Kötü amaçlı girişe karşı koruma bölümünü okuyun.

Verileri sil

Satırları silmek, satır verilerini almaya benzer. Silmek istediğiniz satırlar için seçim ölçütleri belirtirsiniz ve istemci yöntemi, silinen satır sayısını döndürür. Aşağıdaki snippet, uygulama kimliği "user" ile eşleşen satırları siler. Yöntem, silinen satır sayısını döndürür.

Kotlin

// Defines selection criteria for the rows you want to delete
val selectionClause = "${UserDictionary.Words.APP_ID} LIKE ?"
val selectionArgs: Array<String> = arrayOf("user")

// Defines a variable to contain the number of rows deleted
var rowsDeleted: Int = 0
...
// Deletes the words that match the selection criteria
rowsDeleted = contentResolver.delete(
        UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
        selectionClause,                   // The column to select on
        selectionArgs                      // The value to compare to
)

Java

// Defines selection criteria for the rows you want to delete
String selectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] selectionArgs = {"user"};

// Defines a variable to contain the number of rows deleted
int rowsDeleted = 0;
...
// Deletes the words that match the selection criteria
rowsDeleted = getContentResolver().delete(
    UserDictionary.Words.CONTENT_URI,  // The UserDictionary content URI
    selectionClause,                   // The column to select on
    selectionArgs                      // The value to compare to
);

ContentResolver.delete() çağırırken kullanıcı girişini temizleyin. Bu konuda daha fazla bilgi edinmek için Kötü amaçlı girişlere karşı koruma bölümünü okuyun.

Sağlayıcı veri türleri

İçerik sağlayıcılar birçok farklı veri türü sunabilir. Kullanıcı Sözlüğü Sağlayıcısı yalnızca metin sunar ancak sağlayıcılar aşağıdaki biçimleri de sunabilir:

  • Tam sayı
  • uzun tam sayı (uzun)
  • kayan nokta
  • uzun kayan nokta (çift)

Sağlayıcıların sıklıkla kullandığı bir diğer veri türü de 64 KB bayt dizisi olarak uygulanan bir ikili büyük nesne (BLOB) şeklindedir. Cursor sınıfı "get" yöntemlerine bakarak kullanılabilir veri türlerini görebilirsiniz.

Bir sağlayıcıdaki her sütunun veri türü genellikle sağlayıcının dokümanlarında listelenir. Kullanıcı Sözlüğü Sağlayıcısı için veri türleri, UserDictionary.Words sözleşme sınıfının referans dokümanlarında listelenmiştir. Sözleşme sınıfları, Sözleşme sınıfları bölümünde açıklanmıştır. Veri türünü Cursor.getType() çağırarak da belirleyebilirsiniz.

Sağlayıcılar, tanımladıkları her içerik URI'si için MIME veri türü bilgilerini de korur. Uygulamanızın, sağlayıcının sunduğu verileri işleyip işleyemediğini öğrenmek veya MIME türüne göre bir işleme türü seçmek için MIME türü bilgilerini kullanabilirsiniz. Karmaşık veri yapıları veya dosyalar içeren bir sağlayıcıyla çalışırken genellikle MIME türüne ihtiyacınız vardır.

Örneğin, Kişi Sağlayıcı'daki ContactsContract.Data tablosu, her satırda depolanan kişi verilerinin türünü etiketlemek için MIME türlerini kullanır. İçerik URI'sine karşılık gelen MIME türünü almak için ContentResolver.getType() çağrısı yapın.

MIME türü referansı bölümünde, hem standart hem de özel MIME türlerinin söz dizimi açıklanmaktadır.

Alternatif sağlayıcı erişimi biçimleri

Uygulama geliştirmede üç alternatif sağlayıcı erişimi biçimi önemlidir:

  • Toplu erişim: ContentProviderOperation sınıfındaki yöntemlerle bir erişim çağrıları toplu olarak oluşturabilir ve daha sonra bunları ContentResolver.applyBatch() ile uygulayabilirsiniz.
  • Eşzamansız sorgular: Sorguları ayrı bir iş parçacığında yapın. Bir CursorLoader nesnesi kullanabilirsiniz. Yükleyiciler kılavuzundaki örnekler, bunun nasıl yapılacağını gösterir.
  • Amaçlar kullanarak veri erişimi: Doğrudan sağlayıcıya niyet gönderemeseniz de sağlayıcının uygulamasına bir niyet gönderebilirsiniz. Bu uygulama, genellikle sağlayıcının verilerini değiştirmek için en iyi donanıma sahiptir.

Amaçlar kullanılarak yapılan toplu erişim ve değişiklik yapma aşağıdaki bölümlerde açıklanmıştır.

Toplu erişim

Bir sağlayıcıya toplu erişim, çok sayıda satır eklemek, aynı yöntem çağrısında birden çok tabloya satır eklemek ve genel olarak atom işlemi adı verilen işlem olarak işlem sınırları içinde bir dizi işlemi gerçekleştirmek için yararlıdır.

Toplu modda bir sağlayıcıya erişmek için ContentProviderOperation nesne dizisi oluşturup bunları ContentResolver.applyBatch() ile bir içerik sağlayıcıya dağıtın. Belirli bir içerik URI'si yerine, içerik sağlayıcının yetkisini bu yönteme geçirirsiniz.

Bu sayede dizideki her ContentProviderOperation nesnesi farklı bir tabloda çalışabilir. ContentResolver.applyBatch() çağrısı, bir sonuç dizisi döndürür.

ContactsContract.RawContacts sözleşme sınıfının açıklaması, toplu eklemeyi gösteren bir kod snippet'i içerir.

Amaçları kullanarak veri erişimi

Amaçlar, içerik sağlayıcıya dolaylı erişim sağlayabilir. Uygulamanızın erişim izinleri olmasa bile, izinleri olan bir uygulamadan sonuç intent'i geri alarak veya izinlere sahip bir uygulamayı etkinleştirip kullanıcının uygulamada çalışmasına izin vererek kullanıcının erişim izinleri olmasa bile sağlayıcıdaki verilere erişmesine izin verebilirsiniz.

Geçici izinlerle erişim elde etme

Gerekli izinlere sahip bir uygulamaya niyet göndererek ve URI izinlerini içeren bir sonuç niyetini geri alarak uygun erişim izinlerine sahip olmasanız bile içerik sağlayıcıdaki verilere erişebilirsiniz. Bunlar, belirli bir içerik URI'sını alan etkinlik bitene kadar devam eden izinlerdir. Kalıcı izinlere sahip uygulama, sonuç amacında bir işaret ayarlayarak geçici izinler verir:

Not: Bu işaretler, yetkisi içerik URI'sinde bulunan sağlayıcıya genel okuma veya yazma erişimi vermez. Erişim yalnızca URI'nın kendisi içindir.

Başka bir uygulamaya içerik URI'leri gönderirken bu işaretlerden en az birini ekleyin. İşaretler, amacı alan ve Android 11 (API düzeyi 30) veya sonraki sürümleri hedefleyen tüm uygulamalar için aşağıdaki özellikleri sağlar:

  • Amaçta yer alan bayrağa bağlı olarak içerik URI'sının temsil ettiği verileri okuma veya bu verilere yazma.
  • URI yetkilisiyle eşleşen içerik sağlayıcısını içeren uygulamada paket görünürlüğü elde edin. Niyeti gönderen uygulama ile içerik sağlayıcısını içeren uygulama iki farklı uygulama olabilir.

Sağlayıcı, <provider> öğesinin android:grantUriPermissions özelliğini ve <provider> öğesinin <grant-uri-permission> alt öğesini kullanarak manifest dosyasındaki içerik URI'leri için URI izinlerini tanımlar. URI izin mekanizması, Android'de İzinler kılavuzunda daha ayrıntılı olarak açıklanmıştır.

Örneğin, READ_CONTACTS iznine sahip olmasanız bile Kişi Sağlayıcı'da bir kişiyle ilgili verileri alabilirsiniz. Bunu, bir kişiye doğum gününde e-selamlar gönderen bir uygulamada yapmak isteyebilirsiniz. Kullanıcının tüm kişilerine ve tüm bilgilerine erişmenizi sağlayan READ_CONTACTS istemek yerine kullanıcının, uygulamanızda hangi kişileri kullanacağını kontrol etmesini sağlayın. Bunun için aşağıdaki süreci uygulayın:

  1. Uygulamanızda, startActivityForResult() yöntemini kullanarak ACTION_PICK işlemini ve "kişiler" MIME türünü CONTENT_ITEM_TYPE içeren bir niyet gönderin.
  2. Bu amaç, Kişiler uygulamasının "seçim" etkinliğine ait intent filtresiyle eşleştiğinden etkinlik ön plana gelir.
  3. Kullanıcı, seçim etkinliğinde güncellemek için bir kişi seçer. Böyle bir durumda, seçim etkinliği, uygulamanıza geri dönüş yapmak üzere bir amaç oluşturmak için setResult(resultcode, intent) yöntemini çağırır. Amaç, kullanıcının seçtiği kişinin içerik URI'sını ve "ekstralar" FLAG_GRANT_READ_URI_PERMISSION işaretini içerir. Bu işaretler, içerik URI'si ile işaret edilen kişinin verilerini okuması için uygulamanıza URI izni verir. Seçim etkinliği daha sonra kontrolü uygulamanıza geri döndürmek için finish() işlevini çağırır.
  4. Etkinliğiniz ön plana döner ve sistem, etkinliğinizin onActivityResult() yöntemini çağırır. Bu yöntem, Kişiler uygulamasındaki seçim etkinliği tarafından oluşturulan sonuç amacını alır.
  5. Sonuç amacındaki içerik URI'si ile manifest dosyanızdaki sağlayıcı için kalıcı okuma erişimi izni istememiş olsanız bile kişi verilerini Kişi Sağlayıcı'dan okuyabilirsiniz. Sonrasında kişinin doğum günü bilgilerini veya e-posta adresini alıp e-selamlama gönderebilirsiniz.

Başka bir uygulama kullan

Kullanıcının erişim izniniz olmayan verileri değiştirmesine izin vermenin bir başka yolu da izinleri olan bir uygulamayı etkinleştirmek ve işi kullanıcının yapmasına izin vermektir.

Örneğin, Takvim uygulaması, uygulamanın ekleme kullanıcı arayüzünü etkinleştirmenize olanak tanıyan ACTION_INSERT niyetini kabul eder. Bu niyette "ekstralar" veri aktarabilirsiniz. Uygulama, bu verileri kullanıcı arayüzünü önceden doldurmak için kullanır. Düzenli etkinlikler karmaşık bir söz dizimine sahip olduğundan, etkinlikleri Takvim Sağlayıcı'ya eklemek için tercih edilen yöntem, Takvim uygulamasını bir ACTION_INSERT ile etkinleştirmek ve ardından kullanıcının etkinliği oraya eklemesine izin vermektir.

Yardımcı uygulama kullanarak verileri göster

Uygulamanızın erişim izinleri varsa verileri başka bir uygulamada görüntülemek için intent kullanabilirsiniz. Örneğin, Takvim uygulaması belirli bir tarihi veya etkinliği görüntüleyen ACTION_VIEW niyetini kabul eder. Bu sayede, kendi kullanıcı arayüzünüzü oluşturmanıza gerek kalmadan takvim bilgilerini görüntüleyebilirsiniz. Bu özellik hakkında daha fazla bilgi edinmek için Takvim sağlayıcısına genel bakış bölümüne göz atın.

Niyeti gönderdiğiniz uygulamanın, sağlayıcıyla ilişkilendirilmiş uygulama olması gerekmez. Örneğin, Kişi Sağlayıcıdan bir kişi alabilir ve ardından, kişinin resmine ait içerik URI'sini içeren bir ACTION_VIEW niyetini resim görüntüleyiciye gönderebilirsiniz.

Sözleşmeli sınıflar

Sözleşme sınıfı, uygulamaların içerik URI'leri, sütun adları, amaç işlemleri ve içerik sağlayıcının diğer özellikleriyle çalışmasına yardımcı olan sabitleri tanımlar. Sözleşme sınıfları, sağlayıcı ile otomatik olarak dahil edilmez. Sağlayıcının geliştiricisi, bunları tanımlamalı ve ardından diğer geliştiricilerin kullanımına sunmalıdır. Android platformuna dahil olan sağlayıcıların birçoğunun android.provider paketinde ilgili sözleşme sınıfları vardır.

Örneğin, Kullanıcı Sözlüğü Sağlayıcısı, içerik URI'si ve sütun adı sabitlerini içeren UserDictionary sözleşme sınıfına sahiptir. Words tablosunun içerik URI'si UserDictionary.Words.CONTENT_URI sabit değeri ile tanımlanır. UserDictionary.Words sınıfı, bu kılavuzdaki örnek snippet'lerde kullanılan sütun adı sabit değerlerini de içerir. Örneğin, bir sorgu projeksiyonu aşağıdaki gibi tanımlanabilir:

Kotlin

val projection : Array<String> = arrayOf(
        UserDictionary.Words._ID,
        UserDictionary.Words.WORD,
        UserDictionary.Words.LOCALE
)

Java

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

Başka bir sözleşme sınıfı, Kişi Sağlayıcısı için ContactsContract şeklindedir. Bu sınıfın referans belgeleri arasında örnek kod snippet'leri yer almaktadır. Alt sınıflarından biri olan ContactsContract.Intents.Insert, amaçlar ve amaç verileri için sabit değerler içeren bir sözleşme sınıfıdır.

MIME türü referansı

İçerik sağlayıcılar; standart MIME medya türlerini, özel MIME türü dizelerini veya her ikisini birden döndürebilir.

MIME türleri aşağıdaki biçimdedir:

type/subtype

Örneğin, iyi bilinen text/html MIME türü, text türüne ve html alt türüne sahiptir. Sağlayıcının bir URI için bu türü döndürmesi, o URI'yı kullanan sorgunun HTML etiketlerini içeren metni döndürdüğü anlamına gelir.

Sağlayıcıya özel MIME türleri olarak da adlandırılan özel MIME türü dizeleri, daha karmaşık type ve subtype değerlerine sahiptir. Birden çok satır için tür değeri her zaman aşağıdaki gibidir:

vnd.android.cursor.dir

Tek bir satır için tür değeri her zaman aşağıdaki gibidir:

vnd.android.cursor.item

subtype, sağlayıcıya özeldir. Yerleşik Android sağlayıcıların genellikle basit bir alt türü vardır. Örneğin, Kişiler uygulaması bir telefon numarası için bir satır oluşturduğunda, satırda aşağıdaki MIME türünü ayarlar:

vnd.android.cursor.item/phone_v2

Alt tür değeri: phone_v2.

Diğer sağlayıcı geliştiricileri, sağlayıcının yetkisine ve tablo adlarına dayalı olarak kendi alt tür kalıplarını oluşturabilir. Örneğin, tren tarifelerini içeren bir sağlayıcıyı düşünün. Sağlayıcının yetkisi com.example.trains olup Satır1, Satır2 ve Satır3 tablolarını içerir. Line1 tablosu için aşağıdaki içerik URI'sine yanıt olarak:

content://com.example.trains/Line1

sağlayıcı şu MIME türünü döndürür:

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

Satır2 tablosunun 5. satırı için aşağıdaki içerik URI'sine yanıt olarak:

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

sağlayıcı şu MIME türünü döndürür:

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

Çoğu içerik sağlayıcı, kullandıkları MIME türleri için sözleşme sınıfı sabitlerini tanımlar. Örneğin, Kişi Sağlayıcı sözleşme sınıfı ContactsContract.RawContacts, tek bir ham kişi satırının MIME türü için CONTENT_ITEM_TYPE sabitini tanımlar.

Tek satırlara ilişkin içerik URI'leri İçerik URI'leri bölümünde açıklanmıştır.