在 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 Function 以便存取 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);
    });
});

透過這些更改,我們現在確保本機Cloud Firestore 狀態將始終反映裝置的線上/離線狀態。這意味著您可以監聽/status/{uid}文件並使用該資料來更改您的 UI 以反映連線狀態。

網路

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

在全球範圍內更新 Cloud Firestore

儘管我們的應用程式可以正確地向自身報告在線狀態,但此狀態在其他 Cloud Firestore 應用程式中仍不準確,因為我們的「離線」狀態寫入僅在本地,並且在連接恢復時不會同步。為了解決這個問題,我們將使用雲端函數來監視即時資料庫中的status/{uid}路徑。當即時資料庫值變更時,值將同步到 Cloud Firestore,以便所有使用者的狀態都是正確的。

Node.js

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 一起運作的完整狀態系統。以下是使用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 應用程式新增狀態是可擴展且有效的,但有一些限制:

  • 去抖- 當監聽 Cloud Firestore 中的即時變更時,此解決方案可能會觸發多個變更。如果這些變更觸發的事件多於您想要的數量,請手動對 Cloud Firestore 事件進行去抖處理。
  • 連接性- 此實施測量與即時資料庫的連接,而不是與 Cloud Firestore 的連接。如果每個資料庫的連線狀態不相同,此解決方案可能會報告不正確的存在狀態。
  • Android - 在 Android 上,即時資料庫在 60 秒不活動後會與後端斷開連線。不活動表示沒有開啟的偵聽器或掛起的操作。為了保持連線打開,我們建議您在.info/connected以外的路徑新增一個值事件偵聽器。例如,您可以在每個會話開始時執行FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() 。有關詳細信息,請參閱檢測連接狀態