Ten dokument opisuje pracę z listami danych w Firebase. Aby poznać podstawy odczytywania i zapisywania danych Firebase, zobacz Odczytywanie i zapisywanie danych w systemie Android .
Pobierz DatabaseReference
Aby odczytywać i zapisywać dane z bazy danych, potrzebujesz instancji DatabaseReference
:
Kotlin+KTX
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
Czytaj i zapisuj listy
Dołącz do listy danych
Użyj metody push()
, aby dołączyć dane do listy w aplikacjach obsługujących wielu użytkowników. Metoda push()
generuje unikalny klucz za każdym razem, gdy do określonego odniesienia Firebase dodawany jest nowy element potomny. Korzystając z tych automatycznie generowanych kluczy dla każdego nowego elementu na liście, kilku klientów może dodawać elementy podrzędne do tej samej lokalizacji w tym samym czasie bez konfliktów zapisu. Unikalny klucz generowany przez push()
jest oparty na znaczniku czasu, więc elementy listy są automatycznie uporządkowane chronologicznie.
Możesz użyć odwołania do nowych danych zwróconych przez metodę push()
, aby uzyskać wartość automatycznie wygenerowanego klucza lub zestawu danych dziecka. Wywołanie metody getKey()
na odwołaniu push()
zwraca wartość automatycznie wygenerowanego klucza.
Możesz użyć tych automatycznie generowanych kluczy, aby uprościć spłaszczanie struktury danych. Aby uzyskać więcej informacji, zobacz przykład rozkładania danych .
Słuchaj wydarzeń dla dzieci
Podczas pracy z listami aplikacja powinna nasłuchiwać zdarzeń podrzędnych, a nie zdarzeń wartości używanych w przypadku pojedynczych obiektów.
Zdarzenia podrzędne są wyzwalane w odpowiedzi na określone operacje, które mają miejsce na elementach podrzędnych węzła z operacji, takich jak dodanie nowego elementu podrzędnego za pomocą metody push()
lub zaktualizowanie elementu podrzędnego za pomocą metody updateChildren()
. Każde z nich razem może być przydatne do nasłuchiwania zmian w określonym węźle w bazie danych.
Aby nasłuchiwać zdarzeń potomnych w DatabaseReference
, dołącz ChildEventListener
:
Słuchacz | Oddzwonienie do zdarzenia | Typowe użycie |
---|---|---|
ChildEventListener | onChildAdded() | Pobieraj listy elementów lub słuchaj dodatków do listy elementów. To wywołanie zwrotne jest wyzwalane raz dla każdego istniejącego elementu podrzędnego, a następnie ponownie za każdym razem, gdy do określonej ścieżki zostanie dodany nowy element podrzędny. DataSnapshot przekazana do odbiornika zawiera dane nowego elementu podrzędnego. |
onChildChanged() | Słuchaj zmian pozycji na liście. To zdarzenie jest uruchamiane za każdym razem, gdy węzeł podrzędny jest modyfikowany, w tym wszelkie modyfikacje elementów podrzędnych węzła podrzędnego. DataSnapshot przekazana do odbiornika zdarzeń zawiera zaktualizowane dane dla elementu podrzędnego. | |
onChildRemoved() | Słuchaj, czy elementy są usuwane z listy. DataSnapshot przekazana do wywołania zwrotnego zdarzenia zawiera dane usuniętego elementu podrzędnego. | |
onChildMoved() | Słuchaj zmian kolejności elementów na uporządkowanej liście. To zdarzenie jest wyzwalane za każdym razem, gdy wywołanie zwrotne onChildChanged() jest wyzwalane przez aktualizację powodującą zmianę kolejności elementu podrzędnego. Jest używany z danymi uporządkowanymi za pomocą orderByChild lub orderByValue . |
Na przykład aplikacja do blogowania społecznościowego może wykorzystywać te metody razem do monitorowania aktywności w komentarzach do posta, jak pokazano poniżej:
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);
Słuchaj wartościowych wydarzeń
Podczas gdy używanie elementu ChildEventListener
jest zalecanym sposobem odczytywania list danych, istnieją sytuacje, w których przydatne jest dołączenie elementu ValueEventListener
do odwołania do listy.
Dołączenie elementu ValueEventListener
do listy danych spowoduje zwrócenie całej listy danych w postaci pojedynczej DataSnapshot
, którą można następnie zapętlić, aby uzyskać dostęp do poszczególnych elementów podrzędnych.
Nawet jeśli istnieje tylko jedno dopasowanie do zapytania, migawka nadal jest listą; zawiera tylko jeden element. Aby uzyskać dostęp do elementu, musisz zapętlić wynik:
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()); // ... } });
Ten wzorzec może być przydatny, gdy chcesz pobrać wszystkie elementy podrzędne listy w jednej operacji, zamiast nasłuchiwać dodatkowych zdarzeń onChildAdded
.
Odłącz słuchaczy
Wywołania zwrotne są usuwane przez wywołanie metody removeEventListener()
w odwołaniu do bazy danych Firebase.
Jeśli detektor został dodany wiele razy do lokalizacji danych, jest wywoływany wiele razy dla każdego zdarzenia i należy go odłączyć taką samą liczbę razy, aby całkowicie go usunąć.
Wywołanie metody removeEventListener()
na odbiorniku nadrzędnym nie powoduje automatycznego usunięcia detektorów zarejestrowanych w jego węzłach podrzędnych; removeEventListener()
musi być również wywołana na wszystkich słuchaczach podrzędnych, aby usunąć wywołanie zwrotne.
Sortowanie i filtrowanie danych
Możesz użyć klasy Query
do bazy danych czasu rzeczywistego, aby pobrać dane posortowane według klucza, wartości lub wartości elementu podrzędnego. Możesz także filtrować posortowany wynik do określonej liczby wyników lub zakresu kluczy lub wartości.
Sortuj dane
Aby pobrać posortowane dane, zacznij od określenia jednej z metod porządkowania w celu określenia kolejności wyników:
metoda | Stosowanie |
---|---|
orderByChild() | Uporządkuj wyniki według wartości określonego klucza podrzędnego lub zagnieżdżonej ścieżki podrzędnej. | orderByKey() | Uporządkuj wyniki według kluczy potomnych. |
orderByValue() | Uporządkuj wyniki według wartości podrzędnych. |
W danym momencie możesz użyć tylko jednej metody zamawiania według. Wielokrotne wywołanie metody sortowania według w tym samym zapytaniu spowoduje zgłoszenie błędu.
Poniższy przykład pokazuje, jak można pobrać listę najlepszych postów użytkownika posortowaną według liczby gwiazdek:
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 // ... });
Definiuje to zapytanie, które w połączeniu z detektorem podrzędnym synchronizuje klienta z postami użytkownika ze ścieżki w bazie danych na podstawie jego identyfikatora użytkownika, uporządkowanego według liczby gwiazdek otrzymanych przez każdy post. Ta technika używania identyfikatorów jako kluczy indeksowych nazywana jest rozrzutem danych. Więcej informacji na ten temat można znaleźć w artykule Struktura bazy danych .
Wywołanie metody orderByChild()
określa klucz podrzędny, według którego mają zostać uporządkowane wyniki. W takim przypadku posty są sortowane według wartości odpowiadającego im elementu podrzędnego "starCount"
. Zapytania można również uporządkować według zagnieżdżonych elementów podrzędnych, jeśli masz dane, które wyglądają tak:
"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", } },
W tym przykładzie możemy uporządkować nasze elementy listy według wartości zagnieżdżonych w kluczu metrics
, określając ścieżkę względną do zagnieżdżonego elementu podrzędnego w naszym wywołaniu orderByChild()
.
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 // ... });
Aby uzyskać więcej informacji na temat porządkowania innych typów danych, zobacz Jak uporządkowane są dane zapytania .
Filtrowanie danych
Aby filtrować dane, podczas konstruowania zapytania można połączyć dowolną metodę limitu lub zakresu z metodą porządkowania według.
metoda | Stosowanie |
---|---|
limitToFirst() | Ustawia maksymalną liczbę elementów do zwrócenia od początku uporządkowanej listy wyników. |
limitToLast() | Ustawia maksymalną liczbę elementów do zwrócenia z końca uporządkowanej listy wyników. |
startAt() | Zwróć elementy większe lub równe podanemu kluczowi lub wartości w zależności od wybranej metody zamawiania. |
startAfter() | Zwróć elementy większe niż określony klucz lub wartość w zależności od wybranej metody zamawiania. |
endAt() | Zwracaj przedmioty mniejsze lub równe podanemu kluczowi lub wartości, w zależności od wybranej metody zamawiania. |
endBefore() | Zwracaj pozycje mniejsze niż określony klucz lub wartość w zależności od wybranej metody zamawiania. |
equalTo() | Zwróć elementy równe określonemu kluczowi lub wartości w zależności od wybranej metody zamawiania. |
W przeciwieństwie do metod sortowania według, można łączyć wiele funkcji limitów lub zakresów. Można na przykład połączyć metody startAt()
i endAt()
w celu ograniczenia wyników do określonego zakresu wartości.
Nawet jeśli istnieje tylko jedno dopasowanie do zapytania, migawka nadal jest listą; zawiera tylko jeden element. Aby uzyskać dostęp do elementu, musisz zapętlić wynik:
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()); // ... } });
Ogranicz liczbę wyników
Możesz użyć metod limitToFirst()
i limitToLast()
, aby ustawić maksymalną liczbę dzieci do zsynchronizowania dla danego wywołania zwrotnego. Na przykład, jeśli użyjesz limitToFirst()
do ustawienia limitu 100, początkowo otrzymasz tylko do 100 wywołań zwrotnych onChildAdded()
. Jeśli masz mniej niż 100 elementów przechowywanych w bazie danych Firebase, wywołanie zwrotne onChildAdded()
jest uruchamiane dla każdego elementu.
Gdy elementy się zmieniają, otrzymujesz wywołania zwrotne onChildAdded()
dla elementów, które wchodzą w zapytanie, oraz wywołania zwrotne onChildRemoved()
dla elementów, które z niego wypadają, dzięki czemu łączna liczba pozostaje na poziomie 100.
Poniższy przykład pokazuje, jak przykładowa aplikacja do blogowania definiuje zapytanie w celu pobrania listy 100 najnowszych postów wszystkich użytkowników:
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);
Ten przykład definiuje tylko zapytanie, aby faktycznie zsynchronizować dane, musi mieć dołączony odbiornik .
Filtruj według klucza lub wartości
startAt()
, startAfter()
, endAt()
, endBefore()
i equalTo()
można używać do wybierania dowolnych punktów początkowych, końcowych i równoważnych dla zapytań. Może to być przydatne do dzielenia danych na strony lub znajdowania elementów z elementami podrzędnymi, które mają określoną wartość.
Jak uporządkowane są dane zapytania
W tej sekcji wyjaśniono, w jaki sposób dane są sortowane według każdej z metod sortowania według w klasie Query
.
orderByChild
Podczas korzystania z orderByChild()
dane zawierające określony klucz podrzędny są uporządkowane w następujący sposób:
- Dzieci z wartością
null
dla określonego klucza podrzędnego są na pierwszym miejscu. - Dzieci z wartością
false
dla określonego klucza potomnego są następne. Jeśli wiele elementów podrzędnych ma wartośćfalse
, są one sortowane leksykograficznie według klucza. - Dzieci z wartością
true
dla określonego klucza podrzędnego są następne. Jeśli wiele elementów podrzędnych ma wartośćtrue
, są one sortowane leksykograficznie według klucza. - Następne są dzieci z wartością liczbową, posortowane rosnąco. Jeśli wiele elementów podrzędnych ma tę samą wartość liczbową dla określonego węzła podrzędnego, są one sortowane według klucza.
- Ciągi występują po liczbach i są sortowane leksykograficznie w porządku rosnącym. Jeśli wiele elementów podrzędnych ma tę samą wartość dla określonego węzła podrzędnego, są one uporządkowane leksykograficznie według klucza.
- Obiekty są na końcu i są sortowane leksykograficznie według klucza w porządku rosnącym.
orderByKey
Gdy używasz orderByKey()
do sortowania danych, dane są zwracane w porządku rosnącym według klucza.
- Dzieci z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, są pierwsze, posortowane w porządku rosnącym.
- Dzieci z wartością ciągu jako kluczem są następne, posortowane leksykograficznie w porządku rosnącym.
orderByValue
Podczas korzystania z orderByValue()
elementy podrzędne są sortowane według ich wartości. Kryteria porządkowania są takie same jak w przypadku orderByChild()
, z wyjątkiem tego, że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.