Ein Contentanbieter verwaltet den Zugriff auf ein zentrales Daten-Repository. Ein Anbieter ist Teil einer Android-Anwendung, die oft eine eigene Benutzeroberfläche für die Arbeit mit den Daten bietet. Contentanbieter werden jedoch hauptsächlich von anderen Anwendungen verwendet, die über ein Anbieterclientobjekt auf den Anbieter zugreifen. Zusammen bieten Anbieter und Anbieterclients eine einheitliche, standardmäßige Schnittstelle zu Daten, die auch die interprozedurale Kommunikation und den sicheren Datenzugriff abwickelt.
Normalerweise arbeiten Sie mit Contentanbietern in einem von zwei Szenarien: Sie implementieren Code, um in einer anderen Anwendung auf einen vorhandenen Contentanbieter zuzugreifen, oder Sie erstellen in Ihrer Anwendung einen neuen Contentanbieter, um Daten für andere Anwendungen freizugeben.
Auf dieser Seite werden die Grundlagen der Zusammenarbeit mit bestehenden Contentanbietern beschrieben. Informationen zum Implementieren von Inhaltsanbietern in Ihren eigenen Anwendungen finden Sie unter Inhaltsanbieter erstellen.
In diesem Thema wird Folgendes beschrieben:
- So funktionieren Contentanbieter
- Die API, mit der Sie Daten von einem Inhaltsanbieter abrufen.
- Die API, mit der du Daten bei einem Contentanbieter einfügen, aktualisieren oder löschen kannst.
- Andere API-Funktionen, die die Zusammenarbeit mit Anbietern erleichtern.
Übersicht
Ein Contentanbieter stellt Daten externen Anwendungen in einer oder mehreren Tabellen zur Verfügung, die den Tabellen in einer relationalen Datenbank ähneln. Eine Zeile stellt eine Instanz eines Datentyps dar, den der Anbieter erhebt. Jede Spalte in der Zeile steht für ein einzelnes Datenelement, das für eine Instanz erfasst wurde.
Ein Contentanbieter koordiniert den Zugriff auf die Datenspeicherebene in Ihrer Anwendung für verschiedene APIs und Komponenten. Wie in Abbildung 1 dargestellt, umfassen diese Folgendes:
- Zugriff auf Ihre Anwendungsdaten für andere Apps freigeben
- Daten an ein Widget senden
- Benutzerdefinierte Suchvorschläge für Ihre Anwendung über das Such-Framework mit
SearchRecentSuggestionsProvider
zurückgeben - Anwendungsdaten über eine Implementierung von
AbstractThreadedSyncAdapter
mit dem Server synchronisieren - Laden von Daten in Ihre UI mit einem
CursorLoader
Auf einen Anbieter zugreifen
Wenn Sie auf Daten bei einem Contentanbieter zugreifen möchten, verwenden Sie das ContentResolver
-Objekt in der Context
Ihrer Anwendung, um als Client mit dem Anbieter zu kommunizieren. Das ContentResolver
-Objekt kommuniziert mit dem Anbieterobjekt, einer Instanz einer Klasse, die ContentProvider
implementiert.
Das Anbieterobjekt empfängt Datenanfragen von Kunden, führt die angeforderte Aktion aus und gibt die Ergebnisse zurück. Dieses Objekt hat Methoden, die identisch benannte Methoden im Anbieterobjekt aufrufen, einer Instanz einer der konkreten Unterklassen von ContentProvider
. Die ContentResolver
-Methoden bieten die grundlegenden CRUD-Funktionen (Erstellen, Abrufen, Aktualisieren und Löschen) des nichtflüchtigen Speichers.
Ein gängiges Muster für den Zugriff auf eine ContentProvider
über die Benutzeroberfläche verwendet einen CursorLoader
, um eine asynchrone Abfrage im Hintergrund auszuführen. Die Activity
oder Fragment
in Ihrer Benutzeroberfläche ruft eine CursorLoader
für die Abfrage auf, die wiederum die ContentProvider
mithilfe der ContentResolver
abholt.
So ist die Benutzeroberfläche während der Ausführung der Abfrage weiterhin verfügbar. Dieses Muster umfasst die Interaktion einer Reihe verschiedener Objekte sowie des zugrunde liegenden Speichermechanismus, wie in Abbildung 2 dargestellt.
Hinweis:Damit Ihre Anwendung auf einen Anbieter zugreifen kann, muss sie in der Manifestdatei in der Regel bestimmte Berechtigungen anfordern. Dieses Entwicklungsmuster wird im Abschnitt Berechtigungen von Contentanbietern ausführlicher beschrieben.
Einer der integrierten Anbieter auf der Android-Plattform ist der Anbieter des Nutzerwörterbuchs, in dem die nicht standardmäßigen Wörter gespeichert werden, die der Nutzer behalten möchte. In Tabelle 1 sehen Sie, wie die Daten in der Tabelle dieses Anbieters aussehen könnten:
Wortspiele | App-ID | Beiträgen | locale | _ID |
---|---|---|---|---|
mapreduce |
Nutzer 1 | 100 | de_DE | 1 |
precompiler |
Nutzer 14 | 200 | fr_FR | 2 |
applet |
Nutzer 2 | 225 | fr_CA | 3 |
const |
Nutzer 1 | 255 | pt_BR | 4 |
int |
Nutzer5 | 100 | de_DE | 5 |
In Tabelle 1 steht jede Zeile für ein Wort, das in keinem Standardwörterbuch zu finden ist. Jede Spalte stellt einen Datensatz für dieses Wort dar, z. B. die Sprache, in der es zuerst gefunden wurde. Die Spaltenüberschriften sind Spaltennamen, die beim Anbieter gespeichert sind. Wenn Sie beispielsweise auf die Sprache einer Zeile verweisen möchten, verwenden Sie die Spalte locale
. Bei diesem Anbieter dient die Spalte _ID
als Primärschlüsselspalte, die vom Anbieter automatisch verwaltet wird.
Um eine Liste der Wörter und ihrer Sprachen vom User Dictionary Provider zu erhalten, rufe ContentResolver.query()
auf.
Die Methode query()
ruft die vom Anbieter des Nutzerwörterbuchs definierte Methode ContentProvider.query()
auf. Die folgenden Codezeilen zeigen einen ContentResolver.query()
-Aufruf:
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
Tabelle 2 zeigt, wie die Argumente für query(Uri,projection,selection,selectionArgs,sortOrder)
mit einer SQL-SELECT-Anweisung übereinstimmen:
query() -Argument |
SELECT-Keyword/-Parameter | Hinweise |
---|---|---|
Uri |
FROM table_name |
Uri ist der Tabelle im Anbieter table_name zugeordnet. |
projection |
col,col,col,... |
projection ist ein Array von Spalten, das für jede abgerufene Zeile enthalten ist.
|
selection |
WHERE col = value |
selection gibt die Kriterien für die Auswahl von Zeilen an. |
selectionArgs |
Keine genaue Entsprechung. Auswahlargumente ersetzen ? -Platzhalter in der Auswahlklausel.
|
|
sortOrder |
ORDER BY col,col,... |
sortOrder gibt die Reihenfolge an, in der Zeilen in der zurückgegebenen Cursor angezeigt werden.
|
Inhalts-URIs
Ein Inhalts-URI ist ein URI, der Daten bei einem Anbieter identifiziert. Content-URIs enthalten den symbolischen Namen des gesamten Anbieters – seine Autorität – und einen Namen, der auf eine Tabelle verweist – einen Pfad. Wenn Sie eine Clientmethode aufrufen, um auf eine Tabelle in einem Anbieter zuzugreifen, ist der Inhalts-URI für die Tabelle eines der Argumente.
In den vorherigen Codezeilen enthält die Konstante CONTENT_URI
den Inhalts-URI der Tabelle Words
des Anbieters des Nutzerwörterbuchs. Das ContentResolver
-Objekt analysiert die Zertifizierungsstelle des URI und verwendet sie, um den Anbieter zu auflösen, indem die Zertifizierungsstelle mit einer Systemtabelle bekannter Anbieter verglichen wird. Die ContentResolver
kann dann die Abfrageargumente an den richtigen Anbieter weiterleiten.
ContentProvider
verwendet den Pfadteil des Inhalts-URI, um die Tabelle auszuwählen, auf die zugegriffen werden soll. Ein Anbieter hat normalerweise einen Pfad für jede bereitgestellte Tabelle.
In den vorherigen Codezeilen lautet der vollständige URI für die Tabelle Words
:
content://user_dictionary/words
- Der
content://
-String ist das Schema, das immer vorhanden ist und den URI als Inhalts-URI identifiziert. - Der String
user_dictionary
ist die Autorität des Anbieters. - Der String
words
ist der Pfad der Tabelle.
Bei vielen Anbietern können Sie auf eine einzelne Zeile in einer Tabelle zugreifen, indem Sie am Ende des URI einen ID-Wert anhängen. Wenn Sie beispielsweise eine Zeile mit der _ID
4
vom Anbieter des Nutzerwörterbuchs abrufen möchten, können Sie diesen Inhalts-URI verwenden:
Kotlin
val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)
Java
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
Sie verwenden ID-Werte häufig, wenn Sie eine Reihe von Zeilen abrufen und dann eine davon aktualisieren oder löschen möchten.
Hinweis:Die Klassen Uri
und Uri.Builder
enthalten praktische Methoden zum Erstellen korrekt formatierter URI-Objekte aus Strings. Die Klasse ContentUris
enthält praktische Methoden zum Anhängen von ID-Werten an einen URI. Im vorherigen Snippet wird withAppendedId()
verwendet, um dem Inhalts-URI des Anbieters des Nutzerwörterbuchs eine ID anzuhängen.
Daten vom Anbieter abrufen
In diesem Abschnitt wird beschrieben, wie Sie Daten von einem Anbieter abrufen. Als Beispiel dient der User Dictionary Provider.
Zur besseren Verständlichkeit wird in den Code-Snippets in diesem Abschnitt ContentResolver.query()
im UI-Thread aufgerufen. Im eigentlichen Code führen Sie Abfragen jedoch asynchron in einem separaten Thread durch. Sie können die Klasse CursorLoader
verwenden, die im Leitfaden
Lademechanismen ausführlicher beschrieben wird. Außerdem sind die Codezeilen nur Snippets. Sie zeigen keine vollständige Anwendung.
So rufen Sie Daten von einem Anbieter ab:
- Fordern Sie Lesezugriff für den Anbieter an.
- Definieren Sie den Code, mit dem eine Abfrage an den Anbieter gesendet wird.
Lesezugriff anfordern
Damit Ihre Anwendung Daten von einem Anbieter abrufen kann, benötigt sie Lesezugriffsberechtigungen für den Anbieter. Sie können diese Berechtigung nicht zur Laufzeit anfordern. Stattdessen müssen Sie in Ihrem Manifest angeben, dass Sie diese Berechtigung benötigen. Verwenden Sie dazu das Element <uses-permission>
und den genauen Namen der Berechtigung, der vom Anbieter definiert wurde.
Wenn Sie dieses Element in Ihrem Manifest angeben, beantragen Sie diese Berechtigung für Ihre Anwendung. Wenn Nutzer Ihre App installieren, erteilen sie dieser Anfrage implizit die Genehmigung.
Den genauen Namen der Lesezugriffsberechtigung für den von Ihnen verwendeten Anbieter sowie die Namen anderer vom Anbieter verwendeten Zugriffsberechtigungen finden Sie in der Dokumentation des Anbieters.
Die Rolle der Berechtigungen beim Zugriff auf Anbieter wird im Abschnitt Berechtigungen für Contentanbieter ausführlicher beschrieben.
Der User Dictionary Provider definiert die Berechtigung android.permission.READ_USER_DICTIONARY
in seiner Manifestdatei. Eine Anwendung, die Daten vom Anbieter lesen möchte, muss diese Berechtigung also anfordern.
Abfrage erstellen
Der nächste Schritt beim Abrufen von Daten von einem Anbieter besteht darin, eine Abfrage zu erstellen. Im folgenden Snippet werden einige Variablen für den Zugriff auf den Anbieter des Nutzerwörterbuchs definiert:
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 = {""};
Im nächsten Snippet wird gezeigt, wie ContentResolver.query()
verwendet wird. Dabei wird der Nutzerwörterbuchanbieter als Beispiel verwendet. Eine Abfrage des Anbieterclients ähnelt einer SQL-Abfrage und enthält eine Reihe von Spalten, die zurückgegeben werden sollen, eine Reihe von Auswahlkriterien und eine Sortierreihenfolge.
Die von der Abfrage zurückgegebenen Spalten werden als Projektion bezeichnet und die Variable ist mProjection
.
Der Ausdruck, der die abzurufenden Zeilen angibt, wird in eine Auswahlklausel und Auswahlargumente unterteilt. Die Auswahlklausel ist eine Kombination aus logischen und booleschen Ausdrücken, Spaltennamen und Werten. Die Variable ist mSelectionClause
. Wenn Sie anstelle eines Werts den austauschbaren Parameter ?
angeben, ruft die Abfragemethode den Wert aus dem Array der Auswahlargumente ab, also der Variablen mSelectionArgs
.
Wenn der Nutzer im nächsten Snippet kein Wort eingibt, wird die Auswahlklausel auf null
gesetzt und die Abfrage gibt alle Wörter im Anbieter zurück. Wenn der Nutzer ein Wort eingibt, wird die Auswahlklausel auf UserDictionary.Words.WORD + " = ?"
und das erste Element des Arrays „selection arguments“ auf das Wort festgelegt, das der Nutzer eingibt.
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 }
Diese Abfrage entspricht der folgenden SQL-Anweisung:
SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
In dieser SQL-Anweisung werden die tatsächlichen Spaltennamen anstelle von Konstanten der Vertragsklasse verwendet.
Schutz vor schädlichen Eingaben
Wenn sich die vom Contentanbieter verwalteten Daten in einer SQL-Datenbank befinden, kann das Einschleusen externer nicht vertrauenswürdiger Daten in rohe SQL-Anweisungen zu einer SQL-Injection führen.
Betrachten Sie die folgende Auswahlklausel:
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;
Dadurch können Nutzer möglicherweise schädliche SQL-Codezeilen in Ihre SQL-Anweisung einfügen.
Der Nutzer kann beispielsweise „nothing; DROP TABLE *;“ für mUserInput
eingeben, was zur Auswahlklausel var = nothing; DROP TABLE *;
führt.
Da die Auswahlklausel als SQL-Anweisung behandelt wird, kann der Anbieter alle Tabellen in der zugrunde liegenden SQLite-Datenbank löschen, es sei denn, der Anbieter ist so eingerichtet, dass SQL-Injection-Versuche abgefangen werden.
Verwenden Sie zur Vermeidung dieses Problems eine Auswahlklausel mit ?
als austauschbarem Parameter und einem separaten Array von Auswahlargumenten. Auf diese Weise wird die Nutzereingabe direkt an die Abfrage gebunden, anstatt als Teil einer SQL-Anweisung interpretiert zu werden.
Da die Nutzereingaben nicht als SQL behandelt werden, kann über sie kein schädliches SQL-Code eingeschleust werden. Verwenden Sie anstelle der Zusammenführung, um die Nutzereingabe einzubeziehen, diese Auswahlklausel:
Kotlin
// Constructs a selection clause with a replaceable parameter var selectionClause = "var = ?"
Java
// Constructs a selection clause with a replaceable parameter String selectionClause = "var = ?";
Richten Sie das Array von Auswahlargumenten so ein:
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 = {""};
Geben Sie einen Wert in das Array für Auswahlargumente ein:
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;
Eine Auswahlklausel, die ?
als austauschbaren Parameter und ein Array mit Auswahlargumenten verwendet, ist die bevorzugte Methode, um eine Auswahl anzugeben, auch wenn der Anbieter nicht auf einer SQL-Datenbank basiert.
Abfrageergebnisse anzeigen
Die Clientmethode ContentResolver.query()
gibt immer einen Cursor
zurück, der die Spalten enthält, die in der Projektion der Abfrage für die Zeilen angegeben sind, die den Auswahlkriterien der Abfrage entsprechen. Ein Cursor
-Objekt bietet zufälligen Lesezugriff auf die darin enthaltenen Zeilen und Spalten.
Mit Cursor
-Methoden können Sie die Zeilen in den Ergebnissen durchgehen, den Datentyp jeder Spalte ermitteln, die Daten aus einer Spalte abrufen und andere Eigenschaften der Ergebnisse prüfen.
Bei einigen Cursor
-Implementierungen wird das Objekt automatisch aktualisiert, wenn sich die Daten des Anbieters ändern, Methoden in einem Beobachterobjekt ausgelöst werden, wenn sich die Cursor
ändert, oder beides.
Hinweis:Ein Anbieter kann den Zugriff auf Spalten basierend auf der Art des Objekts einschränken, das die Abfrage ausführt. Beispielsweise schränkt der Kontaktdatenanbieter den Zugriff auf einige Spalten auf Synchronadapter ein, sodass sie nicht an eine Aktivität oder einen Dienst zurückgegeben werden.
Wenn keine Zeilen den Auswahlkriterien entsprechen, gibt der Anbieter ein Cursor
-Objekt zurück, für das Cursor.getCount()
0 ist, also ein leerer Cursor.
Bei einem internen Fehler hängen die Ergebnisse der Abfrage vom jeweiligen Anbieter ab. Es kann null
zurückgeben oder eine Exception
auslösen.
Da ein Cursor
eine Liste von Zeilen ist, können Sie den Inhalt eines Cursor
gut mithilfe eines SimpleCursorAdapter
mit einem ListView
verknüpfen.
Im folgenden Snippet wird der Code aus dem vorherigen Snippet fortgesetzt. Es wird ein SimpleCursorAdapter
-Objekt mit dem Cursor
erstellt, das durch die Abfrage abgerufen wurde, und dieses Objekt wird als Adapter für eine ListView
festgelegt.
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);
Hinweis: Um ein ListView
mit einem Cursor
zu stützen, muss der Cursor eine Spalte mit dem Namen _ID
enthalten.
Daher wird in der oben gezeigten Abfrage die Spalte _ID
für die Tabelle Words
abgerufen, obwohl sie in ListView
nicht angezeigt wird.
Diese Einschränkung erklärt auch, warum die meisten Anbieter für jede ihrer Tabellen eine _ID
-Spalte haben.
Daten aus Abfrageergebnissen abrufen
Sie können sie nicht nur für Abfrageergebnisse, sondern auch für andere Aufgaben verwenden. Sie können beispielsweise Rechtschreibvarianten vom Anbieter des Nutzerwörterbuchs abrufen und dann bei anderen Anbietern nachsehen. Dazu iterieren Sie über die Zeilen in Cursor
, wie im folgenden Beispiel gezeigt:
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
-Implementierungen enthalten mehrere „get“-Methoden zum Abrufen verschiedener Datentypen aus dem Objekt. Im vorherigen Snippet wird beispielsweise getString()
verwendet. Außerdem gibt es die Methode getType()
, die einen Wert zurückgibt, der den Datentyp der Spalte angibt.
Ergebnisressourcen der Releaseabfrage
Cursor
-Objekte müssen geschlossen werden, wenn sie nicht mehr benötigt werden, damit die zugehörigen Ressourcen schneller freigegeben werden. Dies kann entweder durch Aufrufen von close()
oder durch Verwendung einer try-with-resources
-Anweisung in der Java-Programmiersprache oder der use()
-Funktion in der Kotlin-Programmiersprache erfolgen.
Berechtigungen von Contentanbietern
Die Anwendung eines Anbieters kann Berechtigungen festlegen, die andere Anwendungen benötigen, um auf die Daten des Anbieters zuzugreifen. Anhand dieser Berechtigungen kann der Nutzer sehen, auf welche Daten eine Anwendung zugreifen möchte. Je nach den Anforderungen des Anbieters fordern andere Anwendungen die Berechtigungen an, die sie für den Zugriff auf den Anbieter benötigen. Endnutzer sehen die angeforderten Berechtigungen, wenn sie die Anwendung installieren.
Wenn die Anwendung eines Anbieters keine Berechtigungen angibt, haben andere Anwendungen keinen Zugriff auf die Daten des Anbieters, es sei denn, der Anbieter wird exportiert. Außerdem haben Komponenten in der Anwendung des Anbieters unabhängig von den angegebenen Berechtigungen immer vollen Lese- und Schreibzugriff.
Der Anbieter des Nutzerwörterbuchs benötigt die Berechtigung android.permission.READ_USER_DICTIONARY
, um Daten daraus abzurufen.
Der Anbieter hat eine separate android.permission.WRITE_USER_DICTIONARY
-Berechtigung zum Einfügen, Aktualisieren oder Löschen von Daten.
Eine Anwendung fordert die erforderlichen Berechtigungen für den Zugriff auf einen Anbieter mit einem <uses-permission>
-Element in der Manifestdatei an. Wenn der Android-Paketmanager die Anwendung installiert, muss der Nutzer alle von der Anwendung angeforderten Berechtigungen genehmigen. Wenn der Nutzer sie genehmigt, fährt der Paketmanager mit der Installation fort. Wenn der Nutzer sie nicht genehmigt, beendet der Paketmanager die Installation.
Im folgenden Beispielelement <uses-permission>
fordert der Nutzerwörterbuchanbieter Lesezugriff an:
<uses-permission android:name="android.permission.READ_USER_DICTIONARY">
Die Auswirkungen von Berechtigungen auf den Zugriff von Anbietern werden in diesen Sicherheitstipps ausführlicher erläutert.
Daten einfügen, aktualisieren und löschen
Genauso wie Sie Daten von einem Anbieter abrufen, verwenden Sie auch die Interaktion zwischen einem Anbieterclient und der ContentProvider
des Anbieters, um Daten zu ändern.
Sie rufen eine Methode von ContentResolver
mit Argumenten auf, die an die entsprechende Methode von ContentProvider
übergeben werden. Der Anbieter und der Providerclient übernehmen automatisch die Sicherheits- und Interprozesskommunikation.
Daten einfügen
Wenn Sie Daten in einen Anbieter einfügen möchten, rufen Sie die Methode ContentResolver.insert()
auf. Mit dieser Methode wird eine neue Zeile in den Anbieter eingefügt und ein Inhalts-URI für diese Zeile zurückgegeben.
Das folgende Snippet zeigt, wie ein neues Wort in den Anbieter des Nutzerwörterbuchs eingefügt wird:
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 );
Die Daten für die neue Zeile werden in ein einzelnes ContentValues
-Objekt aufgenommen, das in seiner Form einem Cursor mit einer Zeile ähnelt. Die Spalten in diesem Objekt müssen nicht denselben Datentyp haben. Wenn Sie keinen Wert angeben möchten, können Sie eine Spalte mit ContentValues.putNull()
auf null
setzen.
Im vorherigen Snippet wird die Spalte _ID
nicht hinzugefügt, da sie automatisch verwaltet wird. Der Anbieter weist jeder hinzugefügten Zeile den eindeutigen Wert _ID
zu. Anbieter verwenden diesen Wert normalerweise als Primärschlüssel der Tabelle.
Der in newUri
zurückgegebene Inhalts-URI identifiziert die neu hinzugefügte Zeile im folgenden Format:
content://user_dictionary/words/<id_value>
<id_value>
ist der Inhalt von _ID
für die neue Zeile.
Die meisten Anbieter können diese Form von Content-URI automatisch erkennen und dann den angeforderten Vorgang auf diese bestimmte Zeile ausführen.
Rufen Sie ContentUris.parseId()
auf, um den Wert von _ID
aus dem zurückgegebenen Uri
abzurufen.
Daten aktualisieren
Um eine Zeile zu aktualisieren, verwenden Sie ein ContentValues
-Objekt mit den aktualisierten Werten, genau wie bei einer Einfügung und Auswahlkriterien wie bei einer Abfrage.
Sie verwenden die Clientmethode ContentResolver.update()
. Sie müssen dem ContentValues
-Objekt nur Werte für die Spalten hinzufügen, die Sie aktualisieren. Wenn Sie den Inhalt einer Spalte löschen möchten, setzen Sie den Wert auf null
.
Im folgenden Snippet wird das Gebietsschema aller Zeilen, deren Sprache "en"
ist, in null
geändert. Der Rückgabewert ist die Anzahl der aktualisierten Zeilen.
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 );
Bereinigen Sie die Nutzereingabe, wenn Sie ContentResolver.update()
aufrufen. Weitere Informationen finden Sie im Abschnitt Schutz vor schädlichen Eingaben.
Daten löschen
Das Löschen von Zeilen ähnelt dem Abrufen von Zeilendaten. Sie geben Auswahlkriterien für die Zeilen an, die Sie löschen möchten, und die Clientmethode gibt die Anzahl der gelöschten Zeilen zurück.
Im folgenden Snippet werden Zeilen gelöscht, deren App-ID mit "user"
übereinstimmt. Die Methode gibt die Anzahl der gelöschten Zeilen zurück.
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 );
Nutzereingabe beim Aufrufen von ContentResolver.delete()
bereinigen Weitere Informationen finden Sie im Abschnitt Vor schädlichen Eingaben schützen.
Datentypen des Anbieters
Contentanbieter können viele verschiedene Datentypen anbieten. Der Anbieter des Nutzerwörterbuchs bietet nur Text an, aber Anbieter können auch die folgenden Formate anbieten:
- Ganzzahl
- Ganzzahl (long)
- Gleitkommawert
- Gleitkomma mit doppelter Genauigkeit (long)
Ein weiterer Datentyp, den Anbieter häufig verwenden, ist ein BLOB (Binary Large Object), das als 64-KB-Byte-Array implementiert ist. Die verfügbaren Datentypen finden Sie in den „get“-Methoden der Klasse Cursor
.
Der Datentyp für jede Spalte eines Anbieters ist in der Regel in der Dokumentation aufgeführt.
Die Datentypen für den Anbieter des Nutzerwörterbuchs sind in der Referenzdokumentation für die Vertragsklasse UserDictionary.Words
aufgeführt. Contract-Klassen werden im Abschnitt Contract-Klassen beschrieben.
Sie können den Datentyp auch durch Aufrufen von Cursor.getType()
ermitteln.
Anbieter speichern auch MIME-Datentypinformationen für jeden von ihnen definierten Inhalts-URI. Anhand der Informationen zum MIME-Typ können Sie herausfinden, ob Ihre Anwendung Daten verarbeiten kann, die der Anbieter anbietet, oder eine Art der Verarbeitung basierend auf dem MIME-Typ auswählen. Sie benötigen den MIME-Typ in der Regel, wenn Sie mit einem Anbieter arbeiten, der komplexe Datenstrukturen oder Dateien enthält.
In der Tabelle ContactsContract.Data
im Kontaktdatenanbieter werden beispielsweise MIME-Typen verwendet, um den Typ der Kontaktdaten zu kennzeichnen, die in jeder Zeile gespeichert sind. Rufe ContentResolver.getType()
auf, um den MIME-Typ abzurufen, der einem Inhalts-URI entspricht.
Im Abschnitt MIME-Typreferenz wird die Syntax sowohl für Standard- als auch für benutzerdefinierte MIME-Typen beschrieben.
Alternative Formen des Anbieterzugriffs
Bei der Anwendungsentwicklung sind drei alternative Formen des Anbieterzugriffs wichtig:
-
Batchzugriff: Sie können mithilfe von Methoden in der Klasse
ContentProviderOperation
einen Batch von Zugriffsaufrufen erstellen und dann mitContentResolver.applyBatch()
anwenden. -
Asynchrone Abfragen: Führen Sie Abfragen in einem separaten Thread durch. Sie können ein
CursorLoader
-Objekt verwenden. Die Beispiele im Leitfaden zu Ladeprogrammen veranschaulichen dies. - Datenzugriff über Intents: Sie können zwar keinen Intent direkt an einen Anbieter senden, aber an die Anwendung des Anbieters, die in der Regel am besten für die Änderung der Daten des Anbieters geeignet ist.
Der Batchzugriff und die Batchänderung mithilfe von Intents werden in den folgenden Abschnitten beschrieben.
Batchzugriff
Der Batchzugriff auf einen Anbieter ist nützlich, um eine große Anzahl von Zeilen einzufügen, Zeilen in mehreren Tabellen innerhalb desselben Methodenaufrufs einzufügen und im Allgemeinen eine Reihe von Vorgängen über Prozessgrenzen hinweg als Transaktion auszuführen. Dieser Vorgang wird als atomarer Vorgang bezeichnet.
Wenn du im Batchmodus auf einen Anbieter zugreifen möchtest, musst du ein Array von ContentProviderOperation
-Objekten erstellen und sie dann mit ContentResolver.applyBatch()
an einen Contentanbieter senden. Sie übergeben die Zertifizierungsstelle des Contentanbieters anstelle eines bestimmten Inhalts-URI an diese Methode.
So kann jedes ContentProviderOperation
-Objekt im Array mit einer anderen Tabelle verwendet werden. Ein Aufruf von ContentResolver.applyBatch()
gibt ein Array von Ergebnissen zurück.
Die Beschreibung der ContactsContract.RawContacts
-Vertragsklasse enthält ein Code-Snippet, das die Batch-Einfügung veranschaulicht.
Datenzugriff mit Intents
Intents können indirekten Zugriff auf einen Inhaltsanbieter bieten. Sie können den Nutzer auch dann auf Daten bei einem Anbieter zugreifen lassen, wenn Ihre App keine Zugriffsberechtigungen hat. Dazu können Sie entweder eine Ergebnisabsicht von einer App zurückgeben lassen, die Berechtigungen hat, oder eine App mit Berechtigungen aktivieren und den Nutzer darin arbeiten lassen.
Zugriff mit temporären Berechtigungen erhalten
Sie können auf Daten in einem Inhaltsanbieter zugreifen, auch wenn Sie nicht über die erforderlichen Zugriffsberechtigungen verfügen. Dazu senden Sie eine Intent an eine Anwendung, die die Berechtigungen hat, und erhalten eine Ergebnis-Intent mit URI-Berechtigungen zurück. Dies sind Berechtigungen für einen bestimmten Inhalts-URI, die bis zum Ende der Aktivität gültig sind, die sie empfängt. Die Anwendung mit dauerhaften Berechtigungen gewährt temporäre Berechtigungen, indem sie ein Flag in der Ergebnisabsicht festlegt:
-
Leseberechtigung:
FLAG_GRANT_READ_URI_PERMISSION
-
Schreibberechtigung:
FLAG_GRANT_WRITE_URI_PERMISSION
Hinweis:Diese Flags gewähren dem Anbieter, dessen Berechtigung im Inhalts-URI liegt, keinen allgemeinen Lese- oder Schreibzugriff. Der Zugriff gilt nur für den URI selbst.
Wenn Sie Inhalts-URIs an eine andere App senden, müssen Sie mindestens eines dieser Flags angeben. Die Flags bieten allen Apps, die eine Intent-Nachricht empfangen und auf Android 11 (API-Level 30) oder höher ausgerichtet sind, die folgenden Funktionen:
- Lesen oder Schreiben von Daten, die durch den Inhalts-URI dargestellt werden, je nach Flag in der Absicht.
- Sie erhalten Paketsichtbarkeit in der App, die den Inhaltsanbieter enthält, der mit der URI-Autorität übereinstimmt. Die App, die den Intent sendet, und die App, die den Contentanbieter enthält, können zwei verschiedene Apps sein.
Ein Anbieter definiert URI-Berechtigungen für Inhalts-URIs in seinem Manifest. Dazu werden das Attribut android:grantUriPermissions
des <provider>
-Elements und das untergeordnete <grant-uri-permission>
-Element des <provider>
-Elements verwendet. Der URI-Berechtigungsmechanismus wird unter Berechtigungen unter Android ausführlicher erläutert.
So können Sie beispielsweise Daten für einen Kontakt im Contacts Provider abrufen, auch wenn Sie nicht die Berechtigung READ_CONTACTS
haben. Das kann beispielsweise in einer Anwendung der Fall sein, die an einem Kontakts Geburtstag eine E-Mail-Grußkarte sendet. Anstatt READ_CONTACTS
anzufordern, wodurch Sie Zugriff auf alle Kontakte und Informationen des Nutzers erhalten, lassen Sie den Nutzer festlegen, welche Kontakte Ihre App verwendet. Gehen Sie dazu so vor:
-
Senden Sie in Ihrer Anwendung mit der Methode
startActivityForResult()
einen Intent mit der AktionACTION_PICK
und dem MIME-Typ „Kontakte“CONTENT_ITEM_TYPE
. - Da dieser Intent mit dem Intent-Filter für die Aktivität „Auswahl“ der Kontakte-App übereinstimmt, wird die Aktivität in den Vordergrund gestellt.
-
In der Auswahlaktivität wählt der Nutzer einen Kontakt zum Aktualisieren aus. In diesem Fall ruft die Auswahlaktivität
setResult(resultcode, intent)
auf, um eine Intent-Funktion einzurichten, die etwas für Ihre Anwendung zurückgibt. Der Intent enthält den Inhalts-URI des vom Nutzer ausgewählten Kontakts und die Flags „Extras“FLAG_GRANT_READ_URI_PERMISSION
. Diese Flags gewähren Ihrer App die URI-Berechtigung zum Lesen von Daten für den Kontakt, auf den der Inhalts-URI verweist. Die Auswahlaktivität ruft dannfinish()
auf, um die Steuerung an die Anwendung zurückzugeben. -
Ihre Aktivität kehrt in den Vordergrund zurück und das System ruft die Methode
onActivityResult()
Ihrer Aktivität auf. Diese Methode empfängt den Ergebnis-Intent, der von der Auswahlaktivität in der Personen-App erstellt wurde. - Mit dem Inhalts-URI aus dem Ergebnis-Intent können Sie die Daten des Kontakts aus dem Contacts Provider lesen, obwohl Sie in Ihrem Manifest keine dauerhafte Lesezugriffsberechtigung an den Anbieter angefordert haben. Sie können dann die Geburtstagsinformationen oder die E-Mail-Adresse des Kontakts abrufen und die E-Mail-Grüße senden.
Andere App verwenden
Eine andere Möglichkeit, dem Nutzer die Möglichkeit zu geben, Daten zu ändern, für die Sie keine Zugriffsberechtigungen haben, besteht darin, eine Anwendung zu aktivieren, die Berechtigungen hat, und der Nutzer die Arbeit dort erledigen zu lassen.
Die Kalenderanwendung akzeptiert beispielsweise einen ACTION_INSERT
-Intent, mit dem Sie die Einfüge-UI der Anwendung aktivieren können. Sie können in diesem Intent zusätzliche Daten übergeben, mit denen die Anwendung die UI vorab füllt. Da wiederkehrende Termine eine komplexe Syntax haben, besteht die bevorzugte Methode zum Einfügen von Terminen in den Kalenderanbieter darin, die Kalender-App mit einem ACTION_INSERT
zu aktivieren und dem Nutzer die Möglichkeit zu geben, den Termin dort einzufügen.
Daten mit einer Hilfs-App anzeigen
Wenn Ihre Anwendung Zugriffsberechtigungen hat, können Sie einen Intent möglicherweise trotzdem zum Anzeigen von Daten in einer anderen Anwendung verwenden. Die Kalender-App akzeptiert beispielsweise eine ACTION_VIEW
-Intent, die ein bestimmtes Datum oder Ereignis anzeigt.
So können Sie Kalenderinformationen anzeigen, ohne eine eigene Benutzeroberfläche erstellen zu müssen.
Weitere Informationen zu dieser Funktion finden Sie in der Übersicht über Kalenderanbieter.
Die Anwendung, an die Sie die Intent senden, muss nicht mit dem Anbieter verknüpft sein. Sie können beispielsweise einen Kontakt vom Kontaktdatenanbieter abrufen und dann eine ACTION_VIEW
-Intent mit dem Inhalts-URI für das Bild des Kontakts an eine Bildanzeige senden.
Vertragsklassen
Eine Vertragsklasse definiert Konstanten, die Anwendungen bei der Arbeit mit den Inhalts-URIs, Spaltennamen, Intent-Aktionen und anderen Funktionen eines Inhaltsanbieters unterstützen. Vertragsklassen sind nicht automatisch bei einem Anbieter enthalten. Der Entwickler des Anbieters muss sie definieren und dann für andere Entwickler verfügbar machen. Viele der in der Android-Plattform enthaltenen Anbieter haben entsprechende Vertragsklassen im Paket android.provider
.
Der Anbieter des Nutzerwörterbuchs hat beispielsweise eine Vertragsklasse UserDictionary
mit Konstanten für Inhalts-URIs und Spaltennamen. Der Inhalts-URI für die Tabelle Words
ist in der Konstante UserDictionary.Words.CONTENT_URI
definiert.
Die Klasse UserDictionary.Words
enthält auch Konstanten für Spaltennamen, die in den Beispiel-Snippets in diesem Leitfaden verwendet werden. Eine Abfrageprojektion kann beispielsweise so definiert werden:
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 };
Eine weitere Vertragsklasse ist ContactsContract
für den Kontaktanbieter.
Die Referenzdokumentation für diese Klasse enthält Beispiel-Code-Snippets. Eine ihrer Unterklassen, ContactsContract.Intents.Insert
, ist eine Vertragsklasse, die Konstanten für Intents und Intent-Daten enthält.
MIME-Typ-Referenz
Contentanbieter können Standard-MIME-Medientypen, benutzerdefinierte MIME-Typ-Strings oder beides zurückgeben.
MIME-Typen haben folgendes Format:
type/subtype
Der bekannte MIME-Typ text/html
hat beispielsweise den Typ text
und den Untertyp html
. Wenn der Anbieter diesen Typ für einen URI zurückgibt, gibt eine Abfrage mit diesem URI Text zurück, der HTML-Tags enthält.
Strings für benutzerdefinierte MIME-Typen, auch anbieterspezifische MIME-Typen genannt, haben komplexere type- und subtype-Werte. Bei mehreren Zeilen ist der Typwert immer:
vnd.android.cursor.dir
Für eine einzelne Zeile lautet der Typwert immer:
vnd.android.cursor.item
Die subtype ist anbieterspezifisch. Die in Android integrierten Anbieter haben in der Regel einen einfachen Untertyp. Wenn die Kontakte-Anwendung beispielsweise eine Zeile für eine Telefonnummer erstellt, wird in der Zeile der folgende MIME-Typ festgelegt:
vnd.android.cursor.item/phone_v2
Der Wert des Untertyps ist phone_v2
.
Andere Entwickler von Anbietern können basierend auf der Befugnis des Anbieters und den Tabellennamen eigene Untertypen erstellen. Angenommen, ein Anbieter stellt Zugfahrpläne bereit.
Die Autorität des Anbieters ist com.example.trains
und enthält die Tabellen „Line1“, „Line2“ und „Line3“. Als Antwort auf den folgenden Inhalts-URI für die Tabelle „Line1“:
content://com.example.trains/Line1
gibt der Anbieter den folgenden MIME-Typ zurück:
vnd.android.cursor.dir/vnd.example.line1
In Antwort auf den folgenden Inhalts-URI für Zeile 5 in Tabelle „Line2“:
content://com.example.trains/Line2/5
Der Anbieter gibt den folgenden MIME-Typ zurück:
vnd.android.cursor.item/vnd.example.line2
Die meisten Contentanbieter definieren Vertragsklassenkonstanten für die von ihnen verwendeten MIME-Typen. Die Vertragsklasse ContactsContract.RawContacts
des Kontaktdatenanbieters definiert beispielsweise die Konstante CONTENT_ITEM_TYPE
für den MIME-Typ einer einzelnen Zeile mit Kontakten im Rohformat.
Content-URIs für einzelne Zeilen werden im Abschnitt Content-URIs beschrieben.