Mit Datenlisten auf Android arbeiten

In diesem Dokument wird die Arbeit mit Datenlisten in Firebase behandelt. 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 Datenbank-Referenz

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

Java

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

Kotlin+KTX

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

Listen lesen und schreiben

An eine Datenliste anhängen

Verwenden Sie die Methode push() , um in Mehrbenutzeranwendungen Daten an eine Liste 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 ohne Schreibkonflikte am selben Speicherort hinzufügen. 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 verwenden, die von der Methode push() werden, 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 Beispiel zum Auffächern von Daten .

Auf Kinderereignisse achten

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

Kinderveranstaltungen werden in Reaktion auf bestimmte Vorgänge ausgelöst, die auf die Kinder eines Knotens aus einer Operation passieren wie ein neues Kind durch den zusätzlichen push() Methode oder ein Kind durch das aktualisiert wird updateChildren() Methode. Alle zusammen können nützlich sein, um Änderungen an einem bestimmten Knoten in einer Datenbank abzuhören.

Um auf ChildEventListener Ereignisse in DatabaseReference zu lauschen, hängen Sie einen ChildEventListener :

Hörer Ereignisrückruf Typische Verwendung
ChildEventListener onChildAdded() Rufen Sie Listen von Elementen ab oder hören Sie auf Ergänzungen zu einer Liste von Elementen. Dieser Rückruf wird einmal für jedes vorhandene Kind ausgelöst und dann jedes Mal, wenn dem angegebenen Pfad ein neues Kind hinzugefügt wird. Der an den Listener übergebene DataSnapshot enthält die Daten des neuen DataSnapshot .
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 DataSnapshot .
onChildRemoved() Achten Sie darauf, dass Elemente aus einer Liste entfernt werden. Der an den Ereignisrückruf übergebene DataSnapshot enthält die Daten für das entfernte DataSnapshot .
onChildMoved() Achten Sie auf Änderungen an der Reihenfolge der Elemente in einer geordneten Liste. Dieses Ereignis wird immer dann ausgelöst, wenn der onChildChanged() Callback durch eine Aktualisierung ausgelöst wird, die eine Neuordnung des onChildChanged() verursacht. Es wird mit Daten verwendet, die mit orderByChild oder orderByValue .

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

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

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)

Auf Wertereignisse achten

Während die Verwendung eines ChildEventListener die empfohlene Methode zum Lesen von ChildEventListener ist, gibt es Situationen, in denen das Anfügen eines ValueEventListener an eine Listenreferenz sinnvoll ist.

ValueEventListener einen ValueEventListener an eine ValueEventListener , wird die gesamte ValueEventListener als einzelner DataSnapshot , den Sie dann ValueEventListener können, um auf einzelne ValueEventListener zuzugreifen.

Auch 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:

Java

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

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

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

Dieses Muster kann nützlich sein, wenn Sie alle onChildAdded einer Liste in einem einzigen Vorgang onChildAdded , anstatt auf zusätzliche onChildAdded Ereignisse zu onChildAdded .

Hörer trennen

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

Wenn einem Datenspeicherort ein Listener mehrmals 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.

Der Aufruf von removeEventListener() auf einem übergeordneten Listener entfernt nicht automatisch Listener, die auf seinen removeEventListener() Knoten registriert sind. removeEventListener() muss auch für alle removeEventListener() Listener aufgerufen werden, um den Rückruf zu entfernen.

Daten sortieren und filtern

Sie können die Klasse Realtime Database Query , um nach Schlüssel, Wert oder Wert eines untergeordneten Query 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 Sortiermethoden an, um zu bestimmen, wie die Ergebnisse sortiert werden:

Methode Verwendung
orderByChild() Sortieren Sie die Ergebnisse nach dem Wert eines angegebenen untergeordneten Schlüssels oder 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 Bestellmethode verwenden. Das mehrmalige Aufrufen einer order-by-Methode in derselben Abfrage führt zu einem Fehler.

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

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

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

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 der Verwendung von IDs als Indexschlüssel wird als Datenauffächerung bezeichnet. Sie können mehr darüber in Strukturieren Ihrer Datenbank lesen.

Der Aufruf der Methode orderByChild() gibt den orderByChild() Schlüssel an, nach dem die Ergebnisse sortiert werden. In diesem Fall werden die Beiträge nach dem Wert ihres jeweiligen "starCount" sortiert. Abfragen können auch nach verschachtelten Kindern geordnet 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 durch Werte verschachtelt unter der Bestellnummer metrics Schlüssel durch den relativen Pfad zu dem verschachtelten Kind in unserer Angabe orderByChild() Aufruf.

Java

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

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

Weitere Informationen zum Sortieren anderer Datentypen finden Sie unter Sortieren von 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.
startAt() Geben Sie Elemente zurück, die größer oder gleich dem angegebenen Schlüssel oder Wert sind, abhängig von der ausgewählten Sortiermethode.
startAfter() Gibt Elemente zurück, die je nach gewählter Sortiermethode größer als der angegebene Schlüssel oder Wert sind.
endAt() Geben Sie Elemente zurück, die kleiner oder gleich dem angegebenen Schlüssel oder Wert sind, abhängig von der ausgewählten Sortiermethode.
endBefore() Gibt Elemente zurück, die weniger als den angegebenen Schlüssel oder Wert aufweisen, abhängig von der ausgewählten Sortiermethode.
equalTo() Geben Sie Elemente zurück, die dem angegebenen Schlüssel oder Wert entsprechen, abhängig von der ausgewählten Sortiermethode.

Im Gegensatz zu den Sortiermethoden können Sie mehrere Grenzwert- 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; es enthält nur ein einzelnes Element. Um auf das Element zuzugreifen, müssen Sie das Ergebnis durchlaufen:

Java

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

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

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

Begrenzen Sie die Anzahl der Ergebnisse

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

Wenn sich Elemente ändern, erhalten Sie onChildAdded() Callbacks für Elemente, die in die Abfrage eintreten, und onChildRemoved() Callbacks für Elemente, die aus ihr 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:

Java

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

Kotlin+KTX

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

In diesem Beispiel wird nur eine Abfrage definiert, um Daten tatsächlich zu synchronisieren, muss ein angehängter Listener vorhanden sein .

Nach Schlüssel oder Wert filtern

Sie können startAt() , startAfter() , endAt() , endBefore() und equalTo() , 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 nach jeder der order-by-Methoden in der Query Klasse sortiert werden.

orderByChild

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

  1. Kinder mit einem null für den angegebenen untergeordneten Schlüssel kommen zuerst.
  2. Als nächstes kommen untergeordnete Elemente mit dem Wert false für den angegebenen untergeordneten Schlüssel. Wenn mehrere Kinder den Wert false , werden sie lexikografisch nach Schlüssel sortiert.
  3. Als nächstes kommen untergeordnete Elemente mit dem Wert true für den angegebenen untergeordneten Schlüssel. Wenn mehrere Kinder den Wert true , 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 dem Schlüssel sortiert.
  5. Strings kommen nach Zahlen und werden lexikographisch aufsteigend sortiert. Wenn mehrere Kinder denselben Wert für den angegebenen Kindknoten haben, werden sie lexikografisch nach Schlüssel sortiert.
  6. Objekte stehen an letzter Stelle und werden lexikographisch nach Schlüsseln 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, kommen zuerst, sortiert in aufsteigender Reihenfolge.
  2. Als nächstes kommen Kinder mit einem String-Wert als Schlüssel, lexikographisch aufsteigend sortiert.

orderByValue

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

Nächste Schritte