視您建構的應用程式類型而定,偵測哪些使用者或裝置正在上線 (又稱為偵測「狀態」) 可能會很有幫助。
舉例來說,如果您要建構社群網路等應用程式,或部署一整套 IoT 裝置,可以利用這項資訊顯示線上的好友清單,讓使用者可以與他們即時通訊,或是依據「上次看到」排序 IoT 裝置。
Cloud Firestore 並未原生支援狀態,但您可以利用其他 Firebase 產品建構狀態系統。
解決方案:搭配即時資料庫的 Cloud 函式
如要將 Cloud Firestore 連結至 Firebase 即時資料庫的原生狀態功能,請使用 Cloud Functions。
使用即時資料庫回報連線狀態,然後使用 Cloud 函式將該資料複製到 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 函式讓即時資料庫和 Cloud Firestore 保持同步。
如果您尚未這麼做,請將 即時資料庫 新增至專案,並納入上述狀態解決方案。
接下來,您將透過下列方法將狀態同步至 Cloud Firestore:
- 在本機,將資料寫入離線裝置的 Cloud Firestore 快取,讓應用程式知道裝置處於離線狀態。
- 在全球範圍內使用 Cloud 函式,讓所有其他存取 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); }); });
透過這些變更,我們現在已確保 local Cloud Firestore 狀態一律會反映裝置的線上/離線狀態。也就是說,您可以監聽 /status/{uid}
文件,並使用資料變更 UI,以反映連線狀態。
網路
userStatusFirestoreRef.onSnapshot(function(doc) { var isOnline = doc.data().state == 'online'; // ... use isOnline });
全域更新 Cloud Firestore
雖然我們的應用程式會正確向自身回報線上狀態,但在其他 Cloud Firestore 應用程式中,這項狀態仍不準確,因為我們的「離線」狀態寫入作業僅限於本機,且不會在連線恢復時同步。為因應此問題,我們會使用 Cloud 函式,監控即時資料庫中的 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()
。詳情請參閱「偵測連線狀態」。