Arbeiten Sie mit Datenlisten auf Android

Dieses Dokument behandelt die Arbeit mit Datenlisten in Firebase. Informationen zu den Grundlagen des Lesens und Schreibens von Firebase-Daten finden Sie unter Lesen und Schreiben von Daten auf Android .

Holen Sie sich eine Datenbankreferenz

Um Daten aus der Datenbank zu lesen und zu schreiben, benötigen Sie eine Instanz von DatabaseReference :

Kotlin+KTX

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

Java

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

Listen lesen und schreiben

An eine Datenliste anhängen

Verwenden Sie die Methode push() , um in Mehrbenutzeranwendungen Daten an eine Liste anzuhängen. Die push() Methode 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 Kinder an derselben Position 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. Der Aufruf von getKey() für eine push() Referenz gibt den Wert des automatisch generierten Schlüssels zurück.

Sie können diese automatisch generierten Schlüssel verwenden, um die Reduzierung Ihrer Datenstruktur zu vereinfachen. Weitere Informationen finden Sie im Daten-Fanout- Beispiel .

Achten Sie auf untergeordnete Ereignisse

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

Untergeordnete Ereignisse werden als Reaktion auf bestimmte Vorgänge ausgelöst, die den untergeordneten Knoten eines Knotens durch einen Vorgang widerfahren, z. B. das Hinzufügen eines neuen untergeordneten Knotens über die Methode push() oder die Aktualisierung eines untergeordneten Knotens über die Methode updateChildren() . Beides zusammen kann nützlich sein, um Änderungen an einem bestimmten Knoten in einer Datenbank abzuhören.

Um auf untergeordnete Ereignisse in DatabaseReference zu warten, fügen Sie einen ChildEventListener an:

Hörer Ereignisrückruf Typische Verwendung
ChildEventListener onChildAdded() Rufen Sie Artikellisten ab oder warten Sie auf Ergänzungen zu einer Artikelliste. Dieser Rückruf wird einmal für jedes vorhandene untergeordnete Element und dann jedes Mal erneut 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() Achten Sie auf Änderungen an den Elementen in einer Liste. Dieses Ereignis wird jedes Mal ausgelöst, wenn ein untergeordneter Knoten geändert wird, einschließlich aller Änderungen an Nachkommen des untergeordneten Knotens. Der an den Ereignis-Listener übergebene DataSnapshot enthält die aktualisierten Daten für das untergeordnete Element.
onChildRemoved() Achten Sie auf Elemente, die aus einer Liste entfernt werden. Der an den Ereignisrückruf übergebene DataSnapshot enthält die Daten für das entfernte untergeordnete Element.
onChildMoved() Achten Sie auf Änderungen in der Reihenfolge der Elemente in einer geordneten Liste. Dieses Ereignis wird immer dann ausgelöst, wenn der onChildChanged() -Rückruf durch eine Aktualisierung ausgelöst wird, die eine Neuordnung des untergeordneten Elements verursacht. Es wird mit Daten verwendet, die mit orderByChild oder orderByValue geordnet sind.

Beispielsweise könnte eine Social-Blogging-App diese Methoden zusammen verwenden, um die Aktivität in den Kommentaren eines Beitrags zu überwachen, wie unten gezeigt:

Kotlin+KTX

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);

Achten Sie auf Wertereignisse

Während die Verwendung eines ChildEventListener die empfohlene Methode zum Lesen von Datenlisten ist, gibt es Situationen, in denen das Anhängen eines ValueEventListener an eine Listenreferenz nützlich ist.

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

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

Kotlin+KTX

// 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.

Trennen Sie Zuhörer

Rückrufe werden durch Aufrufen der Methode removeEventListener() in Ihrer Firebase-Datenbankreferenz entfernt.

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

Durch den Aufruf von removeEventListener() für einen übergeordneten Listener werden die auf seinen untergeordneten Knoten registrierten Listener nicht automatisch entfernt. removeEventListener() muss auch für alle untergeordneten Listener aufgerufen werden, um den Rückruf zu entfernen.

Daten sortieren und filtern

Sie können die Klasse „Realtime Database Query verwenden, um nach Schlüssel, Wert oder Wert eines untergeordneten Elements sortierte Daten abzurufen. Sie können das sortierte Ergebnis auch nach einer bestimmten Anzahl von Ergebnissen oder einem Bereich von Schlüsseln oder Werten filtern.

Daten sortieren

Um sortierte Daten abzurufen, geben Sie zunächst eine der Order-by-Methoden an, um zu bestimmen, wie die Ergebnisse sortiert werden:

Methode Verwendung
orderByChild() Ordnen Sie die Ergebnisse nach dem Wert eines angegebenen untergeordneten Schlüssels oder eines verschachtelten untergeordneten Pfads.
orderByKey() Sortieren Sie die Ergebnisse nach untergeordneten Schlüsseln.
orderByValue() Sortieren Sie die Ergebnisse nach untergeordneten Werten.

Sie können jeweils nur eine Order-By-Methode verwenden. Der mehrmalige Aufruf einer Order-By-Methode in derselben Abfrage führt zu einem Fehler.

Das folgende Beispiel zeigt, wie Sie eine Liste der Top-Beiträge eines Benutzers, sortiert nach der Anzahl der Sterne, abrufen können:

Kotlin+KTX

// 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
    // ...
});

Dies definiert eine Abfrage, die in Kombination mit einem untergeordneten Listener den Client mit den Beiträgen des Benutzers aus dem Pfad in der Datenbank basierend auf seiner Benutzer-ID synchronisiert, geordnet nach der Anzahl der Sterne, die jeder Beitrag erhalten hat. Diese Technik, bei der IDs als Indexschlüssel verwendet werden, wird als Daten-Fan-Out bezeichnet. Weitere Informationen dazu finden Sie unter „Strukturieren Sie Ihre Datenbank“ .

Der Aufruf der Methode orderByChild() gibt den untergeordneten Schlüssel an, nach dem die Ergebnisse sortiert werden sollen. In diesem Fall werden die Beiträge nach dem Wert ihres jeweiligen "starCount" -Kinds sortiert. Abfragen können auch nach verschachtelten untergeordneten Elementen sortiert werden, falls Sie Daten haben, die wie folgt aussehen:

"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 unsere Listenelemente nach Werten ordnen, die unter dem Schlüssel metrics verschachtelt sind, indem wir in unserem orderByChild() Aufruf den relativen Pfad zum verschachtelten untergeordneten Element angeben.

Kotlin+KTX

// 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 Reihenfolge anderer Datentypen finden Sie unter Reihenfolge der Abfragedaten .

Daten filtern

Um Daten zu filtern, können Sie beim Erstellen einer Abfrage jede der Limit- oder Range-Methoden mit einer Order-By-Methode kombinieren.

Methode Verwendung
limitToFirst() Legt die maximale Anzahl von Elementen fest, die vom Anfang der geordneten Ergebnisliste zurückgegeben werden sollen.
limitToLast() Legt die maximale Anzahl von Elementen fest, die vom Ende der geordneten Ergebnisliste zurückgegeben werden sollen.
startAt() Gibt Elemente zurück, die größer oder gleich dem angegebenen Schlüssel oder Wert sind, abhängig von der gewählten Sortiermethode.
startAfter() Gibt Elemente zurück, die größer als der angegebene Schlüssel oder Wert sind, abhängig von der gewählten Sortiermethode.
endAt() Gibt Elemente zurück, die kleiner oder gleich dem angegebenen Schlüssel oder Wert sind, abhängig von der gewählten Sortiermethode.
endBefore() Gibt Elemente zurück, die kleiner als der angegebene Schlüssel oder Wert sind, abhängig von der gewählten Sortiermethode.
equalTo() Gibt abhängig von der gewählten Sortiermethode Elemente zurück, die dem angegebenen Schlüssel oder Wert entsprechen.

Im Gegensatz zu den Order-by-Methoden können Sie mehrere Grenzwert- oder Bereichsfunktionen kombinieren. Beispielsweise können Sie die Methoden startAt() und endAt() kombinieren, um die Ergebnisse auf einen angegebenen Wertebereich zu beschränken.

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

Kotlin+KTX

// 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());
        // ...
    }
});

Begrenzen Sie die Anzahl der Ergebnisse

Sie können die Methoden limitToFirst() und limitToLast() verwenden, um eine maximale Anzahl von untergeordneten Elementen festzulegen, die für einen bestimmten Rückruf synchronisiert werden sollen. Wenn Sie beispielsweise mit limitToFirst() ein Limit von 100 festlegen, erhalten Sie zunächst nur bis zu 100 onChildAdded() Rückrufe. Wenn in Ihrer Firebase-Datenbank weniger als 100 Elemente gespeichert sind, wird für jedes Element ein onChildAdded() -Rückruf ausgelöst.

Wenn sich Elemente ändern, erhalten Sie onChildAdded() Rückrufe für Elemente, die in die Abfrage eingegeben werden, und onChildRemoved() Rückrufe für Elemente, die aus der Abfrage herausfallen, sodass die Gesamtzahl bei 100 bleibt.

Das folgende Beispiel zeigt, wie eine Beispiel-Blogging-App eine Abfrage definiert, um eine Liste der 100 neuesten Beiträge aller Benutzer abzurufen:

Kotlin+KTX

// 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. Um Daten tatsächlich zu synchronisieren, muss ein Listener angehängt sein.

Nach Schlüssel oder Wert filtern

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

So werden Abfragedaten geordnet

In diesem Abschnitt wird erläutert, wie Daten durch die einzelnen Order-by-Methoden in der Query Klasse sortiert werden.

orderByChild

Bei Verwendung von orderByChild() werden Daten, die den angegebenen untergeordneten Schlüssel enthalten, wie folgt sortiert:

  1. Kinder mit einem null für den angegebenen Kinderschlüssel stehen an erster Stelle.
  2. Als nächstes folgen Kinder mit dem Wert false für den angegebenen Kinderschlüssel. Wenn mehrere untergeordnete Elemente den Wert false haben, werden sie lexikografisch nach Schlüssel sortiert.
  3. Als nächstes folgen Kinder mit dem Wert true für den angegebenen Kinderschlüssel. Wenn mehrere untergeordnete Elemente den Wert true haben, werden sie lexikografisch nach Schlüssel sortiert.
  4. Als nächstes folgen Kinder mit einem numerischen Wert, sortiert in aufsteigender Reihenfolge. Wenn mehrere untergeordnete Knoten denselben numerischen Wert für den angegebenen untergeordneten Knoten haben, werden sie nach Schlüssel sortiert.
  5. Zeichenfolgen kommen nach Zahlen und werden lexikografisch in aufsteigender Reihenfolge sortiert. Wenn mehrere untergeordnete Knoten denselben Wert für den angegebenen untergeordneten Knoten haben, werden sie lexikografisch nach Schlüssel sortiert.
  6. Die Objekte stehen an letzter Stelle 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. Kinder mit einem Schlüssel, der als 32-Bit-Ganzzahl geparst werden kann, stehen an erster Stelle und werden in aufsteigender Reihenfolge sortiert.
  2. Als nächstes folgen Kinder mit einem Zeichenfolgenwert als Schlüssel, lexikografisch in aufsteigender Reihenfolge sortiert.

orderByValue

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

Nächste Schritte