在 Cloud Firestore 中建立存在感

根據您正在構建的應用程序類型,您可能會發現檢測哪些用戶或設備在線非常有用 - 也稱為檢測“在線狀態”。

例如,如果您正在構建一個類似社交網絡的應用程序或部署一組 IoT 設備,您可以使用此信息來顯示在線和免費聊天的朋友列表,或按“上次看到”對您的 IoT 設備進行排序.”

Cloud Firestore 本身不支持在線狀態,但您可以利用其他 Firebase 產品來構建在線狀態系統。

解決方案:具有實時數據庫的雲函數

要將 Cloud Firestore 連接到 Firebase 實時數據庫的本機狀態功能,請使用 Cloud Functions。

使用實時數據庫報告連接狀態,然後使用 Cloud Functions 將該數據鏡像到 Cloud Firestore。

在實時數據庫中使用在線狀態

首先,考慮傳統的在線狀態系統如何在實時數據庫中工作。

網絡

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

這個例子是一個完整的實時數據庫在線系統。它處理多次斷開連接、崩潰等。

連接到 Cloud Firestore

要在 Cloud Firestore 中實施類似的解決方案,請使用相同的實時數據庫代碼,然後使用 Cloud Functions 使實時數據庫和 Cloud Firestore 保持同步。

如果你還沒有準備好,加入實時數據庫到您的項目,包括上述存在的解決方案。

接下來,您將通過以下方法將在線狀態同步到 Cloud Firestore:

  1. 在本地,到離線設備的 Cloud Firestore 緩存,以便應用知道它處於離線狀態。
  2. 在全球範圍內,使用 Cloud Functions 以便所有其他訪問 Cloud Firestore 的設備都知道此特定設備處於離線狀態。

更新 Cloud Firestore 的本地緩存

讓我們來看看完成第一個問題所需的更改 - 更新 Cloud Firestore 的本地緩存。

網絡

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

有了這些變化,我們現在已經確保了本地雲公司的FireStore狀態將始終反映設備的在線/離線狀態。你可以聽,這意味著/status/{uid}文件,利用這些數據來更改用戶界面,以反映連接狀態。

網絡

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

在全球範圍內更新 Cloud Firestore

儘管我們的應用程序正確地向自身報告了在線狀態,但此狀態在其他 Cloud Firestore 應用程序中尚不准確,因為我們的“離線”狀態寫入僅在本地進行,並且在連接恢復時不會同步。為了解決這個問題,我們將使用雲功能該款腕錶的status/{uid}實時數據庫路徑。當實時數據庫值更改時,該值將同步到 Cloud Firestore,以便所有用戶的狀態都是正確的。

節點.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);
    });

部署此功能後,您將擁有一個與 Cloud Firestore 一起運行的完整在線狀態系統。以下是監視誰聯機或使用脫機任何用戶的例子where()查詢。

網絡

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

限制

使用實時數據庫向 Cloud Firestore 應用添加狀態是可擴展且有效的,但有一些限制:

  • 反跳-聽實時雲公司的FireStore發生變化時,這種解決方案很可能引發多種變化。如果這些更改觸發的事件比您想要的多,請手動去抖動 Cloud Firestore 事件。
  • 連接-這個實施辦法連通性實時數據庫,而不是雲計算公司的FireStore。如果每個數據庫的連接狀態不同,則此解決方案可能會報告不正確的在線狀態。
  • Android的-在Android上,從後端的數據庫實時斷開閒置60秒後。不活動意味著沒有打開的偵聽器或掛起的操作。為了保持連接,我們建議你添加一個數據事件監聽器的路徑之外.info/connected 。例如,你可以做FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()在每次會話的開始。欲了解更多信息,請參見檢測連接狀態