Contentanbieter erstellen

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 oder CursorLoader 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:

  1. 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.
  2. 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".
  3. 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.
  4. 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 und android.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ür ListView 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 Namen table1.
  • content://com.example.app.provider/table2/dataset1: eine Tabelle mit dem Namen dataset1.
  • content://com.example.app.provider/table2/dataset2: eine Tabelle mit dem Namen dataset2.
  • content://com.example.app.provider/table3: eine Tabelle mit dem Namen table3.

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 und dataset2, stimmt aber nicht mit den Inhalts-URIs für table1 überein oder table3
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 durch 6.

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:

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/
  • 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 und android: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. Anruf Context.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 Flag FLAG_GRANT_WRITE_URI_PERMISSION oder beides. Diese werden mit der Methode setFlags() 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">

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