Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie z siecią. Firebase udostępnia też narzędzia do utrwalania danych lokalnie, zarządzania obecnością i obsługi opóźnień.
Trwałość na dysku
Aplikacje Firebase automatycznie obsługują tymczasowe przerwy w połączeniu z siecią. Dane w pamięci podręcznej są dostępne w trybie offline, a Firebase ponownie wysyła wszystkie zapisy po przywróceniu połączenia z siecią.
Gdy włączysz trwałość na dysku, aplikacja zapisuje dane lokalnie na urządzeniu, dzięki czemu może zachować stan w trybie offline, nawet jeśli użytkownik lub system operacyjny ponownie uruchomi aplikację.
Trwałość na dysku możesz włączyć za pomocą tylko 1 wiersza kodu.
FirebaseDatabase.instance.setPersistenceEnabled(true);
Zachowanie trwałości
Gdy włączysz trwałość, wszystkie dane, które klient Bazy danych czasu rzeczywistego Firebase synchronizuje w trybie online, będą utrwalane na dysku i dostępne w trybie offline, nawet jeśli użytkownik lub system operacyjny ponownie uruchomi aplikację. Oznacza to, że aplikacja działa tak samo jak w trybie online, korzystając z danych lokalnych przechowywanych w pamięci podręcznej. Wywołania zwrotne detektora będą nadal aktywowane w przypadku aktualizacji lokalnych.
Klient Bazy danych czasu rzeczywistego Firebase automatycznie utrzymuje kolejkę wszystkich operacji zapisu wykonywanych, gdy aplikacja jest offline. Gdy trwałość jest włączona, ta kolejka jest też utrwalana na dysku, dzięki czemu wszystkie zapisy są dostępne, gdy użytkownik lub system operacyjny ponownie uruchomi aplikację. Gdy aplikacja odzyska połączenie, wszystkie operacje zostaną wysłane do serwera Bazy danych czasu rzeczywistego Firebase.
Jeśli aplikacja korzysta z Uwierzytelniania Firebase, klient Bazy danych czasu rzeczywistego Firebase utrwala token uwierzytelniania użytkownika podczas ponownego uruchamiania aplikacji. Jeśli token uwierzytelniania wygaśnie, gdy aplikacja jest offline, klient wstrzyma operacje zapisu do czasu, aż aplikacja ponownie uwierzytelni użytkownika. W przeciwnym razie operacje zapisu mogą się nie powieść z powodu reguł zabezpieczeń.
Odświeżanie danych
Baza danych czasu rzeczywistego Firebase synchronizuje i przechowuje lokalną kopię danych dla aktywnych detektorów. Możesz też synchronizować określone lokalizacje.
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);
Klient Bazy danych czasu rzeczywistego Firebase automatycznie pobiera dane z tych lokalizacji i synchronizuje je, nawet jeśli odwołanie nie ma aktywnych detektorów. Synchronizację możesz wyłączyć za pomocą tego wiersza kodu.
scoresRef.keepSynced(false);
Domyślnie w pamięci podręcznej przechowywane jest 10 MB wcześniej zsynchronizowanych danych. Powinno to wystarczyć w przypadku większości aplikacji. Jeśli pamięć podręczna przekroczy skonfigurowany rozmiar, Baza danych czasu rzeczywistego Firebase usunie dane, które były używane najrzadziej. Dane, które są synchronizowane, nie są usuwane z pamięci podręcznej.
Wysyłanie zapytań o dane w trybie offline
Baza danych czasu rzeczywistego Firebase przechowuje dane zwrócone przez zapytanie do wykorzystania w trybie offline. W przypadku zapytań utworzonych w trybie offline Baza danych czasu rzeczywistego Firebase nadal działa na podstawie wcześniej załadowanych danych. Jeśli żądane dane nie zostały załadowane, Baza danych czasu rzeczywistego Firebase załaduje dane z lokalnej pamięci podręcznej. Gdy połączenie z siecią zostanie przywrócone, dane zostaną załadowane i będą odzwierciedlać zapytanie.
Ten kod wysyła na przykład zapytanie o 4 ostatnie elementy w bazie danych wyników:
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.orderByValue().limitToLast(4).onChildAdded.listen((event) {
debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});
Załóżmy, że użytkownik traci połączenie, przechodzi w tryb offline i ponownie uruchamia aplikację. W trybie offline aplikacja wysyła zapytanie o 2 ostatnie elementy z tej samej lokalizacji. To zapytanie zwróci 2 ostatnie elementy, ponieważ aplikacja załadowała wszystkie 4 elementy w zapytaniu powyżej.
scoresRef.orderByValue().limitToLast(2).onChildAdded.listen((event) {
debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});
W poprzednim przykładzie klient Bazy danych czasu rzeczywistego Firebase wywołuje zdarzenia „child added” dla 2 dinozaurów z najwyższymi wynikami, korzystając z utrwalonej pamięci podręcznej. Nie wywoła jednak zdarzenia „value”, ponieważ aplikacja nigdy nie wykonała tego zapytania w trybie online.
Jeśli aplikacja poprosi o 6 ostatnich elementów w trybie offline, od razu otrzyma zdarzenia „child added” dla 4 elementów w pamięci podręcznej. Gdy urządzenie wróci do trybu online, klient Bazy danych czasu rzeczywistego Firebase zsynchronizuje się z serwerem i otrzyma 2 ostatnie zdarzenia „child added” oraz zdarzenie „value” dla aplikacji.
Obsługa transakcji w trybie offline
Wszystkie transakcje wykonywane, gdy aplikacja jest offline, są umieszczane w kolejce. Gdy aplikacja odzyska połączenie z siecią, transakcje zostaną wysłane do serwera Bazy danych czasu rzeczywistego.
Baza danych czasu rzeczywistego Firebase ma wiele funkcji do obsługi scenariuszy offline i połączeń z siecią. Pozostała część tego przewodnika dotyczy Twojej aplikacji niezależnie od tego, czy masz włączoną trwałość.
Zarządzanie obecnością
W aplikacjach czasu rzeczywistego często przydaje się wykrywanie, kiedy klienci się łączą i rozłączają. Możesz na przykład oznaczyć użytkownika jako „offline”, gdy jego klient się rozłączy.
Klienci Bazy danych Firebase udostępniają proste elementy, których możesz użyć do zapisywania w bazie danych, gdy klient rozłączy się z serwerami Bazy danych Firebase. Te aktualizacje występują niezależnie od tego, czy klient rozłączy się prawidłowo, czy nie, więc możesz na nich polegać, aby zwolnić miejsce, nawet jeśli połączenie zostanie przerwane lub klient ulegnie awarii. Po rozłączeniu można wykonywać wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie.
Oto prosty przykład zapisywania danych po rozłączeniu za pomocą elementu onDisconnect:
final presenceRef = FirebaseDatabase.instance.ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
Jak działa onDisconnect
Gdy utworzysz operację onDisconnect(), będzie ona działać na serwerze Bazy danych czasu rzeczywistego Firebase. Serwer sprawdza zabezpieczenia, aby upewnić się, że użytkownik może wykonać żądane zdarzenie zapisu, i informuje aplikację, jeśli jest ono nieprawidłowe. Następnie serwer monitoruje połączenie. Jeśli w dowolnym momencie połączenie zostanie przerwane lub aktywnie zamknięte przez klienta Bazy danych czasu rzeczywistego, serwer ponownie sprawdzi zabezpieczenia (aby upewnić się, że operacja jest nadal prawidłowa), a następnie wywoła zdarzenie.
try {
await presenceRef.onDisconnect().remove();
} catch (error) {
debugPrint("Could not establish onDisconnect event: $error");
}
Zdarzenie onDisconnect można też anulować, wywołując metodę .cancel():
final onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();
Wykrywanie stanu połączenia
W przypadku wielu funkcji związanych z obecnością przydaje się, gdy aplikacja wie, czy jest online, czy offline. Baza danych czasu rzeczywistego Firebase udostępnia specjalną lokalizację /.info/connected, która jest aktualizowana za każdym razem, gdy zmienia się stan połączenia klienta Bazy danych czasu rzeczywistego Firebase. Oto przykład:
final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
final connected = event.snapshot.value as bool? ?? false;
if (connected) {
debugPrint("Connected.");
} else {
debugPrint("Not connected.");
}
});
/.info/connected to wartość logiczna, która nie jest synchronizowana między klientami Bazy danych czasu rzeczywistego, ponieważ zależy od stanu klienta. Innymi słowy, jeśli jeden klient odczyta /.info/connected jako false, nie ma gwarancji, że inny klient też odczyta false.
Obsługa opóźnień
Sygnatury czasowe serwera
Serwery Bazy danych czasu rzeczywistego Firebase udostępniają mechanizm wstawiania sygnatur czasowych generowanych na serwerze jako danych. Ta funkcja w połączeniu z onDisconnect ułatwia niezawodne zapisywanie czasu, w którym klient Bazy danych czasu rzeczywistego rozłączył się:
final userLastOnlineRef =
FirebaseDatabase.instance.ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(ServerValue.timestamp);
Różnica czasu
Chociaż ServerValue.timestamp jest znacznie dokładniejsza i preferowana w przypadku większości operacji odczytu i zapisu, czasami może się przydać oszacowanie różnicy czasu między zegarem klienta a serwerami Bazy danych czasu rzeczywistego Firebase. Aby uzyskać wartość w milisekundach, którą klienci Bazy danych czasu rzeczywistego Firebase dodają do lokalnego czasu (czas epoki w milisekundach), aby oszacować czas serwera, możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset. Pamiętaj, że na dokładność tego przesunięcia może wpływać opóźnienie sieci, dlatego przydaje się ono głównie do wykrywania dużych (> 1 sekunda) różnic w czasie zegara.
final offsetRef = FirebaseDatabase.instance.ref(".info/serverTimeOffset");
offsetRef.onValue.listen((event) {
final offset = event.snapshot.value as num? ?? 0.0;
final estimatedServerTimeMs =
DateTime.now().millisecondsSinceEpoch + offset;
});
Przykładowa aplikacja do zarządzania obecnością
Łącząc operacje rozłączania z monitorowaniem stanu połączenia i sygnaturami czasowymi serwera, możesz utworzyć system obecności użytkowników. W tym systemie każdy użytkownik przechowuje dane w lokalizacji bazy danych, aby wskazać, czy klient Bazy danych czasu rzeczywistego jest online. Gdy klienci przechodzą do trybu online, ustawiają tę lokalizację na true, a gdy się rozłączają, ustawiają sygnaturę czasową. Ta sygnatura czasowa wskazuje, kiedy dany użytkownik był ostatnio online.
Pamiętaj, że aplikacja powinna umieścić operacje rozłączania w kolejce, zanim użytkownik zostanie oznaczony jako online, aby uniknąć sytuacji wyścigu, gdy połączenie sieciowe klienta zostanie utracone, zanim oba polecenia zostaną wysłane do serwera.
// Since I can connect from multiple devices, we store each connection
// instance separately any time that connectionsRef's value is null (i.e.
// has no children) I am offline.
final myConnectionsRef =
FirebaseDatabase.instance.ref("users/joe/connections");
// Stores the timestamp of my last disconnect (the last time I was seen online)
final lastOnlineRef =
FirebaseDatabase.instance.ref("/users/joe/lastOnline");
final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
final connected = event.snapshot.value as bool? ?? false;
if (connected) {
final con = myConnectionsRef.push();
// When this device disconnects, remove it.
con.onDisconnect().remove();
// When I disconnect, update the last time I was seen online.
lastOnlineRef.onDisconnect().set(ServerValue.timestamp);
// Add this device to my connections list.
// This value could contain info about the device or a timestamp too.
con.set(true);
}
});