Mit Datenlisten auf Android-Geräten arbeiten

In diesem Dokument wird die Arbeit mit Datenlisten in Firebase beschrieben. Die Grundlagen zum Lesen und Schreiben von Firebase-Daten finden Sie unter Daten unter Android lesen und schreiben.

DatabaseReference abrufen

Zum Lesen und Schreiben von Daten aus der Datenbank benötigen Sie eine Instanz von DatabaseReference:

Kotlin

private lateinit var database: DatabaseReference
// ...
database = Firebase.database.reference

Java

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

Listen lesen und schreiben

An eine Liste von Daten anhängen

Verwenden Sie die Methode push(), um Daten an eine Liste in Anwendungen mit mehreren Nutzern anzuhängen. Die Methode push() generiert jedes Mal einen eindeutigen Schlüssel, wenn der angegebenen Firebase-Referenz ein neues untergeordnetes Element hinzugefügt wird. Durch die Verwendung dieser automatisch generierten Schlüssel für jedes neue Element in der Liste können mehrere Clients gleichzeitig untergeordnete Elemente am selben Speicherort hinzufügen, ohne dass es zu Schreibkonflikten kommt. Der von push() generierte eindeutige Schlüssel basiert auf einem Zeitstempel, sodass Listenelemente automatisch chronologisch sortiert werden.

Sie können den Verweis auf die neuen Daten, die von der Methode push() zurückgegeben werden, verwenden, um den Wert des automatisch generierten Schlüssels des untergeordneten Elements abzurufen oder Daten für das untergeordnete Element festzulegen. Wenn Sie getKey() für eine push()-Referenz aufrufen, wird der Wert des automatisch generierten Schlüssels zurückgegeben.

Sie können diese automatisch generierten Schlüssel verwenden, um die Vereinfachung Ihrer Datenstruktur zu vereinfachen. Weitere Informationen finden Sie im Beispiel für die Datenverteilung.

Auf untergeordnete Ereignisse warten

Wenn Sie mit Listen arbeiten, sollte Ihre Anwendung auf untergeordnete Ereignisse warten, anstatt auf die Wertereignisse, die für einzelne Objekte verwendet werden.

Untergeordnete Ereignisse werden als Reaktion auf bestimmte Vorgänge ausgelöst, die für die untergeordneten Elemente eines Knotens ausgeführt werden, z. B. wenn ein neues untergeordnetes Element über die Methode push() hinzugefügt oder ein untergeordnetes Element über die Methode updateChildren() aktualisiert wird. Beide zusammen können nützlich sein, um auf Änderungen an einem bestimmten Knoten in einer Datenbank zu warten.

Wenn Sie auf untergeordnete Ereignisse in DatabaseReference warten möchten, hängen Sie einen ChildEventListener an:

Listener Ereignis-Callback Typische Verwendung
ChildEventListener onChildAdded() Listen mit Elementen abrufen oder auf Ergänzungen zu einer Liste mit Elementen warten. Dieser Callback wird einmal für jedes vorhandene untergeordnete Element und dann jedes Mal ausgelöst, wenn dem angegebenen Pfad ein neues untergeordnetes Element hinzugefügt wird. Der an den Listener übergebene DataSnapshot enthält die Daten des neuen untergeordneten Elements.
onChildChanged() Änderungen an den Elementen in einer Liste beobachten. Dieses Ereignis wird ausgelöst, wenn ein untergeordneter Knoten geändert wird, einschließlich aller Änderungen an Nachfolgern des untergeordneten Knotens. Der an den Event-Listener übergebene DataSnapshot enthält die aktualisierten Daten für das untergeordnete Element.
onChildRemoved() Auf das Entfernen von Elementen aus einer Liste warten. Der an den Ereignis-Callback übergebene DataSnapshot enthält die Daten für das entfernte untergeordnete Element.
onChildMoved() Änderungen an der Reihenfolge der Elemente in einer sortierten Liste überwachen. Dieses Ereignis wird immer dann ausgelöst, wenn der onChildChanged()-Callback durch eine Aktualisierung ausgelöst wird, die zu einer Neuanordnung des untergeordneten Elements führt. Sie wird für Daten verwendet, die mit orderByChild oder orderByValue sortiert sind.

Eine Social-Blogging-App könnte diese Methoden beispielsweise zusammen verwenden, um die Aktivität in den Kommentaren eines Beitrags zu beobachten, wie unten gezeigt:

Kotlin

val childEventListener = object : ChildEventListener {
    override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!)

        // A new comment has been added, add it to the displayed list
        val comment = dataSnapshot.getValue<Comment>()

        // ...
    }

    override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildChanged: ${dataSnapshot.key}")

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        val newComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildRemoved(dataSnapshot: DataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!)

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!)

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        val movedComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException())
        Toast.makeText(
            context,
            "Failed to load comments.",
            Toast.LENGTH_SHORT,
        ).show()
    }
}
databaseReference.addChildEventListener(childEventListener)

Java

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);

        // ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
databaseReference.addChildEventListener(childEventListener);

Auf Wertereignisse warten

Die Verwendung eines ChildEventListener ist zwar die empfohlene Methode zum Lesen von Datenlisten, es gibt jedoch Situationen, in denen es nützlich ist, einen ValueEventListener an eine Listenreferenz anzuhängen.

Wenn Sie einen ValueEventListener an eine Liste von Daten anhängen, wird die gesamte Liste als einzelner DataSnapshot zurückgegeben. Sie können dann eine Schleife durchlaufen, um auf einzelne untergeordnete Elemente zuzugreifen.

Auch wenn es nur eine einzige Übereinstimmung für die Abfrage gibt, ist der Snapshot immer noch eine Liste, die nur ein einziges Element enthält. Um auf das Element zuzugreifen, müssen Sie das Ergebnis durchlaufen:

Kotlin

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

Java

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Dieses Muster kann nützlich sein, wenn Sie alle untergeordneten Elemente einer Liste in einem einzigen Vorgang abrufen möchten, anstatt auf zusätzliche onChildAdded-Ereignisse zu warten.

Listener trennen

Callbacks werden entfernt, indem die Methode removeEventListener() für Ihre Firebase-Datenbankreferenz aufgerufen wird.

Wenn ein Listener mehrmals zu einem Datenspeicherort hinzugefügt wurde, wird er für jedes Ereignis mehrmals aufgerufen. Sie müssen ihn dann genauso oft trennen, um ihn vollständig zu entfernen.

Wenn Sie removeEventListener() für einen übergeordneten Listener aufrufen, werden Listener, die für die untergeordneten Knoten registriert sind, nicht automatisch entfernt. removeEventListener() muss auch für alle untergeordneten Listener aufgerufen werden, um den Callback zu entfernen.

Daten sortieren und filtern

Mit der Klasse Realtime Database Query können Sie Daten abrufen, die nach Schlüssel, Wert oder Wert eines untergeordneten Elements sortiert sind. Sie können das sortierte Ergebnis auch auf eine bestimmte Anzahl von Ergebnissen oder einen Bereich von Schlüsseln oder Werten filtern.

Daten sortieren

Wenn Sie sortierte Daten abrufen möchten, geben Sie zuerst eine der order by-Methoden an, um festzulegen, wie die Ergebnisse sortiert werden sollen:

Methode Nutzung
orderByChild() Ergebnisse nach dem Wert eines angegebenen untergeordneten Schlüssels oder verschachtelten untergeordneten Pfads sortieren.
orderByKey() Ergebnisse nach untergeordneten Schlüsseln sortieren.
orderByValue() Ergebnisse nach untergeordneten Werten sortieren.

Sie können jeweils nur eine order by-Methode verwenden. Wenn eine order by-Methode mehrmals in derselben Abfrage aufgerufen wird, wird ein Fehler ausgegeben.

Im folgenden Beispiel wird gezeigt, wie Sie eine Liste der Top-Beiträge eines Nutzers abrufen können, sortiert nach der Anzahl der Sterne:

Kotlin

// My top posts by number of stars
val myUserId = uid
val myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
    .orderByChild("starCount")

myTopPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

Java

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Damit wird eine Abfrage definiert, die in Kombination mit einem untergeordneten Listener den Client mit den Beiträgen des Nutzers aus dem Pfad in der Datenbank basierend auf seiner Nutzer-ID synchronisiert, sortiert nach der Anzahl der Sterne, die jeder Beitrag erhalten hat. Diese Technik, IDs als Indexschlüssel zu verwenden, wird als Daten-Fan-out bezeichnet. Weitere Informationen dazu finden Sie unter Datenbank strukturieren.

Beim Aufruf der Methode orderByChild() wird der untergeordnete Schlüssel angegeben, nach dem die Ergebnisse sortiert werden sollen. In diesem Fall werden die Beiträge nach dem Wert des jeweiligen untergeordneten "starCount"-Elements sortiert. Abfragen können auch nach verschachtelten untergeordneten Elementen sortiert werden, wenn Sie Daten wie die folgenden haben:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

In diesem Beispiel können wir die Listenelemente nach Werten sortieren, die unter dem Schlüssel metrics verschachtelt sind. Dazu geben wir im Aufruf orderByChild() den relativen Pfad zum verschachtelten untergeordneten Element an.

Kotlin

// Most viewed posts
val myMostViewedPostsQuery = databaseReference.child("posts")
    .orderByChild("metrics/views")
myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

Java

// Most viewed posts
Query myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views");
myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Weitere Informationen zur Sortierung anderer Datentypen finden Sie unter Sortierung von Abfragedaten.

Daten filtern

Wenn Sie Daten filtern möchten, können Sie beim Erstellen einer Abfrage eine beliebige Limit- oder Bereichsmethode mit einer order by-Methode kombinieren.

Methode Nutzung
limitToFirst() Legt die maximale Anzahl von Elementen fest, die ab dem Anfang der sortierten Ergebnisliste zurückgegeben werden sollen.
limitToLast() Legt die maximale Anzahl der Elemente fest, die vom Ende der sortierten Ergebnisliste zurückgegeben werden sollen.
startAt() Gibt Elemente zurück, die größer oder gleich dem angegebenen Schlüssel oder Wert sind, je nach gewählter order by-Methode.
startAfter() Gibt Elemente zurück, die größer als der angegebene Schlüssel oder Wert sind, je nach gewählter order by-Methode.
endAt() Gibt Elemente zurück, die kleiner oder gleich dem angegebenen Schlüssel oder Wert sind, je nach gewählter order by-Methode.
endBefore() Gibt Elemente zurück, die kleiner als der angegebene Schlüssel oder Wert sind, je nach gewählter order by-Methode.
equalTo() Gibt Elemente zurück, die dem angegebenen Schlüssel oder Wert entsprechen, je nach gewählter order by-Methode.

Im Gegensatz zu den order by-Methoden können Sie mehrere Limit- oder Bereichsfunktionen kombinieren. Sie können beispielsweise die Methoden startAt() und endAt() kombinieren, um die Ergebnisse auf einen bestimmten Wertebereich zu beschränken.

Auch wenn es nur eine einzige Übereinstimmung für die Abfrage gibt, ist der Snapshot immer noch eine Liste, die nur ein einzelnes Element enthält. Wenn Sie auf das Element zugreifen möchten, müssen Sie das Ergebnis in einer Schleife durchlaufen:

Kotlin

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

Java

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Anzahl der Ergebnisse begrenzen

Mit den Methoden limitToFirst() und limitToLast() können Sie die maximale Anzahl von untergeordneten Elementen festlegen, die für einen bestimmten Callback synchronisiert werden sollen. Wenn Sie beispielsweise mit limitToFirst() ein Limit von 100 festlegen, erhalten Sie anfangs nur bis zu 100 onChildAdded()-Callbacks. Wenn weniger als 100 Elemente in Ihrer Firebase-Datenbank gespeichert sind, wird für jedes Element ein onChildAdded()-Callback ausgelöst.

Wenn sich Elemente ändern, erhalten Sie onChildAdded()-Callbacks für Elemente, die in die Abfrage aufgenommen werden, und onChildRemoved()-Callbacks für Elemente, die aus der Abfrage entfernt werden, sodass die Gesamtzahl bei 100 bleibt.

Im folgenden Beispiel wird gezeigt, wie die Beispiel-Blogging-App eine Abfrage definiert, um eine Liste der 100 neuesten Beiträge aller Nutzer abzurufen:

Kotlin

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys.
databaseReference.child("posts").limitToFirst(100)

Java

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
        .limitToFirst(100);

In diesem Beispiel wird nur eine Abfrage definiert. Damit Daten tatsächlich synchronisiert werden, muss ein Listener angehängt werden.

Nach Schlüssel oder Wert filtern

Mit startAt(), startAfter(), endAt(), endBefore() und equalTo() können Sie beliebige Start-, End- und Äquivalenzpunkte für Abfragen auswählen. Das kann nützlich sein, um Daten zu paginieren oder Elemente mit untergeordneten Elementen zu finden, die einen bestimmten Wert haben.

So werden Abfragedaten sortiert

In diesem Abschnitt wird erläutert, wie Daten nach den einzelnen order-by-Methoden in der Klasse Query sortiert werden.

orderByChild

Wenn Sie orderByChild() verwenden, werden Daten, die den angegebenen untergeordneten Schlüssel enthalten, so sortiert:

  1. Untergeordnete Elemente mit dem Wert null für den angegebenen untergeordneten Schlüssel stehen an erster Stelle.
  2. Als Nächstes folgen untergeordnete Elemente mit dem Wert false für den angegebenen untergeordneten Schlüssel. Wenn mehrere untergeordnete Elemente den Wert false haben, werden sie lexikografisch nach Schlüssel sortiert.
  3. Als Nächstes folgen untergeordnete Elemente mit dem Wert true für den angegebenen untergeordneten Schlüssel. Wenn mehrere untergeordnete Elemente den Wert true haben, werden sie lexikografisch nach Schlüssel sortiert.
  4. Als Nächstes folgen untergeordnete Elemente mit einem numerischen Wert, sortiert in aufsteigender Reihenfolge. Wenn mehrere untergeordnete Elemente denselben numerischen Wert für den angegebenen untergeordneten Knoten haben, werden sie nach Schlüssel sortiert.
  5. Strings werden nach Zahlen aufgeführt und lexikografisch in aufsteigender Reihenfolge sortiert. Wenn mehrere untergeordnete Elemente denselben Wert für den angegebenen untergeordneten Knoten haben, werden sie lexikografisch nach Schlüssel sortiert.
  6. Objekte kommen zuletzt und werden lexikografisch nach Schlüssel in aufsteigender Reihenfolge sortiert.

orderByKey

Wenn Sie orderByKey() zum Sortieren Ihrer Daten verwenden, werden die Daten in aufsteigender Reihenfolge nach Schlüssel zurückgegeben.

  1. Untergeordnete Elemente mit einem Schlüssel, der als 32-Bit-Ganzzahl geparst werden kann, werden zuerst angezeigt, sortiert in aufsteigender Reihenfolge.
  2. Als Nächstes folgen untergeordnete Elemente mit einem Stringwert als Schlüssel, die lexikografisch in aufsteigender Reihenfolge sortiert sind.

orderByValue

Bei Verwendung von orderByValue() werden untergeordnete Elemente nach ihrem Wert sortiert. Die Sortierkriterien sind dieselben wie in orderByChild(), mit der Ausnahme, dass der Wert des Knotens anstelle des Werts eines angegebenen untergeordneten Schlüssels verwendet wird.

Nächste Schritte