W tym dokumencie opisujemy pracę z listami danych w Firebase. Podstawowe informacje o odczytywaniu i zapisywaniu danych Firebase znajdziesz w artykule Odczytywanie i zapisywanie danych na Androidzie.
Pobieranie DatabaseReference
Aby odczytywać i zapisywać dane w bazie danych, potrzebujesz instancji DatabaseReference
:
Kotlin
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
Odczytywanie i zapisywanie list
Dołączanie do listy danych
Użyj metody push()
, aby dołączyć dane do listy w aplikacjach wielodostępnych.
Metoda push()
generuje unikalny klucz za każdym razem, gdy do określonego odwołania Firebase dodawane jest nowe dziecko. Dzięki używaniu 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. Klucz unikalny generowany przez push()
jest oparty na sygnaturze czasowej, więc elementy listy są automatycznie porządkowane chronologicznie.
Możesz użyć odwołania do nowych danych zwróconych przez metodę push()
, aby uzyskać wartość automatycznie wygenerowanego klucza elementu podrzędnego lub ustawić dane elementu podrzędnego. Wywołanie
getKey()
w przypadku odwołania push()
zwraca wartość klucza wygenerowanego automatycznie.
Możesz używać tych automatycznie generowanych kluczy, aby uprościć spłaszczanie struktury danych. Więcej informacji znajdziesz w przykładzie dotyczącym rozsyłania danych.
Nasłuchiwanie zdarzeń dotyczących dziecka
W przypadku list aplikacja powinna nasłuchiwać zdarzeń podrzędnych, a nie zdarzeń wartości używanych w przypadku pojedynczych obiektów.
Zdarzenia dotyczące elementów podrzędnych są wywoływane w odpowiedzi na określone operacje wykonywane na elementach podrzędnych węzła, np. dodanie nowego elementu podrzędnego za pomocą metody push()
lub zaktualizowanie elementu podrzędnego za pomocą metody updateChildren()
.
Każda z tych funkcji może być przydatna do śledzenia zmian w określonym węźle w bazie danych.
Aby nasłuchiwać zdarzeń dotyczących dzieci w DatabaseReference
, dołącz ChildEventListener
:
Odbiornik | Wywołanie zwrotne zdarzenia | Typowe zastosowanie |
---|---|---|
ChildEventListener
| onChildAdded() |
pobierać listy elementów lub nasłuchiwać dodawania elementów do listy;
Wywołanie zwrotne jest wywoływane raz dla każdego istniejącego węzła podrzędnego, a potem ponownie za każdym razem, gdy do określonej ścieżki zostanie dodany nowy węzeł podrzędny. Parametr DataSnapshot przekazywany do odbiorcy zawiera dane nowego dziecka.
|
onChildChanged() |
Monitorowanie zmian w elementach na liście. To zdarzenie jest wywoływane za każdym razem, gdy węzeł podrzędny jest modyfikowany, w tym gdy modyfikowane są elementy podrzędne węzła podrzędnego. Obiekt DataSnapshot przekazywany do odbiornika zdarzeń zawiera zaktualizowane dane dotyczące dziecka.
|
|
onChildRemoved() |
Nasłuchiwanie elementów usuwanych z listy. Obiekt DataSnapshot przekazywany do wywołania zwrotnego zdarzenia zawiera dane usuniętego elementu podrzędnego.
|
|
onChildMoved() |
Nasłuchiwanie zmian kolejności elementów na liście numerowanej.
To zdarzenie jest wywoływane za każdym razem, gdy wywołanie zwrotne onChildChanged()
jest wywoływane przez aktualizację, która powoduje zmianę kolejności elementu podrzędnego.
Jest używana w przypadku danych uporządkowanych za pomocą funkcji orderByChild lub orderByValue .
|
Na przykład aplikacja do blogowania społecznościowego może używać tych metod razem do monitorowania aktywności w komentarzach do posta, jak pokazano poniżej:
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);
Nasłuchiwanie zdarzeń wartości
Używanie ChildEventListener
to zalecany sposób odczytywania list danych, ale w niektórych sytuacjach przydatne jest dołączanie ValueEventListener
do odwołania do listy.
Dołączenie ValueEventListener
do listy danych spowoduje zwrócenie całej listy danych jako pojedynczego elementu DataSnapshot
, po którym można iterować, aby uzyskać dostęp do poszczególnych elementów podrzędnych.
Nawet jeśli zapytanie zwraca tylko 1 wynik, migawka jest listą, która zawiera tylko 1 element. Aby uzyskać dostęp do elementu, musisz wykonać pętlę po wyniku:
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()); // ... } });
Ten wzorzec może być przydatny, gdy chcesz pobrać wszystkie elementy podrzędne listy w ramach jednej operacji, zamiast nasłuchiwać dodatkowych zdarzeń onChildAdded
.
Odłączanie detektorów
Wywołania zwrotne są usuwane przez wywołanie metody removeEventListener()
w odniesieniu do bazy danych Firebase.
Jeśli odbiornik został dodany do lokalizacji danych kilka razy, jest wywoływany kilka razy w przypadku każdego zdarzenia. Aby go całkowicie usunąć, musisz go odłączyć tyle samo razy.
Wywołanie removeEventListener()
na odbiorniku rodzica nie powoduje automatycznego usunięcia odbiorników zarejestrowanych w jego węzłach podrzędnych. Aby usunąć wywołanie zwrotne, należy też wywołać removeEventListener()
na wszystkich odbiornikach podrzędnych.
Sortowanie i filtrowanie danych
Możesz użyć klasy Realtime Database Query
, aby pobrać dane posortowane według klucza, wartości lub wartości elementu podrzędnego. Możesz też filtrować posortowane wyniki, aby uzyskać określoną liczbę wyników lub zakres kluczy lub wartości.
Sortowanie danych
Aby pobrać posortowane dane, zacznij od określenia jednej z metod sortowania, aby określić sposób sortowania wyników:
Metoda | Wykorzystanie |
---|---|
orderByChild() |
Sortowanie wyników według wartości określonego klucza podrzędnego lub zagnieżdżonej ścieżki podrzędnej. |
orderByKey()
| Sortuj wyniki według kluczy podrzędnych. |
orderByValue() |
Sortuj wyniki według wartości elementów podrzędnych. |
Możesz używać tylko jednej metody zamawiania naraz. Wywołanie metody order-by kilka razy w tym samym zapytaniu powoduje błąd.
Poniższy przykład pokazuje, jak pobrać listę najpopularniejszych postów użytkownika posortowanych według liczby gwiazdek:
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 // ... });
Definiuje zapytanie, które w połączeniu z elementem nasłuchującym podrzędnym synchronizuje klienta z postami użytkownika z ścieżki w bazie danych na podstawie jego identyfikatora, uporządkowanymi według liczby gwiazdek, które otrzymał każdy post. Technika używania identyfikatorów jako kluczy indeksu nazywa się rozgałęzianiem danych. Więcej informacji znajdziesz w artykule Struktura bazy danych.
Wywołanie metody orderByChild()
określa klucz podrzędny, według którego mają być uporządkowane wyniki. W tym przypadku posty są sortowane według wartości odpowiedniego "starCount"
elementu podrzędnego. Zapytania można też porządkować według zagnieżdżonych elementów podrzędnych, jeśli masz dane w takim formacie:
"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ć elementy listy według wartości zagnieżdżonych pod kluczem metrics
, określając względną ścieżkę do zagnieżdżonego elementu podrzędnego w wywołaniu orderByChild()
.
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 // ... });
Więcej informacji o kolejności innych typów danych znajdziesz w artykule Jak są uporządkowane dane zapytań.
Filtrowanie danych
Aby filtrować dane, możesz łączyć dowolne metody ograniczania lub zakresu z metodą sortowania podczas tworzenia zapytania.
Metoda | Wykorzystanie |
---|---|
limitToFirst() |
Określa maksymalną liczbę elementów do zwrócenia z 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() |
Zwraca elementy o wartości klucza lub wartości większej lub równej podanej wartości, w zależności od wybranej metody sortowania. |
startAfter() |
Zwraca elementy o wartości większej niż określony klucz lub wartość w zależności od wybranej metody sortowania. |
endAt() |
Zwraca elementy mniejsze lub równe podanemu kluczowi lub wartości w zależności od wybranej metody sortowania. |
endBefore() |
Zwraca produkty o wartości mniejszej niż określony klucz lub wartość w zależności od wybranej metody sortowania. |
equalTo() |
Zwraca elementy równe określonemu kluczowi lub wartości w zależności od wybranej metody sortowania. |
W przeciwieństwie do metod sortowania możesz łączyć wiele funkcji limitu lub zakresu.
Możesz np. połączyć metody startAt()
i endAt()
, aby ograniczyć wyniki do określonego zakresu wartości.
Nawet jeśli zapytanie zwraca tylko 1 wynik, migawka jest listą, tylko że zawiera 1 element. Aby uzyskać dostęp do elementu, musisz przejść przez wynik w pętli:
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()); // ... } });
Ograniczanie liczby wyników
Za pomocą metod limitToFirst()
i limitToLast()
możesz ustawić maksymalną liczbę dzieci, które mają być synchronizowane w przypadku danego wywołania zwrotnego. Jeśli na przykład użyjesz symbolu limitToFirst()
, aby ustawić limit 100, początkowo otrzymasz tylko do 100 wywołań zwrotnych onChildAdded()
. Jeśli w bazie danych Firebase masz mniej niż 100 elementów, wywoływane jest wywołanie zwrotne onChildAdded()
dla każdego z nich.
W miarę zmian w produktach otrzymujesz wywołania zwrotne onChildAdded()
dla produktów, które pojawiają się w zapytaniu, i wywołania zwrotne onChildRemoved()
dla produktów, które z niego znikają, dzięki czemu łączna liczba pozostaje na poziomie 100.
Poniższy przykład pokazuje, jak przykładowa aplikacja do blogowania definiuje zapytanie, aby pobrać listę 100 najnowszych postów wszystkich użytkowników:
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);
Ten przykład definiuje tylko zapytanie. Aby zsynchronizować dane, musi mieć dołączony odbiornik.
Filtrowanie według klucza lub wartości
Możesz użyć zasad startAt()
, startAfter()
, endAt()
, endBefore()
i equalTo()
, aby wybrać dowolne punkty początkowe, końcowe i równoważności dla zapytań. Może to być przydatne w przypadku stronicowania danych lub wyszukiwania elementów podrzędnych o określonej wartości.
Sposób uporządkowania danych zapytania
W tej sekcji wyjaśniamy, jak dane są sortowane za pomocą każdej z metod sortowania w klasie Query
.
orderByChild
Gdy używasz orderByChild()
, dane zawierające określony klucz podrzędny są porządkowane w ten sposób:
- Najpierw pojawiają się dzieci z wartością
null
dla określonego klucza dziecka. - Następnie wyświetlane są dzieci z wartością
false
dla określonego klucza podrzędnego. Jeśli kilka elementów podrzędnych ma wartośćfalse
, są one sortowane leksykograficznie według klucza. - Następnie wyświetlane są dzieci z wartością
true
dla określonego klucza podrzędnego. Jeśli kilka elementów podrzędnych ma wartośćtrue
, są one sortowane leksykograficznie według klucza. - Następnie pojawiają się elementy podrzędne z wartością liczbową, posortowane w kolejności rosnącej. Jeśli kilka elementów podrzędnych ma tę samą wartość liczbową w przypadku określonego węzła podrzędnego, są one sortowane według klucza.
- Ciągi znaków występują po liczbach i są sortowane leksykograficznie w kolejności rosnącej. Jeśli kilka węzłów podrzędnych ma tę samą wartość, są one uporządkowane leksykograficznie według klucza.
- Obiekty są umieszczane na końcu i sortowane leksykograficznie według klucza w kolejności rosnącej.
orderByKey
Gdy używasz funkcji orderByKey()
do sortowania danych, są one zwracane w kolejności rosnącej według klucza.
- Najpierw pojawiają się dzieci z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, posortowane w kolejności rosnącej.
- Następnie pojawiają się dzieci z wartością tekstową jako kluczem, posortowane leksykograficznie w kolejności rosnącej.
orderByValue
W przypadku korzystania z orderByValue()
dzieci są uporządkowane według wartości. Kryteria sortowania są takie same jak w przypadku orderByChild()
, z tym że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.