Save the date - Google I/O returns May 18-20. Register to get the most out of the digital experience: Build your schedule, reserve space, participate in Q&As, earn Google Developer profile badges, and more. Register now
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Bauen Sie Präsenz im Cloud Firestore auf

Abhängig von der Art der App, die Sie erstellen, ist es möglicherweise hilfreich zu erkennen, welche Ihrer Benutzer oder Geräte aktiv online sind. Dies wird auch als "Präsenz" bezeichnet.

Wenn Sie beispielsweise eine App wie ein soziales Netzwerk erstellen oder eine Flotte von IoT-Geräten bereitstellen, können Sie anhand dieser Informationen eine Liste von Freunden anzeigen, die online sind und kostenlos chatten können, oder Ihre IoT-Geräte nach "zuletzt gesehen" sortieren . "

Cloud Firestore unterstützt die Präsenz nicht von Haus aus, Sie können jedoch andere Firebase-Produkte nutzen, um ein Präsenzsystem aufzubauen.

Lösung: Cloud-Funktionen mit Echtzeitdatenbank

Verwenden Sie Cloud-Funktionen, um den Cloud Firestore mit der nativen Präsenzfunktion der Firebase Realtime Database zu verbinden.

Verwenden Sie die Echtzeitdatenbank, um den Verbindungsstatus zu melden, und verwenden Sie dann die Cloud-Funktionen, um diese Daten in den Cloud Firestore zu spiegeln.

Präsenz in der Echtzeitdatenbank verwenden

Betrachten Sie zunächst, wie ein traditionelles Präsenzsystem in der Echtzeitdatenbank funktioniert.

Netz

// Fetch the current user's ID from Firebase Authentication.
var uid = firebase.auth().currentUser.uid;

// Create a reference to this user's specific status node.
// This is where we will store data about being online/offline.
var userStatusDatabaseRef = firebase.database().ref('/status/' + uid);

// We'll create two constants which we will write to 
// the Realtime database when this device is offline
// or online.
var isOfflineForDatabase = {
    state: 'offline',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

var isOnlineForDatabase = {
    state: 'online',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

// Create a reference to the special '.info/connected' path in 
// Realtime Database. This path returns `true` when connected
// and `false` when disconnected.
firebase.database().ref('.info/connected').on('value', function(snapshot) {
    // If we're not currently connected, don't do anything.
    if (snapshot.val() == false) {
        return;
    };

    // If we are currently connected, then use the 'onDisconnect()' 
    // method to add a set which will only trigger once this 
    // client has disconnected by closing the app, 
    // losing internet, or any other means.
    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        // The promise returned from .onDisconnect().set() will
        // resolve as soon as the server acknowledges the onDisconnect() 
        // request, NOT once we've actually disconnected:
        // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect

        // We can now safely set ourselves as 'online' knowing that the
        // server will mark us as offline once we lose connection.
        userStatusDatabaseRef.set(isOnlineForDatabase);
    });
});

Dieses Beispiel ist ein vollständiges Präsenzsystem der Echtzeitdatenbank. Es behandelt mehrere Unterbrechungen, Abstürze und so weiter.

Herstellen einer Verbindung zum Cloud Firestore

Um eine ähnliche Lösung in Cloud Firestore zu implementieren, verwenden Sie denselben Echtzeitdatenbankcode und verwenden Sie dann Cloud-Funktionen, um Echtzeitdatenbank und Cloud Firestore synchron zu halten.

Wenn Sie dies noch nicht getan haben, fügen Sie Ihrem Projekt eine Echtzeitdatenbank hinzu und fügen Sie die oben genannte Präsenzlösung hinzu.

Als Nächstes synchronisieren Sie den Anwesenheitsstatus mit den folgenden Methoden mit Cloud Firestore:

  1. Lokal in den Cloud Firestore-Cache des Offline-Geräts, damit die App weiß, dass es offline ist.
  2. Verwenden Sie global eine Cloud-Funktion, damit alle anderen Geräte, die auf den Cloud Firestore zugreifen, wissen, dass dieses bestimmte Gerät offline ist.

Aktualisieren des lokalen Caches des Cloud Firestore

Werfen wir einen Blick auf die Änderungen, die erforderlich sind, um das erste Problem zu lösen - die Aktualisierung des lokalen Caches des Cloud Firestore.

Netz

// ...
var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid);

// Firestore uses a different server timestamp value, so we'll 
// create two more constants for Firestore state.
var isOfflineForFirestore = {
    state: 'offline',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

var isOnlineForFirestore = {
    state: 'online',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

firebase.database().ref('.info/connected').on('value', function(snapshot) {
    if (snapshot.val() == false) {
        // Instead of simply returning, we'll also set Firestore's state
        // to 'offline'. This ensures that our Firestore cache is aware
        // of the switch to 'offline.'
        userStatusFirestoreRef.set(isOfflineForFirestore);
        return;
    };

    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        userStatusDatabaseRef.set(isOnlineForDatabase);

        // We'll also add Firestore set here for when we come online.
        userStatusFirestoreRef.set(isOnlineForFirestore);
    });
});

Mit diesen Änderungen haben wir jetzt sichergestellt, dass der lokale Cloud Firestore-Status immer den Online- / Offline-Status des Geräts widerspiegelt. Dies bedeutet, dass Sie das Dokument /status/{uid} anhören und die Daten verwenden können, um Ihre Benutzeroberfläche so zu ändern, dass sie den Verbindungsstatus widerspiegelt.

Netz

userStatusFirestoreRef.onSnapshot(function(doc) {
    var isOnline = doc.data().state == 'online';
    // ... use isOnline
});

Cloud Firestore global aktualisieren

Obwohl unsere Anwendung die Online-Präsenz korrekt an sich selbst meldet, ist dieser Status in anderen Cloud Firestore-Apps noch nicht korrekt, da unser "Offline" -Statusschreiben nur lokal ist und nicht synchronisiert wird, wenn eine Verbindung wiederhergestellt wird. Um dem entgegenzuwirken, verwenden wir eine Cloud-Funktion, die den status/{uid} -Pfad in der Echtzeitdatenbank status/{uid} . Wenn sich der Wert der Echtzeitdatenbank ändert, wird der Wert mit dem Cloud Firestore synchronisiert, sodass der Status aller Benutzer korrekt ist.

Node.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

// Since this code will be running in the Cloud Functions environment
// we call initialize Firestore without any arguments because it
// detects authentication from the environment.
const firestore = admin.firestore();

// Create a new function which is triggered on changes to /status/{uid}
// Note: This is a Realtime Database trigger, *not* Firestore.
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
    async (change, context) => {
      // Get the data written to Realtime Database
      const eventStatus = change.after.val();

      // Then use other event data to create a reference to the
      // corresponding Firestore document.
      const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);

      // It is likely that the Realtime Database change that triggered
      // this event has already been overwritten by a fast change in
      // online / offline status, so we'll re-read the current data
      // and compare the timestamps.
      const statusSnapshot = await change.after.ref.once('value');
      const status = statusSnapshot.val();
      functions.logger.log(status, eventStatus);
      // If the current timestamp for this data is newer than
      // the data that triggered this event, we exit this function.
      if (status.last_changed > eventStatus.last_changed) {
        return null;
      }

      // Otherwise, we convert the last_changed field to a Date
      eventStatus.last_changed = new Date(eventStatus.last_changed);

      // ... and write it to Firestore.
      return userStatusFirestoreRef.set(eventStatus);
    });

Sobald Sie diese Funktion bereitgestellt haben, wird ein vollständiges Präsenzsystem mit Cloud Firestore ausgeführt. Im Folgenden finden Sie ein Beispiel für die Überwachung aller Benutzer, die mit einer where() Abfrage online oder offline gehen.

Netz

firebase.firestore().collection('status')
    .where('state', '==', 'online')
    .onSnapshot(function(snapshot) {
        snapshot.docChanges().forEach(function(change) {
            if (change.type === 'added') {
                var msg = 'User ' + change.doc.id + ' is online.';
                console.log(msg);
                // ...
            }
            if (change.type === 'removed') {
                var msg = 'User ' + change.doc.id + ' is offline.';
                console.log(msg);
                // ...
            }
        });
    });

Einschränkungen

Die Verwendung der Echtzeitdatenbank zum Hinzufügen von Präsenz zu Ihrer Cloud Firestore-App ist skalierbar und effektiv, weist jedoch einige Einschränkungen auf:

  • Entprellen - Wenn Sie Änderungen in Echtzeit im Cloud Firestore abhören , löst diese Lösung wahrscheinlich mehrere Änderungen aus. Wenn diese Änderungen mehr Ereignisse auslösen als Sie möchten, entprellen Sie die Cloud Firestore-Ereignisse manuell.
  • Konnektivität - Diese Implementierung misst die Konnektivität zur Echtzeitdatenbank und nicht zum Cloud Firestore. Wenn der Verbindungsstatus zu jeder Datenbank nicht identisch ist, meldet diese Lösung möglicherweise einen falschen Anwesenheitsstatus.
  • Android - Unter Android wird die Echtzeitdatenbank nach 60 Sekunden Inaktivität vom Backend getrennt. Inaktivität bedeutet keine offenen Listener oder ausstehenden Vorgänge. Um die Verbindung offen zu halten, empfehlen wir, einem Pfad neben .info/connected connection einen Wertereignis-Listener hinzuzufügen. Beispielsweise könnten Sie zu Beginn jeder Sitzung FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() . Weitere Informationen finden Sie unter Erkennen des Verbindungsstatus .