Ein Contentanbieter verwaltet den Zugriff auf ein zentrales Repository mit Daten. Sie implementieren eine
als eine oder mehrere Klassen in einer Android-App zusammen mit Elementen in
der Manifestdatei. Eine Ihrer Klassen implementiert die abgeleitete Klasse von
ContentProvider
, die Schnittstelle zwischen Ihrem Anbieter und
andere Anwendungen.
Obwohl Contentanbieter Daten für andere können Sie Aktivitäten in Ihrer Anwendung anbieten, mit denen Nutzer von Ihrem Anbieter verwaltete Daten abfragen und ändern.
Diese Seite enthält den grundlegenden Prozess zum Erstellen eines Contentanbieters und eine Liste. der zu verwendenden APIs.
Bevor Sie mit dem Erstellen
Bevor Sie mit der Erstellung eines Anbieters beginnen, sollten Sie Folgendes berücksichtigen:
-
Entscheiden Sie, ob Sie einen Contentanbieter benötigen. Sie müssen Inhalte erstellen,
wenn Sie eine oder mehrere der folgenden Funktionen anbieten möchten:
<ph type="x-smartling-placeholder">
- </ph>
- Sie möchten anderen Anwendungen komplexe Daten oder Dateien zur Verfügung stellen.
- Sie möchten es Nutzern ermöglichen, komplexe Daten aus Ihrer App in andere Apps zu kopieren.
- Sie möchten mithilfe des Such-Frameworks benutzerdefinierte Suchvorschläge bereitstellen.
- Sie möchten Ihre Anwendungsdaten für Widgets freigeben.
- Sie möchten die
AbstractThreadedSyncAdapter
implementieren,CursorAdapter
oderCursorLoader
Klassen.
Sie benötigen keinen Anbieter, um Datenbanken oder andere Arten von nichtflüchtiger Speicher, wenn die Verwendung ausschließlich durch Ihre eigene Anwendung erfolgt und Sie benötigen keine der oben aufgeführten Funktionen. Stattdessen können Sie eines der in diesem Artikel Daten- und Dateispeicher – Übersicht
- Falls Sie dies noch nicht getan haben, lesen Sie <ph type="x-smartling-placeholder"></ph> Grundlagen zu Contentanbietern, um mehr über Anbieter und ihre Funktionsweise zu erfahren.
Führen Sie als Nächstes die folgenden Schritte aus, um Ihren Anbieter zu erstellen:
-
Entwerfen Sie den Rohdatenspeicher für Ihre Daten. Ein Contentanbieter stellt Daten auf zwei Arten zur Verfügung:
<ph type="x-smartling-placeholder">
- </ph>
- Dateidaten
- Daten, die normalerweise in Dateien gespeichert werden, z. B. Fotos, Audiodateien oder Videos. Dateien im privaten Ordner Ihrer Anwendung speichern Leerzeichen. Als Antwort auf die Anforderung einer Datei aus einer anderen Anwendung einen Handle für die Datei anbieten kann.
- „Strukturiert“ Daten
- Daten, die normalerweise in eine Datenbank, ein Array oder eine ähnliche Struktur einfließen. Speichern Sie die Daten in einem Formular, das mit Tabellen mit Zeilen und Spalten kompatibel ist. A-Reihe steht für eine Entität, z. B. eine Person oder einen Artikel im Inventar. Eine Spalte steht für einige Daten zur Entität, z. B. den Namen einer Person oder den Preis eines Artikels. Eine gängige Methode, können Sie diese Art von Daten in einer SQLite-Datenbank speichern, nichtflüchtigem Speicher. Weitere Informationen zu den verfügbaren Speichertypen finden Sie in der Android-System finden Sie in der Design-Datenspeicherung.
-
Definieren Sie eine konkrete Implementierung der
ContentProvider
-Klasse und ihre erforderlichen Methoden. Diese Klasse ist die Schnittstelle zwischen Ihren Daten und dem Rest der Android-System. Weitere Informationen zu diesem Kurs finden Sie in der Implementieren Sie den Abschnitt "ContentProvider-Klasse". - Definieren Sie den Autorisierungsstring des Anbieters, die Inhalts-URIs und die Spaltennamen. Bei Bedarf die Anwendung des Anbieters zur Verarbeitung von Intents, Definition von Intent-Aktionen, und Markierungen. Definieren Sie außerdem die Berechtigungen, die Sie für Anwendungen benötigen, um auf Ihre Daten zuzugreifen. Definieren Sie all diese Werte als Konstanten in einem separate Contract-Klasse. Später können Sie diese Klasse für andere Entwickler freigeben. Weitere Informationen Informationen zu Inhalts-URIs finden Sie in der Abschnitt Designinhalts-URIs. Weitere Informationen zu Intents finden Sie in der Abschnitt Intents und Datenzugriff.
-
Fügen Sie weitere optionale Informationen hinzu, z. B. Beispieldaten oder eine Implementierung
von
AbstractThreadedSyncAdapter
, die Daten synchronisieren können zwischen den Anbieter und cloudbasierte Daten.
Datenspeicherung entwerfen
Ein Contentanbieter ist die Schnittstelle zu Daten, die in einem strukturierten Format gespeichert sind. Vor dem Erstellen über die Schnittstelle entscheiden, wie die Daten gespeichert werden sollen. Sie können die Daten in jeder beliebigen Form speichern, und dann die Schnittstelle so gestalten, dass die Daten nach Bedarf gelesen und geschrieben werden.
Für Android stehen unter anderem die folgenden Datenspeichertechnologien zur Verfügung:
- Wenn Sie mit strukturierten Daten arbeiten, sollten Sie entweder eine relationale Datenbank wie wie SQLite oder ein nicht relationaler Datenspeicher für Schlüssel/Wert-Paare wie LevelDB: Wenn Sie mit unstrukturierten Daten wie Audio-, Bild- oder Videomedien, Daten als Dateien. Sie können verschiedene Speichertypen mischen und bereitstellen. wenn nötig nur einen Contentanbieter nutzen.
-
Das Android-System kann mit der Raumpersistenzbibliothek interagieren,
bietet Zugriff auf die SQLite-Datenbank-API der Android-Anbieter
zum Speichern tabellenorientierter Daten. Zum Erstellen einer Datenbank mit diesem
eine abgeleitete Klasse von
<ph type="x-smartling-placeholder"></ph>
RoomDatabase
, wie in Daten in einer lokalen Datenbank mithilfe von „Room“ speichern.Sie müssen Ihr Repository nicht mit einer Datenbank implementieren. Ein Anbieter werden extern als ein Satz von Tabellen angezeigt, ähnlich wie bei einer relationalen Datenbank, keine Voraussetzung für die interne Implementierung des Anbieters.
- Zum Speichern von Dateidaten bietet Android eine Vielzahl von dateiorientierten APIs. Weitere Informationen zum Dateispeicher finden Sie in der Daten- und Dateispeicher – Übersicht Wenn Sie Wenn Sie einen Anbieter entwerfen, der medienbezogene Daten wie Musik oder Videos anbietet, können Sie einen Anbieter haben, der Tabellendaten und Dateien kombiniert.
- In seltenen Fällen kann es für Sie sinnvoll sein, mehr als einen Contentanbieter für auf eine einzige Anwendung. So können Sie z. B. einige Daten mithilfe von einem Contentanbieter bereitgestellt und andere Anwendungen.
-
Verwenden Sie für die Arbeit mit netzwerkbasierten Daten Klassen in
java.net
undandroid.net
. Netzwerkbasierte Daten lassen sich auch mit lokalen Daten synchronisieren. z. B. eine Datenbank, und bieten die Daten dann als Tabellen oder Dateien an.
Hinweis: Wenn Sie eine Änderung an Ihrem Repository vornehmen, abwärtskompatibel ist, müssen Sie das Repository mit einer neuen Version Nummer. Sie müssen auch die Versionsnummer Ihrer App erhöhen, implementiert den neuen Contentanbieter. Durch diese Änderung wird das System ein System abstürzt, wenn versucht wird, eine App mit einem inkompatiblen Contentanbieter
Überlegungen zum Datendesign
Hier sind einige Tipps zum Entwerfen der Datenstruktur Ihres Anbieters:
-
Tabellendaten müssen immer einen „Primärschlüssel“ haben Spalte, die vom Anbieter verwaltet wird
als eindeutigen numerischen Wert für jede Zeile. Sie können diesen Wert verwenden, um die Zeile mit zugehörigen
in anderen Tabellen und verwenden diesen als Fremdschlüssel. Auch wenn Sie jeden beliebigen Namen
Für diese Spalte empfiehlt sich die Verwendung von
BaseColumns._ID
da die Verknüpfung der Ergebnisse einer Anbieterabfrage mit einem FürListView
muss eine der abgerufenen Spalten den Namen haben_ID
-
Wenn Sie Bitmapbilder oder andere sehr große dateiorientierte Daten bereitstellen möchten, speichern Sie
und stellen sie indirekt zur Verfügung, anstatt sie direkt in einem
. In diesem Fall müssen Sie die Nutzer Ihres Anbieters darüber informieren, dass sie ein
ContentResolver
-Dateimethode für den Zugriff auf die Daten. -
Verwenden Sie den Datentyp Binary Large Object (BLOB), um Daten zu speichern, die in ihrer Größe variieren oder
unterschiedlichen Strukturen. Sie können beispielsweise eine BLOB-Spalte verwenden, um ein
Protokollzwischenspeicher oder
JSON-Struktur.
Mit einem BLOB können Sie auch eine schemaunabhängige Tabelle implementieren. In definieren Sie eine Primärschlüsselspalte, eine Spalte für den MIME-Typ und ein oder Spalten als BLOB. Die Bedeutung der Daten in den BLOB-Spalten durch den Wert in der Spalte MIME-Typ. So können Sie verschiedene Zeilentypen in derselben Tabelle. Die „Daten“ des Contacts Providers Tabelle
ContactsContract.Data
ist ein Beispiel für eine schemaunabhängige .
Inhalts-URIs entwerfen
Ein Inhalts-URI ist ein URI, der Daten bei einem Anbieter identifiziert. Inhalts-URIs umfassen
den symbolischen Namen des gesamten Anbieters (seine Zertifizierungsstelle) und ein
der auf eine Tabelle oder Datei verweist (ein Pfad). Der optionale ID-Teil verweist auf
einzelne Zeile in einer Tabelle. Jede Datenzugriffsmethode
ContentProvider
hat einen Inhalts-URI als Argument. So können Sie
um die Tabelle, Zeile oder Datei zu bestimmen, auf die zugegriffen werden soll.
Informationen zu Inhalts-URIs finden Sie unter <ph type="x-smartling-placeholder"></ph> Grundlagen des Contentanbieters
Eine Behörde entwerfen
Ein Anbieter hat in der Regel eine einzige Behörde, die als Android-interner Name dient. Bis Konflikte mit anderen Anbietern vermeiden, Internetdomain-Inhaberschaft verwenden (umgekehrt) als Grundlage für Ihre Anbieterbefugnis. Da diese Empfehlung auch für Android Paketnamen können Sie Ihre Anbieterbefugnis als Erweiterung des Namens definieren des Pakets, das den Anbieter enthält.
Lautet der Name Ihres Android-Pakets beispielsweise
com.example.<appname>
, geben Sie Ihrem Anbieter die
Zertifizierungsstelle com.example.<appname>.provider
.
Eine Pfadstruktur entwerfen
Entwickler erstellen in der Regel Inhalts-URIs über die Zertifizierungsstelle, indem sie Pfade anhängen, die auf
einzelne Tabellen erstellen. Beispiel: Wenn Sie zwei Tabellen haben, table1 und
table2 können Sie diese mit den Befugnissen aus dem vorherigen Beispiel kombinieren, um
Inhalts-URIs
com.example.<appname>.provider/table1
und
com.example.<appname>.provider/table2
. Pfade sind nicht
ist auf ein einzelnes Segment beschränkt und es muss nicht für jede Pfadebene eine Tabelle vorhanden sein.
Inhalts-URI-IDs verarbeiten
Konventionsgemäß bieten Anbieter Zugriff auf eine einzelne Zeile in einer Tabelle an, indem sie einen Inhalts-URI akzeptieren
durch einen ID-Wert für die Zeile am Ende des URI. Konventionsgemäß erfüllen Anbieter die
ID-Wert in die Spalte _ID
der Tabelle ein und führen Sie den angeforderten Zugriff für die
Zeile, die übereinstimmt.
Diese Konvention ermöglicht ein gemeinsames Designmuster für Apps, die auf einen Anbieter zugreifen. Die App
fragt den Anbieter ab und zeigt die resultierende Cursor
an
in einer ListView
mit einem CursorAdapter
.
Die Definition von CursorAdapter
erfordert eine der Spalten im
Cursor
wird zu _ID
Der Nutzer wählt dann eine der angezeigten Zeilen aus der Benutzeroberfläche aus, um die
Daten. Die App ruft die entsprechende Zeile aus der Cursor
ab, die die
ListView
, ruft den Wert _ID
für diese Zeile ab und hängt ihn an
den Inhalts-URI und sendet die Zugriffsanfrage an den Anbieter. Der Anbieter kann dann
Abfrage oder Änderung für die genaue Zeile, die der Nutzer ausgewählt hat.
Inhalts-URI-Muster
Damit Sie besser entscheiden können, welche Aktion für einen eingehenden Inhalts-URI ausgeführt werden soll, enthält die Provider API Folgendes:
der Convenience-Klasse UriMatcher
, die Inhalts-URI-Muster zu
ganzzahlige Werte. Sie können die ganzzahligen Werte in einer switch
-Anweisung verwenden,
Wählt die gewünschte Aktion für den Inhalts-URI oder die URIs aus, die einem bestimmten Muster entsprechen.
Ein Inhalts-URI-Muster gleicht Inhalts-URIs mit Platzhalterzeichen ab:
-
*
entspricht einem String beliebiger gültiger Zeichen. -
#
entspricht einer Zeichenfolge aus numerischen Zeichen beliebiger Länge.
Als Beispiel für die Gestaltung und Codierung der URI-Verarbeitung von Inhalten bietet sich ein Anbieter mit der
Zertifizierungsstelle com.example.app.provider
, die die folgenden Inhalts-URIs erkennt
auf Tabellen verweisen:
-
content://com.example.app.provider/table1
: eine Tabelle mit dem Namentable1
. -
content://com.example.app.provider/table2/dataset1
: eine Tabelle mit dem Namendataset1
. -
content://com.example.app.provider/table2/dataset2
: eine Tabelle mit dem Namendataset2
. -
content://com.example.app.provider/table3
: eine Tabelle mit dem Namentable3
.
Der Anbieter erkennt diese Inhalts-URIs auch, wenn ihnen eine Zeilen-ID angehängt ist, z. B. content://com.example.app.provider/table3/1
für die Zeile, die durch
1
in table3
.
Die folgenden Inhalts-URI-Muster sind möglich:
-
content://com.example.app.provider/*
- Stimmt mit jedem Inhalts-URI im Anbieter überein.
-
content://com.example.app.provider/table2/*
-
Entspricht einem Inhalts-URI für die Tabellen
dataset1
unddataset2
, stimmt aber nicht mit den Inhalts-URIs fürtable1
überein odertable3
-
content://com.example.app.provider/table3/#
-
Übereinstimmung mit einem Inhalts-URI
für einzelne Zeilen in
table3
, z. B.content://com.example.app.provider/table3/6
für die Zeile, die durch6
.
Das folgende Code-Snippet zeigt, wie die Methoden in UriMatcher
funktionieren.
Dieser Code behandelt URIs für eine ganze Tabelle anders als URIs für eine
Einzelne Zeile unter Verwendung des Inhalts-URI-Musters
content://<authority>/<path>
für Tabellen und
content://<authority>/<path>/<id>
für einzelne Zeilen.
Die Methode addURI()
ordnet ein
und Pfad zu einem ganzzahligen Wert. Die Methode match()
gibt den ganzzahligen Wert für einen URI zurück. Eine switch
-Anweisung
zwischen der Abfrage der gesamten Tabelle und der Abfrage eines einzelnen Datensatzes.
Kotlin
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used * in the path. */ addURI("com.example.app.provider", "table3", 1) /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ addURI("com.example.app.provider", "table3/#", 2) } ... class ExampleProvider : ContentProvider() { ... // Implements ContentProvider.query() override fun query( uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { var localSortOrder: String = sortOrder ?: "" var localSelection: String = selection ?: "" when (sUriMatcher.match(uri)) { 1 -> { // If the incoming URI was for all of table3 if (localSortOrder.isEmpty()) { localSortOrder = "_ID ASC" } } 2 -> { // If the incoming URI was for a single row /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ localSelection += "_ID ${uri?.lastPathSegment}" } else -> { // If the URI isn't recognized, // do some error handling here } } // Call the code to actually do the query } }
Java
public class ExampleProvider extends ContentProvider { ... // Creates a UriMatcher object. private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to one. No wildcard is used * in the path. */ uriMatcher.addURI("com.example.app.provider", "table3", 1); /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ uriMatcher.addURI("com.example.app.provider", "table3/#", 2); } ... // Implements ContentProvider.query() public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... /* * Choose the table to query and a sort order based on the code returned for the incoming * URI. Here, too, only the statements for table 3 are shown. */ switch (uriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ selection = selection + "_ID = " + uri.getLastPathSegment(); break; default: ... // If the URI isn't recognized, do some error handling here } // Call the code to actually do the query }
Eine weitere Klasse, ContentUris
, bietet Komfortmethoden zum Arbeiten
durch den id
-Teil der Inhalts-URIs. Die Klassen Uri
und
Uri.Builder
enthalten praktische Methoden zum Parsen von vorhandenen
Uri
Objekte und es werden neue erstellt.
ContentProvider-Klasse implementieren
Die Instanz ContentProvider
verwaltet den Zugriff
durch die Verarbeitung von Anfragen von anderen Anwendungen. Alle Formulare
des Zugriffs rufen schließlich ContentResolver
auf, was wiederum eine konkrete
von ContentProvider
, um Zugriff zu erhalten.
Erforderliche Methoden
Die abstrakte Klasse ContentProvider
definiert sechs abstrakte Methoden, die
die Sie als Teil Ihrer konkreten abgeleiteten Klasse implementieren. Alle diese Methoden außer
onCreate()
werden von einer Clientanwendung aufgerufen
die versucht, auf Ihren Contentanbieter zuzugreifen.
-
query()
-
Rufen Sie Daten von Ihrem Anbieter ab. Verwenden Sie die Argumente, um die Tabelle auszuwählen,
Abfrage, die zurückzugebenden Zeilen und Spalten und die Sortierreihenfolge des Ergebnisses.
Geben Sie die Daten als
Cursor
-Objekt zurück. -
insert()
- Fügen Sie bei Ihrem Anbieter eine neue Zeile ein. Verwenden Sie die Argumente, um die Zieltabelle und um die zu verwendenden Spaltenwerte abzurufen. Geben Sie einen Inhalts-URI für den die neu eingefügte Zeile.
-
update()
- Aktualisieren Sie vorhandene Zeilen bei Ihrem Anbieter. Tabelle und Zeilen mithilfe der Argumente auswählen um die aktualisierten Spaltenwerte zu aktualisieren und die aktualisierten Spaltenwerte abzurufen. Gibt die Anzahl der aktualisierten Zeilen zurück.
-
delete()
- Löschen Sie Zeilen von Ihrem Anbieter. Verwenden Sie die Argumente, um die Tabelle und die Zeilen auszuwählen, Löschen. Gibt die Anzahl der gelöschten Zeilen zurück.
-
getType()
- Gibt den MIME-Typ zurück, der einem Inhalts-URI entspricht. Diese Methode wird ausführlicher im Abschnitt MIME-Typen für Contentanbieter implementieren.
-
onCreate()
-
Initialisieren Sie Ihren Anbieter. Das Android-System ruft diese Methode unmittelbar im Anschluss an
erstellt Ihren Anbieter. Ihr Anbieter wird erst erstellt,
ContentResolver
-Objekt versucht, darauf zuzugreifen.
Diese Methoden haben dieselbe Signatur wie die mit dem identischen Namen
ContentResolver
-Methoden.
Bei der Implementierung dieser Methoden muss Folgendes berücksichtigt werden:
-
Alle diese Methoden außer
onCreate()
können von mehreren Threads gleichzeitig aufgerufen werden, daher müssen sie threadsicher sein. Weitere Informationen Mehr über mehrere Threads erfahren Sie in den <ph type="x-smartling-placeholder"></ph> Prozesse und Threads – Übersicht -
Vermeiden Sie langwierige Vorgänge in
onCreate()
. Initialisierungsaufgaben aufschieben, bis sie tatsächlich benötigt werden Abschnitt zum Implementieren der onCreate()-Methode ausführlicher behandelt. -
Sie müssen diese Methoden zwar implementieren, Ihr Code muss jedoch nichts weiter tun, außer
wird den erwarteten Datentyp zurückgegeben. So können Sie z. B. verhindern, dass andere Anwendungen
das Einfügen von Daten in einige Tabellen
durch Ignorieren des Aufrufs von
insert()
und wiederkehrend 0.
Methode query() implementieren
Die
Die Methode ContentProvider.query()
muss ein Cursor
-Objekt zurückgeben.
schlägt er fehl, lösen Exception
aus. Wenn Sie eine SQLite-Datenbank als Daten verwenden
Speicherplatz haben, können Sie die Cursor
zurückgeben, die von einem der
query()
-Methoden der Klasse SQLiteDatabase
.
Wenn bei der Abfrage keine Zeilen gefunden werden, wird Cursor
zurückgegeben.
Instanz, deren getCount()
-Methode 0 zurückgibt.
Geben Sie null
nur dann zurück, wenn während des Abfragevorgangs ein interner Fehler aufgetreten ist.
Wenn Sie keine SQLite-Datenbank als Datenspeicher verwenden, verwenden Sie eine der konkreten abgeleiteten Klassen.
von Cursor
. Beispiel: Die Klasse MatrixCursor
Implementiert einen Cursor, bei dem jede Zeile ein Array von Object
-Instanzen ist. In diesem Kurs
Verwenden Sie addRow()
, um eine neue Zeile hinzuzufügen.
Das Android-System muss in der Lage sein, Exception
zu kommunizieren
über Prozessgrenzen hinweg. Android kann dies auch für die folgenden nützlichen Ausnahmen tun
bei der Behandlung von Anfragefehlern:
-
IllegalArgumentException
. Sie können diese Nachricht senden, wenn Ihr Anbieter empfängt einen ungültigen Inhalts-URI. -
NullPointerException
insert()-Methode implementieren
Die Methode insert()
fügt ein
in die entsprechende Tabelle ein. Verwenden Sie dazu die Werte im Feld ContentValues
. Wenn ein Spaltenname nicht im Argument ContentValues
enthalten ist, geben Sie
Sie können dafür entweder in Ihrem Anbietercode oder in Ihrer Datenbank einen Standardwert angeben.
Schema.
Diese Methode gibt den Inhalts-URI für die neue Zeile zurück. Hängen Sie hierzu das neue
Primärschlüssel der Zeile, in der Regel der Wert _ID
, mit dem Inhalts-URI der Tabelle unter Verwendung von
withAppendedId()
.
Methode delete() implementieren
Die Methode delete()
müssen keine Zeilen
aus dem Datenspeicher löschen. Bei Verwendung eines Synchronisierungsadapters
können Sie eine gelöschte Zeile markieren,
mit der Schaltfläche „Löschen“ anstatt die Zeile vollständig zu entfernen. Der Synchronisierungsadapter kann
suchen Sie nach gelöschten Zeilen und entfernen Sie sie vom Server, bevor Sie sie vom Anbieter löschen.
update()-Methode implementieren
Die Methode update()
verwendet dasselbe ContentValues
-Argument, das von
insert()
und die
dieselben Argumente selection
und selectionArgs
, die von
delete()
und
ContentProvider.query()
So können Sie Code zwischen diesen Methoden wiederverwenden.
onCreate()-Methode implementieren
Das Android-System ruft onCreate()
auf
wenn der Anbieter gestartet wird. Nur schnell laufende Initialisierung durchführen
in dieser Methode ausgeführt werden und
die Datenbankerstellung und das Laden von Daten aufschieben,
eine Datenanfrage erhält. Wenn Sie langwierige Aufgaben
onCreate()
, du verlangsamt dein
für das Start-up des Anbieters. Dies wiederum verlangsamt die Antwort vom Anbieter auf andere
Anwendungen.
Die folgenden beiden Snippets veranschaulichen die Interaktion zwischen
ContentProvider.onCreate()
und
<ph type="x-smartling-placeholder"></ph>
Room.databaseBuilder()
Die erste
zeigt die Implementierung eines
ContentProvider.onCreate()
, wobei der
wird erstellt und Handles für die Datenzugriffsobjekte werden erstellt:
Kotlin
// Defines the database name private const val DBNAME = "mydb" ... class ExampleProvider : ContentProvider() { // Defines a handle to the Room database private lateinit var appDatabase: AppDatabase // Defines a Data Access Object to perform the database operations private var userDao: UserDao? = null override fun onCreate(): Boolean { // Creates a new database object appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build() // Gets a Data Access Object to perform the database operations userDao = appDatabase.userDao return true } ... // Implements the provider's insert method override fun insert(uri: Uri, values: ContentValues?): Uri? { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Java
public class ExampleProvider extends ContentProvider // Defines a handle to the Room database private AppDatabase appDatabase; // Defines a Data Access Object to perform the database operations private UserDao userDao; // Defines the database name private static final String DBNAME = "mydb"; public boolean onCreate() { // Creates a new database object appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build(); // Gets a Data Access Object to perform the database operations userDao = appDatabase.getUserDao(); return true; } ... // Implements the provider's insert method public Cursor insert(Uri uri, ContentValues values) { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
ContentProvider-MIME-Typen implementieren
Die Klasse ContentProvider
hat zwei Methoden zum Zurückgeben von MIME-Typen:
-
getType()
- Eine der erforderlichen Methoden, die Sie für jeden Anbieter implementieren.
-
getStreamTypes()
- Eine Methode, die Sie implementieren sollten, wenn Ihr Anbieter Dateien anbietet.
MIME-Typen für Tabellen
Die Methode getType()
gibt eine
String
im MIME-Format, das den Typ der vom Content zurückgegebenen Daten beschreibt
URI-Argument. Das Argument Uri
kann ein Muster anstelle eines bestimmten URI sein.
In diesem Fall wird der Datentyp zurückgegeben, der mit Inhalts-URIs verknüpft ist, die mit der
Muster zu ändern.
Für gängige Datentypen wie Text, HTML oder JPEG
getType()
gibt den Standard-
MIME-Typ für diese Daten. Eine vollständige Liste dieser Standardtypen finden Sie auf der
IANA-MIME-Medientypen
Website.
Für Inhalts-URIs, die auf eine oder mehrere Zeilen mit Tabellendaten verweisen,
getType()
Rückgaberecht
einen MIME-Typ im anbieterspezifischen MIME-Format von Android:
-
Teil eingeben:
vnd
-
Untertypteil:
<ph type="x-smartling-placeholder">
- </ph>
-
Wenn das URI-Muster für eine einzelne Zeile gilt:
android.cursor.item/
-
Wenn das URI-Muster für mehr als eine Zeile gilt:
android.cursor.dir/
-
Wenn das URI-Muster für eine einzelne Zeile gilt:
-
Anbieterspezifischer Teil:
vnd.<name>
.<type>
Sie geben
<name>
und<type>
an. Der Wert<name>
ist global eindeutig, und der Wert<type>
für den entsprechenden URI eindeutig ist Muster zu ändern. Eine gute Wahl für<name>
ist der Name Ihres Unternehmens oder Teil des Android-Paketnamens Ihrer App. Eine gute Wahl für<type>
ist ein String, der die Tabelle identifiziert, die mit dem URI.
Wenn z. B. die Befugnis eines Anbieters
com.example.app.provider
und stellt eine Tabelle mit dem Namen
table1
lautet der MIME-Typ für mehrere Zeilen in table1
:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Für eine einzelne Zeile von table1
lautet der MIME-Typ:
vnd.android.cursor.item/vnd.com.example.provider.table1
MIME-Typen für Dateien
Wenn Ihr Anbieter Dateien anbietet, implementieren Sie
getStreamTypes()
Die Methode gibt ein String
-Array mit MIME-Typen für die Dateien zurück, die von Ihrem Anbieter bereitgestellt werden
für einen bestimmten Inhalts-URI zurückgeben kann. Von Ihnen angebotene MIME-Typen nach MIME-Typ filtern
Filter-Argument, sodass nur die MIME-Typen zurückgegeben werden, die der Client verarbeiten möchte.
Stellen Sie sich z. B. einen Anbieter vor, der Fotobilder als JPG-,
PNG- und GIF-Format.
Wenn eine Anwendung ContentResolver.getStreamTypes()
mit dem Filterstring image/*
aufruft,
ein „Bild“ ist,
gibt die Methode ContentProvider.getStreamTypes()
das Array zurück:
{ "image/jpeg", "image/png", "image/gif"}
Wenn die App nur an JPG-Dateien interessiert ist, kann sie
ContentResolver.getStreamTypes()
mit dem Filterstring *\/jpeg
und
getStreamTypes()
gibt Folgendes zurück:
{"image/jpeg"}
Wenn Ihr Anbieter keinen der im Filterstring angeforderten MIME-Typen anbietet,
getStreamTypes()
gibt null
zurück.
Vertragsklasse implementieren
Eine Vertragsklasse ist eine public final
-Klasse, die konstante Definitionen für die
URIs, Spaltennamen, MIME-Typen und andere Metadaten, die sich auf den Anbieter beziehen. Die Klasse
stellt einen Vertrag zwischen dem Anbieter und anderen Anwendungen auf, wobei sichergestellt wird, dass der Anbieter
kann auch bei Änderungen an
den tatsächlichen Werten von URIs, Spaltennamen
und so weiter.
Eine Vertragsklasse ist auch hilfreich für Entwickler, sodass Entwickler weniger wahrscheinlich falsche Werte für Spaltennamen oder URIs verwenden. Da es sich um eine kann sie Javadoc-Dokumentation enthalten. Integrierte Entwicklungsumgebungen wie Android Studio kann konstante Namen aus der Vertragsklasse automatisch vervollständigen und Javadoc für anzeigen lassen die Konstanten.
Entwickler können über Ihre Anwendung nicht auf die Klassendatei der Vertragsklasse zugreifen, aber sie können ihn aus einer von Ihnen bereitgestellten JAR-Datei statisch in die Anwendung zu kompilieren.
Die Klasse ContactsContract
und ihre verschachtelten Klassen sind Beispiele für
Vertragsklassen.
Berechtigungen für Contentanbieter implementieren
Die Berechtigungen und der Zugriff für alle Aspekte des Android-Systems werden ausführlich beschrieben unter Sicherheitstipps Die Übersicht über Daten- und Dateispeicher werden die Sicherheits- und Berechtigungen beschrieben, die für die verschiedenen Speicherarten gelten. Die wichtigsten Punkte im Überblick:
- Standardmäßig sind Datendateien, die im internen Speicher des Geräts gespeichert sind, nur für Ihr Anwendung und Anbieter.
-
SQLiteDatabase
von Ihnen erstellte Datenbanken sind privat in Ihrem Anwendung und Anbieter. - Standardmäßig sind Datendateien, die Sie auf einem externen Speicher speichern, öffentlich. weltweit lesbar ist. Sie können keinen Contentanbieter verwenden, um den Zugriff auf Dateien in externen Speicher, da andere Anwendungen andere API-Aufrufe zum Lesen und Schreiben verwenden können.
- Die Methode erfordert das Öffnen oder Erstellen von Dateien oder SQLite-Datenbanken auf der internen kann allen anderen Anwendungen Lese- und Schreibzugriff gewähren. Wenn Sie eine interne Datei oder Datenbank als Repository Ihres Anbieters verwenden „weltweit lesbar“ oder „weltweit beschreibbar“, die Berechtigungen, die Sie für Ihren Anbieter in dessen Manifest Ihre Daten nicht schützen. Der Standardzugriff für Dateien und Datenbanken in interner Speicher ist "privat". für das Repository Ihres Anbieters.
Wenn Sie den Zugriff auf Ihre Daten mithilfe von Berechtigungen für Contentanbieter steuern möchten, gehen Sie so vor: Speichern Sie Ihre Daten in internen Dateien, SQLite-Datenbanken oder in der Cloud, z. B. auf einem Remote-Server speichern und Dateien und Datenbanken in Ihrer Anwendung privat halten.
Berechtigungen implementieren
Standardmäßig können alle Anwendungen Daten von Ihrem Anbieter lesen oder in diesen schreiben, auch wenn die zugrunde liegenden Daten
privat, da Ihr Anbieter standardmäßig keine Berechtigungen festgelegt hat. So können Sie dies ändern:
Legen Sie in der Manifestdatei Berechtigungen für Ihren Anbieter fest, indem Sie Attribute oder untergeordnete Elemente
des
<provider>
-Elements. Sie können Berechtigungen festlegen,
die für den gesamten Anbieter gelten,
auf bestimmte Tabellen,
bestimmte Datensätze oder auf alle drei.
Sie definieren die Berechtigungen für Ihren Anbieter mit einem oder mehreren
<permission>
-Elemente in der Manifestdatei. Damit die
nur für Ihren Anbieter zu verwenden, verwenden Sie für den
Attribut „
android:name
“. Benennen Sie beispielsweise die Leseberechtigung
com.example.app.provider.permission.READ_PROVIDER
In der folgenden Liste wird der Umfang der Anbieterberechtigungen beschrieben, beginnend mit die für den gesamten Anbieter gelten und dann detaillierter werden. Detailliertere Berechtigungen haben Vorrang vor Berechtigungen mit einem größeren Umfang.
- Eine einzelne Lese-/Schreibberechtigung auf Anbieterebene
-
Eine Berechtigung, die sowohl den Lese- als auch den Schreibzugriff auf den gesamten Anbieter steuert, angegeben
mit dem Attribut
android:permission
der<provider>
-Element. - Separate Lese- und Schreibberechtigungen auf Anbieterebene
-
Eine Leseberechtigung und eine Schreibberechtigung für den gesamten Anbieter. Sie geben diese an.
mit den
android:readPermission
undandroid:writePermission
-Attribute des<provider>
-Element. Sie haben Vorrang vor den Berechtigungen,android:permission
- Berechtigung auf Pfadebene
-
Lese-, Schreib- oder Lese-/Schreibberechtigung für einen Inhalts-URI bei Ihrem Anbieter. Ihre Angaben
jeder URI, den Sie mit einem
<path-permission>
untergeordnetes Element von<provider>
-Element. Für jeden angegebenen Inhalts-URI können Sie einen Lese-/Schreibberechtigung, Leseberechtigung, Schreibberechtigung oder alle drei. Die Lese- und Schreibberechtigungen haben Vorrang vor der Lese-/Schreibberechtigung. Auch Pfadebene hat Vorrang vor Berechtigungen auf Anbieterebene. - Temporäre Berechtigung
-
Eine Berechtigungsstufe, die temporären Zugriff auf eine Anwendung gewährt, auch wenn die Anwendung
verfügt nicht über die Berechtigungen, die normalerweise erforderlich sind. Das temporäre
Durch das Zugriffsfeature verringert sich die Anzahl der Berechtigungen, die eine Anwendung in einer
Manifests. Wenn Sie temporäre Berechtigungen aktivieren, benötigen
dauerhaften Zugriff auf alle
Ihre Daten.
Berücksichtigen Sie beispielsweise die Berechtigungen, die Sie benötigen, wenn Sie einen E-Mail-Anbieter und eine App implementieren und möchten Sie einer externen Bild-Viewer-Anwendung erlauben, Fotoanhänge aus Ihrem Dienstanbieter. Um dem Bildanzeige den notwendigen Zugriff zu gewähren, ohne dass Berechtigungen erforderlich sind, gehen Sie so vor: können Sie temporäre Berechtigungen für Inhalts-URIs für Fotos einrichten.
Gestalten Sie Ihre E-Mail-App so, Wenn der Nutzer ein Foto anzeigen möchte, sendet die App einen Intent mit dem Inhalts-URI und Berechtigungs-Flags des Fotos an die Bildanzeige. Mit der Bildanzeige Fragen Sie dann Ihren E-Mail-Anbieter nach, um das Foto abzurufen, auch wenn der Betrachter das Foto Sie haben die normale Leseberechtigung für Ihren Anbieter.
Um temporäre Berechtigungen zu aktivieren, Attribut
android:grantUriPermissions
des<provider>
-Element oder füge eines oder mehrere hinzu<grant-uri-permission>
untergeordnete Elemente zu Ihrem<provider>
-Element. AnrufContext.revokeUriPermission()
, wenn Sie die Unterstützung für einen Inhalts-URI entfernen, der mit einer temporären Berechtigung von Ihrem Dienstanbieter.Der Wert des Attributs bestimmt, welcher Anteil Ihres Anbieters verfügbar ist. Wenn das Attribut auf
"true"
gesetzt ist, gewährt das System temporäre die Berechtigung für Ihren gesamten Anbieter zu erteilen, wodurch alle anderen erforderlichen Berechtigungen Anbieter- oder Pfadebene untergeordnet werden.Wenn dieses Flag auf
"false"
gesetzt ist, fügen Sie<grant-uri-permission>
untergeordnete Elemente zu Ihrem<provider>
-Element. Jedes untergeordnete Element gibt den Inhalts-URI oder URIs, für die temporärer Zugriff gewährt wird.Zum Delegieren des temporären Zugriffs auf eine Anwendung muss ein Intent Folgendes enthalten: das Flag
FLAG_GRANT_READ_URI_PERMISSION
, der das FlagFLAG_GRANT_WRITE_URI_PERMISSION
oder beides. Diese werden mit der MethodesetFlags()
festgelegt.Wenn das Attribut
android:grantUriPermissions
nicht vorhanden ist, wird angenommen, dass es sich um"false"
.
<provider> Unterelement
Wie die Activity
- und Service
-Komponenten
eine abgeleitete Klasse von ContentProvider
wird in der Manifest-Datei für die zugehörige Anwendung definiert, wobei die Methode
<provider>
-Element. Das Android-System erhält die folgenden Informationen von
das Element:
-
Befugnis
(
android:authorities
) - Symbolische Namen, die den gesamten Anbieter innerhalb des Systems identifizieren. Dieses wird ausführlicher in der Abschnitt Designinhalts-URIs.
-
Name der Anbieterklasse
(
android:name
) -
Die Klasse, die
ContentProvider
implementiert. Dieser Kurs ist genauer beschrieben in der Implementieren Sie den Abschnitt "ContentProvider-Klasse". - Berechtigungen
-
Attribute, die die Berechtigungen angeben, die andere Anwendungen haben müssen, um auf sie zuzugreifen
des Anbieters verwenden:
<ph type="x-smartling-placeholder">
- </ph>
-
android:grantUriPermissions
: temporäres Berechtigungs-Flag -
android:permission
: Lese-/Schreibberechtigung für einzelne Anbieter -
android:readPermission
: anbieterweite Leseberechtigung -
android:writePermission
: anbieterweite Schreibberechtigung
Berechtigungen und die entsprechenden Attribute finden Sie in Details in der Implementieren Sie die Berechtigungen für Contentanbieter.
-
- Start- und Steuerattribute
-
Diese Attribute bestimmen, wie und wann das Android-System den Anbieter startet, den
Prozesseigenschaften des Anbieters und andere Laufzeiteinstellungen:
<ph type="x-smartling-placeholder">
- </ph>
-
android:enabled
: Flag, mit dem das System den Anbieter starten kann -
android:exported
: Flag, das anderen Anwendungen den Zugriff auf diesen Anbieter ermöglicht -
android:initOrder
: Reihenfolge, in der dieser Anbieter gestartet wird im Vergleich zu anderen Anbietern -
android:multiProcess
: Flag, mit dem das System den Anbieter starten kann im selben Prozess wie der aufrufende Client -
android:process
: der Name des Prozesses, in dem der Anbieter ausgeführt wird -
android:syncable
: Flag, das angibt, dass die Daten des Anbieters mit Daten auf einem Server synchronisiert
Diese Attribute sind im Leitfaden zur
<provider>
-Element. -
- Informationsattribute
-
Optionales Symbol und Label für den Anbieter:
<ph type="x-smartling-placeholder">
- </ph>
-
android:icon
: eine Drawable-Ressource, die ein Symbol für den Anbieter enthält Das Symbol erscheint neben dem Label des Anbieters in der Liste der Apps in Einstellungen > Apps > Alle: -
android:label
: ein Informationslabel, das den Anbieter und seine Daten oder beides. Das Label wird in der Liste der Apps in Einstellungen > Apps > Alle:
Diese Attribute sind im Leitfaden zur
<provider>
-Element. -
Hinweis:Wenn deine App auf Android 11 oder höher ausgerichtet ist, sieh in der Dokumentation zur Paketsichtbarkeit für weitere Konfigurationsanforderungen.
Intents und Datenzugriff
Apps können über eine Intent
indirekt auf einen Contentanbieter zugreifen.
Die Anwendung ruft keine der Methoden von ContentResolver
oder
ContentProvider
. Stattdessen sendet er
einen Intent, der eine Aktivität startet,
die oft Teil der
eigenen Anwendung des Anbieters ist. Für die Zielaktivität
Abrufen und Anzeigen der Daten auf der Benutzeroberfläche.
Je nach Aktion im Intent Zielaktivitäten können den Nutzer auch dazu auffordern, Änderungen an den Daten des Anbieters vorzunehmen. Ein Intent kann auch „Extras“ enthalten. Daten, die von der Zielaktivität auf der Benutzeroberfläche. Der Nutzer hat dann die Möglichkeit, diese Daten zu ändern, bevor er sie zum Anpassen der Daten beim Anbieter.
Mit dem Intent-Zugriff können Sie die Datenintegrität verbessern. Ihr Anbieter ist möglicherweise abhängig auf das Einfügen, Aktualisieren und Löschen von Daten gemäß genau definierter Geschäftslogik. Wenn Wenn andere Anwendungen Ihre Daten direkt ändern, kann dies ungültige Daten.
Wenn Sie möchten, dass Entwickler Intent-Zugriff verwenden, müssen Sie dies gründlich dokumentieren. Erklären Sie, warum der Intent-Zugriff über die UI Ihrer Anwendung besser ist als der Versuch, die Daten mit ihrem Code zu modifizieren.
Der Umgang mit einem eingehenden Intent, der die Daten Ihres Anbieters ändern möchte, unterscheidet sich nicht von anderen Intents zu verarbeiten. Weitere Informationen zur Verwendung von Intents finden Sie unter Intents und Intent-Filter:
Weitere Informationen finden Sie in der Übersicht des Kalenderanbieters