(Opcjonalnie) Prototypowanie i testowanie w Pakiecie emulatorów Firebase
Zanim opowiemy o tym, jak Twoja aplikacja odczytuje dane w bazie danych czasu rzeczywistego i zapisy w niej, przedstawimy zestaw narzędzi, które można wykorzystać do prototypowania i testowania Bazy danych czasu rzeczywistego pakietu emulatorów Firebase. Jeśli testujesz inne dane optymalizowanie reguł zabezpieczeń lub wyszukiwanie opłacalny sposób interakcji z backendem, który daje możliwość pracy lokalnej bez wdrażania aktywnych usług może być świetnym pomysłem.
Emulator Bazy danych czasu rzeczywistego jest częścią Pakietu emulatorów, który umożliwia aplikacji interakcję z treścią i konfiguracją emulowanej bazy danych a także opcjonalnie emulowane zasoby projektu (funkcje, inne bazy danych, i reguły zabezpieczeń).emulator_suite_short
Aby użyć emulatora Bazy danych czasu rzeczywistego, wystarczy kilka kroków:
- Dodajesz wiersz kodu do konfiguracji testowej aplikacji, aby połączyć się z emulatorem.
- Uruchomienie
firebase emulators:start
w katalogu głównym projektu lokalnego. - Wykonywanie wywołań z prototypowego kodu aplikacji za pomocą platformy Bazy danych czasu rzeczywistego pakietu SDK lub interfejsu API REST Realtime Database.
Dostępny jest szczegółowy instrukcja korzystania z Bazy danych czasu rzeczywistego i Cloud Functions. Zapoznaj się też z wprowadzeniem do Pakietu emulatorów.
Pobieranie odniesienia do bazy danych
Aby móc odczytywać lub zapisywać dane z bazy danych, musisz mieć instancję
DatabaseReference
:
DatabaseReference ref = FirebaseDatabase.instance.ref();
Zapisywanie danych
Ten dokument zawiera podstawowe informacje o odczytywaniu i zapisywaniu danych Firebase.
Dane Firebase są zapisywane w DatabaseReference
i pobierane przez
Oczekiwanie lub nasłuchiwanie zdarzeń wysyłanych przez odwołanie. Zdarzenia są emitowane
raz dla początkowego stanu danych i ponownie po każdej zmianie danych.
Podstawowe operacje zapisu
W przypadku podstawowych operacji zapisu możesz użyć funkcji set()
, aby zapisać dane w określonym
zastępując wszystkie istniejące dane w tej ścieżce. Możesz utworzyć odwołanie,
na te typy: String
, boolean
, int
, double
, Map
, List
.
Użytkownik z adresem set()
możesz na przykład dodać w ten sposób:
DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");
await ref.set({
"name": "John",
"age": 18,
"address": {
"line1": "100 Mountain View"
}
});
Użycie tego parametru set()
spowoduje zastąpienie danych w określonej lokalizacji,
łącznie z węzłami podrzędnymi. Nadal możesz jednak zaktualizować dziecko bez
cały obiekt jest tworzony na nowo. Jeśli chcesz zezwolić użytkownikom na aktualizowanie swoich profili
możesz zmienić nazwę użytkownika w następujący sposób:
DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");
// Only update the name, leave the age and address!
await ref.update({
"age": 19,
});
Metoda update()
akceptuje ścieżkę podrzędną do węzłów, co pozwala aktualizować wiele
węzłów w bazie danych:
DatabaseReference ref = FirebaseDatabase.instance.ref("users");
await ref.update({
"123/age": 19,
"123/address/line1": "1 Mountain View",
});
Odczyt danych
Odczytuj dane przez nasłuchiwanie zdarzeń wartości
Aby odczytać dane na ścieżce i nasłuchiwać zmian, użyj funkcji
Właściwość onValue
elementu DatabaseReference
do nasłuchiwania
DatabaseEvent
s
Za pomocą DatabaseEvent
możesz odczytywać dane na określonej ścieżce,
aktualny w momencie wystąpienia zdarzenia. To zdarzenie jest wywoływane raz, gdy parametr
za każdym razem, gdy dane, w tym dzieci,
zmian. Zdarzenie ma właściwość snapshot
zawierającą wszystkie dane w tym
lokalizacji, w tym danych podrzędnych. Jeśli nie ma danych, zrzut
Właściwość exists
będzie miała wartość false
, a jej właściwość value
będzie null.
W poniższym przykładzie pokazano, jak aplikacja do blogowania społecznościowego pobiera szczegóły posta z bazy danych:
DatabaseReference starCountRef =
FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
updateStarCount(data);
});
Detektor odbiera obiekt DataSnapshot
zawierający dane w określonym miejscu
znajduje się w bazie danych w chwili wystąpienia zdarzenia we właściwości value
.
Odczytaj dane raz
Odczytaj raz za pomocą get()
Pakiet SDK służy do zarządzania interakcjami z serwerami baz danych niezależnie od tego, czy jest online lub offline.
Ogólnie do odczytu wartości zdarzeń należy używać opisanych powyżej technik aby otrzymywać powiadomienia o aktualizacjach danych z backendu. Te techniki zmniejszają wykorzystanie zasobów i płatności, są zoptymalizowane, aby zapewniać użytkownikom korzystają z internetu i offline.
Jeśli dane są potrzebne tylko raz, możesz użyć funkcji get()
, aby uzyskać zrzut
z bazy danych. Jeśli z jakiegoś powodu get()
nie może zwrócić
wartość serwera, klient sprawdzi pamięć podręczną w lokalnej pamięci masowej i zwróci błąd
, jeśli nadal nie możesz znaleźć wartości.
Ten przykład pokazuje, jak pobrać nazwę użytkownika widoczną publicznie z bazy danych:
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('users/$userId').get();
if (snapshot.exists) {
print(snapshot.value);
} else {
print('No data available.');
}
Niepotrzebne użycie get()
może zwiększyć wykorzystanie przepustowości i doprowadzić do utraty
wydajności. Można to zapobiec za pomocą detektora w czasie rzeczywistym,
powyżej.
Odczytaj dane raz za pomocą funkcji Raz()
W niektórych przypadkach możesz chcieć, aby zwracana była wartość z lokalnej pamięci podręcznej
natychmiast, bez sprawdzania aktualizacji na serwerze. W tych
przypadków możesz użyć funkcji once()
, aby pobrać dane z pamięci podręcznej dysku lokalnego
natychmiast.
Jest to przydatne w przypadku danych, które trzeba wczytać tylko raz i w pewnym stopniu często się zmieniają lub wymagają aktywnego słuchania. Na przykład aplikacja do blogowania w poprzednich przykładach korzysta z tej metody do wczytywania profilu użytkownika, gdy rozpocznij tworzenie nowego posta:
final event = await ref.once(DatabaseEventType.value);
final username = event.snapshot.value?.username ?? 'Anonymous';
Aktualizowanie lub usuwanie danych
Zaktualizuj określone pola
Jednoczesne zapisywanie do określonych elementów podrzędnych węzła bez zastępowania innych
węzłów podrzędnych, użyj metody update()
.
Wywołując update()
, możesz zaktualizować wartości podrzędne niższego poziomu przez
i podaj ścieżkę dostępu do klucza. Jeśli dane są przechowywane w wielu lokalizacjach na potrzeby skalowania
możesz zaktualizować wszystkie wystąpienia tych danych za pomocą funkcji
rozpowszechnianie danych. Na przykład plik
aplikacja do obsługi blogów społecznościowych może utworzyć posta i jednocześnie zaktualizować go
kanał ostatniej aktywności i kanał aktywności użytkownika publikującego. W tym celu
aplikacja do tworzenia blogów używa takiego kodu:
void writeNewPost(String uid, String username, String picture, String title,
String body) async {
// A post entry.
final postData = {
'author': username,
'uid': uid,
'body': body,
'title': title,
'starCount': 0,
'authorPic': picture,
};
// Get a key for a new Post.
final newPostKey =
FirebaseDatabase.instance.ref().child('posts').push().key;
// Write the new post's data simultaneously in the posts list and the
// user's post list.
final Map<String, Map> updates = {};
updates['/posts/$newPostKey'] = postData;
updates['/user-posts/$uid/$newPostKey'] = postData;
return FirebaseDatabase.instance.ref().update(updates);
}
W tym przykładzie użyto metody push()
do utworzenia w węźle posta posta zawierającego posty dla:
wszystkich użytkowników w organizacji /posts/$postid
i jednocześnie pobieraj klucz za pomocą polecenia
key
Za pomocą klucza można utworzyć drugi wpis w
posty na blogu /user-posts/$userid/$postid
.
Korzystając z tych ścieżek, można wprowadzać zmiany w wielu lokalizacjach jednocześnie
drzewo JSON z pojedynczym wywołaniem update()
, jak w tym przykładzie
utworzy nowy post w obu lokalizacjach. Równoczesne aktualizacje dokonywane w ten sposób
są niepodzielne: wszystkie aktualizacje zakończą się sukcesem, albo wszystkie aktualizacje kończą się niepowodzeniem.
Dodaj pełne wywołanie zwrotne
Jeśli chcesz się dowiedzieć, kiedy Twoje dane zostały zatwierdzone, zarejestruj się
wywołania zwrotne do ukończenia. Zarówno set()
, jak i update()
zwracają wartości Future
, do których
możesz dołączyć wywołania zwrotne powodzenia i błędu, które są wywoływane, gdy zapis ma
dla bazy danych i gdy wywołanie nie powiodło się.
FirebaseDatabase.instance
.ref('users/$userId/email')
.set(emailAddress)
.then((_) {
// Data saved successfully!
})
.catchError((error) {
// The write failed...
});
Usuń dane
Najprostszym sposobem usunięcia danych jest wywołanie funkcji remove()
w odniesieniu do
lokalizacji danych.
Możesz też usunąć wartość, określając wartość null w innej operacji zapisu.
na przykład set()
lub update()
. Tej techniki możesz używać w połączeniu z usługą update()
, aby:
usunąć wiele elementów podrzędnych w jednym wywołaniu interfejsu API.
Zapisywanie danych jako transakcji
Podczas pracy z danymi, które mogą ulec uszkodzeniu na skutek równoczesnych modyfikacji,
takich jak liczniki przyrostowe, możesz użyć transakcji, przekazując
modułu obsługi transakcji na runTransaction()
. Moduł obsługi transakcji pobiera
bieżącego stanu danych jako argumentu oraz
zwraca nowy pożądany stan, który ma być zapisany. Jeśli inny klient
zapisuje w lokalizacji przed zapisaniem nowej wartości,
funkcja aktualizacji jest wywoływana ponownie z nową bieżącą wartością, a zapis
podjęto próbę ponowienia próby.
Na przykład w przykładowej aplikacji do blogowania społecznościowego użytkownicy mogą oznaczać gwiazdką i usuwać oznaczenia gwiazdką oraz śledzić liczbę gwiazdek przyznanych postom:
void toggleStar(String uid) async {
DatabaseReference postRef =
FirebaseDatabase.instance.ref("posts/foo-bar-123");
TransactionResult result = await postRef.runTransaction((Object? post) {
// Ensure a post at the ref exists.
if (post == null) {
return Transaction.abort();
}
Map<String, dynamic> _post = Map<String, dynamic>.from(post as Map);
if (_post["stars"] is Map && _post["stars"][uid] != null) {
_post["starCount"] = (_post["starCount"] ?? 1) - 1;
_post["stars"][uid] = null;
} else {
_post["starCount"] = (_post["starCount"] ?? 0) + 1;
if (!_post.containsKey("stars")) {
_post["stars"] = {};
}
_post["stars"][uid] = true;
}
// Return the new data.
return Transaction.success(_post);
});
}
Domyślnie zdarzenia są zgłaszane przy każdym uruchomieniu funkcji aktualizacji transakcji,
więc gdy uruchomisz funkcję wiele razy, możesz zobaczyć stany pośrednie.
Możesz ustawić applyLocally
na false
, aby pomijać te stany pośrednie i
zamiast tego poczekaj na zakończenie transakcji przed wygenerowaniem zdarzeń:
await ref.runTransaction((Object? post) {
// ...
}, applyLocally: false);
Wynikiem transakcji jest plik TransactionResult
, który zawiera informacje
takie jak to, czy transakcja została zatwierdzona, oraz nowy zrzut:
DatabaseReference ref = FirebaseDatabase.instance.ref("posts/123");
TransactionResult result = await ref.runTransaction((Object? post) {
// ...
});
print('Committed? ${result.committed}'); // true / false
print('Snapshot? ${result.snapshot}'); // DataSnapshot
Anulowanie transakcji
Jeśli chcesz bezpiecznie anulować transakcję, zadzwoń pod numer Transaction.abort()
, aby
wyślij AbortTransactionException
:
TransactionResult result = await ref.runTransaction((Object? user) {
if (user !== null) {
return Transaction.abort();
}
// ...
});
print(result.committed); // false
Atomic przyrosty po stronie serwera
W powyższym przypadku użycia zapisujemy w bazie danych 2 wartości: identyfikator użytkownik, który oznacza post lub oznaczenie gwiazdką posta, i zwiększona liczba gwiazdek. Jeśli że użytkownik oznaczy post gwiazdką, możemy użyć przyrostu atomowego zamiast transakcji.
void addStar(uid, key) async {
Map<String, Object?> updates = {};
updates["posts/$key/stars/$uid"] = true;
updates["posts/$key/starCount"] = ServerValue.increment(1);
updates["user-posts/$key/stars/$uid"] = true;
updates["user-posts/$key/starCount"] = ServerValue.increment(1);
return FirebaseDatabase.instance.ref().update(updates);
}
Ten kod nie korzysta z operacji transakcji, więc nie pobiera automatycznie w przypadku wystąpienia konfliktu aktualizacji uruchom ponownie system. Ponieważ jednak operacja przyrostu odbywa się bezpośrednio na serwerze bazy danych, nie występuje konflikt.
Jeśli chcesz wykrywać i odrzucać konflikty dotyczące aplikacji, na przykład w postach, które zostały już wcześniej oznaczone gwiazdką, należy napisać niestandardowy dla konkretnego przypadku użycia.
Praca z danymi w trybie offline
Jeśli klient utraci połączenie sieciowe, aplikacja będzie nadal działać .
Każdy klient połączony z bazą danych Firebase ma własną wersję wewnętrzną wszystkich aktywnych danych. Podczas zapisywania danych są one zapisywane w tej wersji lokalnej . Klient Firebase następnie synchronizuje te dane ze zdalną bazą danych. z serwerami Google i innymi klientami w ramach „najlepszych starań”, podstaw.
W rezultacie wszystkie zapisy w bazie danych wywołują zdarzenia lokalne natychmiast, przed jakiekolwiek dane są zapisywane na serwerze. Oznacza to, że aplikacja pozostaje niezależnie od opóźnienia sieciowego czy połączenia.
Po przywróceniu połączenia aplikacja otrzyma odpowiedni zestaw zdarzeń, aby klient synchronizował się z bieżącym stanem serwera bez konieczności napisać dowolny niestandardowy kod.
Więcej informacji o zachowaniu użytkowników offline Więcej informacji o możliwościach pracy online i offline