Włączanie funkcji offline

Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie z siecią. Dodatkowo Firebase udostępnia narzędzia do trwałego przechowywania danych lokalnie, zarządzania obecnością i obsługi opóźnień.

Trwałość dysku

Aplikacje Firebase automatycznie radzą sobie z tymczasowymi przerwami w działaniu sieci. Dane w pamięci podręcznej są dostępne w trybie offline, a Firebase ponownie wysyła wszystkie zapisy, gdy połączenie sieciowe zostanie przywrócone.

Gdy włączysz trwałość dysku, aplikacja zapisuje dane lokalnie na urządzeniu, dzięki czemu może zachowywać stan w trybie offline, nawet jeśli użytkownik lub system operacyjny ponownie uruchomi aplikację.

Trwałość dysku możesz włączyć za pomocą tylko jednego wiersza kodu.

FirebaseDatabase.instance.setPersistenceEnabled(true);

Zachowanie trwałości

Po włączeniu trwałości wszystkie dane, które klient Bazy danych czasu rzeczywistego Firebase synchronizuje w trybie online, są zapisywane na dysku i dostępne w trybie offline, nawet gdy użytkownik lub system operacyjny ponownie uruchomi aplikację. Oznacza to, że aplikacja działa tak samo jak w trybie online, korzystając z lokalnych danych przechowywanych w pamięci podręcznej. Wywołania zwrotne odbiorcy będą nadal wywoływane w przypadku lokalnych aktualizacji.

Klient Bazy danych czasu rzeczywistego Firebase automatycznie przechowuje kolejkę wszystkich operacji zapisu wykonywanych, gdy aplikacja jest w trybie offline. Gdy trwałość jest włączona, ta kolejka jest też zapisywana 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 są wysyłane na serwer Bazy danych czasu rzeczywistego Firebase.

Jeśli Twoja aplikacja korzysta z Uwierzytelniania Firebase, klient Bazy danych czasu rzeczywistego Firebase przechowuje token uwierzytelniania użytkownika podczas ponownego uruchamiania aplikacji. Jeśli token uwierzytelniania wygaśnie, gdy aplikacja jest offline, klient wstrzyma operacje zapisu do momentu ponownego uwierzytelnienia użytkownika przez aplikację. 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 odbiorców. Synchronizację możesz ponownie 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 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 w przypadku wcześniej załadowanych danych. Jeśli żądane dane nie zostały wczytane, Baza danych czasu rzeczywistego Firebase wczytuje dane z lokalnej pamięci podręcznej. Gdy połączenie sieciowe będzie ponownie dostępne, dane zostaną wczytane i będą odzwierciedlać zapytanie.

Na przykład ten kod wysyła 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 wczytał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” (dodano element podrzędny) dla 2 dinozaurów z najwyższym wynikiem, 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 „dodano dziecko” 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 ostateczne zdarzenia „child added” oraz zdarzenie „value” dla aplikacji.

Obsługa transakcji offline

Wszystkie transakcje przeprowadzane w trybie offline są umieszczane w kolejce. Gdy aplikacja ponownie nawiąże połączenie z siecią, transakcje zostaną wysłane na serwer Bazy danych czasu rzeczywistego.

Baza danych czasu rzeczywistego Firebase ma wiele funkcji do obsługi scenariuszy offline i połączeń sieciowych. Pozostała część tego przewodnika dotyczy Twojej aplikacji niezależnie od tego, czy masz włączone utrwalanie.

Zarządzanie obecnością

W aplikacjach działających w czasie rzeczywistym 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żywać do zapisywania danych w bazie, gdy klient odłączy się od serwerów bazy danych Firebase. Te aktualizacje są wykonywane niezależnie od tego, czy klient rozłączy się prawidłowo, czy nie. Dzięki temu możesz polegać na nich w zakresie zwalniania miejsca, nawet jeśli połączenie zostanie przerwane lub klient ulegnie awarii. Wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie, można wykonywać po odłączeniu.

Oto prosty przykład zapisywania danych po odłą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 funkcja 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. Serwer monitoruje połączenie. Jeśli w dowolnym momencie połączenie zostanie przerwane lub aktywnie zamknięte przez klienta Bazy danych czasu rzeczywistego, serwer po raz drugi 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 .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ą przydatne jest, aby aplikacja wiedziała, kiedy jest online lub 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 wartość /.info/connected jako fałsz, nie ma gwarancji, że inny klient również odczyta wartość fałsz.

Opóźnienie obsługi

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 umożliwia łatwe i niezawodne zapisywanie czasu, w którym klient bazy danych czasu rzeczywistego został odłączony:

final userLastOnlineRef =
    FirebaseDatabase.instance.ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(ServerValue.timestamp);

Clock Skew

Chociaż ServerValue.timestamp jest znacznie dokładniejsza i preferowana w większości operacji odczytu i zapisu, czasami może być przydatne oszacowanie odchylenia zegara klienta względem serwerów Bazy danych czasu rzeczywistego Firebase. Możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset, aby uzyskać wartość w milisekundach, którą klienci Bazy danych czasu rzeczywistego Firebase dodają do lokalnego zgłoszonego czasu (czas epoki w milisekundach), aby oszacować czas serwera. Pamiętaj, że na dokładność tego przesunięcia może wpływać opóźnienie sieci, dlatego jest ono przydatne głównie do wykrywania dużych (> 1 sekunda) rozbieżności 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 Presence

Łącząc operacje odłączania z monitorowaniem stanu połączenia i znacznikami czasu 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 w czasie rzeczywistym jest online. Klienci ustawiają tę lokalizację na wartość „true”, gdy są online, a znacznik czasu, gdy się rozłączają. Ten sygnatura czasowa wskazuje, kiedy ostatnio dany użytkownik był online.

Pamiętaj, że aplikacja powinna umieszczać operacje odłączania w kolejce, zanim użytkownik zostanie oznaczony jako online. Pozwoli to uniknąć sytuacji wyścigu w przypadku utraty połączenia sieciowego klienta, zanim oba polecenia zostaną wysłane na serwer.

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