(選用) 使用 Firebase Emulator 套件設計原型並進行測試
在討論應用程式如何讀取即時資料庫及寫入資料之前, 現在就來介紹一組工具 可用於設計原型及測試即時資料庫 功能:Firebase 模擬器套件。如要透過其他資料 最佳化安全性規則,或是盡量找出 以符合成本效益的方式與後端互動 未部署即時服務,也是不錯的點子
即時資料庫模擬器屬於 Emulator 套件的一部分 可讓應用程式與模擬的資料庫內容和設定互動,如 以及選用的模擬專案資源 (函式、其他資料庫 和安全性規則).emulator_suite_short
使用即時資料庫模擬器只需完成幾個步驟:
- 將一行程式碼新增至應用程式的測試設定,即可與模擬器連線。
- 從本機專案目錄的根目錄中執行
firebase emulators:start
。 - 使用即時資料庫平台,從應用程式的原型程式碼發出呼叫 繼續使用 SDK,或是使用即時資料庫 REST API。
歡迎查看詳細的即時資料庫和 Cloud Functions 逐步操作說明。建議您也參閱模擬器套件簡介。
取得 DatabaseReference
如要從資料庫讀取或寫入資料,您需要使用
DatabaseReference
:
DatabaseReference ref = FirebaseDatabase.instance.ref();
寫入資料
本文說明讀取及寫入 Firebase 資料的基本概念。
Firebase 資料會寫入 DatabaseReference
,並由以下方法擷取:
等待或監聽參照產生的事件。發出事件
也就是資料的初始狀態,並在資料變更時再次執行
基本寫入作業
針對基本寫入作業,您可以使用 set()
將資料儲存至指定
取代該路徑中的任何現有資料。您可以設定一個參考檔案
直至下列類型:String
、boolean
、int
、double
、Map
、List
。
舉例來說,您可以新增包含 set()
的使用者,如下所示:
DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");
await ref.set({
"name": "John",
"age": 18,
"address": {
"line1": "100 Mountain View"
}
});
以這種方式使用 set()
會覆寫指定位置的資料,
包括任何子節點不過,您仍可在沒有權限的情況下更新孩子
重寫整個物件如要允許使用者更新個人資料
您可以按照下列方式更新使用者名稱:
DatabaseReference ref = FirebaseDatabase.instance.ref("users/123");
// Only update the name, leave the age and address!
await ref.update({
"age": 19,
});
update()
方法接受節點的子路徑,可讓您更新多個節點
一次建立資料庫節點:
DatabaseReference ref = FirebaseDatabase.instance.ref("users");
await ref.update({
"123/age": 19,
"123/address/line1": "1 Mountain View",
});
讀取資料
監聽價值事件來讀取資料
如要在路徑中讀取資料及監聽變更,請使用
要監聽的 onValue
屬性 (共 DatabaseReference
)
DatabaseEvent
秒。
您可以使用 DatabaseEvent
讀取指定路徑的資料。
與活動當下一樣。這個事件會在
並在每次資料 (包括任何子項) 時
並輸入變更內容這個事件有一個 snapshot
屬性,其中包含該事件的所有資料
包括兒童資料如果沒有資料,則快照的
exists
屬性為 false
,其 value
屬性為空值。
下列範例示範社交網誌應用程式擷取 資料庫中貼文的詳細資料:
DatabaseReference starCountRef =
FirebaseDatabase.instance.ref('posts/$postId/starCount');
starCountRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
updateStarCount(data);
});
事件監聽器收到含有指定指定位置資料的 DataSnapshot
位置是在事件發生時位於資料庫的 value
屬性中。
讀取資料一次
使用 get() 讀取一次
SDK 的設計用意是管理與資料庫伺服器的互動 應用程式處於連線狀態或離線。
一般而言,您應使用上述的價值事件技術,以便讀取 接收資料更新通知,以便接收來自後端的資料更新通知。這些技術 減少用量與帳單,並經過最佳化調整 都能更輕鬆有效率地完成工作
如果只需要資料一次,可以使用 get()
取得
儲存資料庫資料如果因任何原因導致 get()
無法傳回
伺服器值時,用戶端會探測本機儲存空間快取,並傳回錯誤
。
以下範例說明如何擷取公開的使用者名稱 只需刪除資料庫一次:
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child('users/$userId').get();
if (snapshot.exists) {
print(snapshot.value);
} else {
print('No data available.');
}
不必要的使用 get()
可能會增加頻寬用量,導致損失
如果使用即時事件監聽器可以避免最佳化,如圖所示
。
使用 one() 讀取資料一次
在某些情況下,您可能會希望傳回本機快取中的值
,而不是在伺服器上檢查更新的值。在這些情境下
在這類情況下,您可以使用 once()
從本機磁碟快取取得資料
立即生效
這項功能適用於只需載入一次且預期會執行 經常調整或需要主動聆聽舉例來說,網誌應用程式 都採用這個方法,在使用者選擇載入個人資料時, 開始撰寫新文章:
final event = await ref.once(DatabaseEventType.value);
final username = event.snapshot.value?.username ?? 'Anonymous';
更新或刪除資料
更新特定欄位
於不覆寫其他節點的情況下,同時寫入節點的特定子項
子節點,請使用 update()
方法。
呼叫 update()
時,您可以透過
指定金鑰的路徑如果資料儲存在多個位置,以便進行擴充
您可以使用 kubectl 指令
資料擴散傳遞。舉例來說
社交網誌應用程式可用來建立文章,並同時更新到
最近的活動動態消息和發布使用者的活動動態消息。如要這麼做,
網誌應用程式使用以下程式碼:
void writeNewPost(String uid, String username, String picture, String title,
String body) async {
// A post entry.
final postData = {
'author': username,
'uid': uid,
'body': body,
'title': title,
'starCount': 0,
'authorPic': picture,
};
// Get a key for a new Post.
final newPostKey =
FirebaseDatabase.instance.ref().child('posts').push().key;
// Write the new post's data simultaneously in the posts list and the
// user's post list.
final Map<String, Map> updates = {};
updates['/posts/$newPostKey'] = postData;
updates['/user-posts/$uid/$newPostKey'] = postData;
return FirebaseDatabase.instance.ref().update(updates);
}
本例使用 push()
在包含貼文的節點中張貼文章
位於 /posts/$postid
的所有使用者,並同時擷取
key
。接著,該鍵就會在使用者的
在 /user-posts/$userid/$postid
張貼的訊息。
您可以使用這些路徑,同時更新位於
透過單一呼叫 update()
的 JSON 樹狀結構 (如本範例所示)
就會在兩個位置建立新貼文以這種方式同時進行更新
不可分割:所有更新成功或所有更新都失敗
新增完成回呼
如要瞭解資料修訂時間,可以註冊
完成回呼。set()
和 update()
都會傳回 Future
,也就是
您可附加在寫入含有
已提交至資料庫以及呼叫失敗時。
FirebaseDatabase.instance
.ref('users/$userId/email')
.set(emailAddress)
.then((_) {
// Data saved successfully!
})
.catchError((error) {
// The write failed...
});
刪除資料
刪除資料最簡單的方法是對對物件的參照呼叫 remove()
這些資料的位置
您也可以指定空值做為另一項寫入作業的值來刪除
例如 set()
或 update()
您可以將這項技巧與 update()
搭配使用,
在單一 API 呼叫中刪除多個子項。
將資料儲存為交易
在處理可能由並行修改損毀的資料時,
例如漸進式計數器,您可以傳送
交易處理常式至 runTransaction()
。交易處理常式
資料目前狀態做為引數
會傳回要寫入的新所需狀態如果其他客戶
會先寫入位置,等到成功寫入新值之前,
系統會使用新的目前值再次呼叫 update 函式,然後寫入
再重試一次。
以社交網誌應用程式為例,您可以讓使用者加上星號 也可以移除貼文的星號,並追蹤貼文獲得的星星數量,方法如下:
void toggleStar(String uid) async {
DatabaseReference postRef =
FirebaseDatabase.instance.ref("posts/foo-bar-123");
TransactionResult result = await postRef.runTransaction((Object? post) {
// Ensure a post at the ref exists.
if (post == null) {
return Transaction.abort();
}
Map<String, dynamic> _post = Map<String, dynamic>.from(post as Map);
if (_post["stars"] is Map && _post["stars"][uid] != null) {
_post["starCount"] = (_post["starCount"] ?? 1) - 1;
_post["stars"][uid] = null;
} else {
_post["starCount"] = (_post["starCount"] ?? 0) + 1;
if (!_post.containsKey("stars")) {
_post["stars"] = {};
}
_post["stars"][uid] = true;
}
// Return the new data.
return Transaction.success(_post);
});
}
根據預設,每次執行交易更新函式時,都會引發事件。
多次執行這個函式,可能會看到中間狀態。
您可以將 applyLocally
設為 false
隱藏這些中繼狀態,
而是等到交易完成後再引發事件:
await ref.runTransaction((Object? post) {
// ...
}, applyLocally: false);
交易的結果是 TransactionResult
,其中包含
例如交易是否已修訂,以及新的快照:
DatabaseReference ref = FirebaseDatabase.instance.ref("posts/123");
TransactionResult result = await ref.runTransaction((Object? post) {
// ...
});
print('Committed? ${result.committed}'); // true / false
print('Snapshot? ${result.snapshot}'); // DataSnapshot
取消交易
如要安全地取消交易,請呼叫 Transaction.abort()
以
擲回 AbortTransactionException
:
TransactionResult result = await ref.runTransaction((Object? user) {
if (user !== null) {
return Transaction.abort();
}
// ...
});
print(result.committed); // false
整體伺服器端增量
在上述用途中,我們要將兩個值寫入資料庫: 使用者為貼文加上星號/移除星號,以及逐漸增加的星號數量。如果我們 就可以知道使用者已為貼文加上星號 而不是交易作業
void addStar(uid, key) async {
Map<String, Object?> updates = {};
updates["posts/$key/stars/$uid"] = true;
updates["posts/$key/starCount"] = ServerValue.increment(1);
updates["user-posts/$key/stars/$uid"] = true;
updates["user-posts/$key/starCount"] = ServerValue.increment(1);
return FirebaseDatabase.instance.ref().update(updates);
}
此程式碼不會使用交易作業,因此不會自動取得 如果有衝突的更新,則重新執行。不過,由於遞增作業 這並不會發生衝突。
如要偵測並拒絕應用程式特定衝突 (例如使用者) 為先前加上星號的訊息加上星號,請自訂 應用情境
離線使用資料
如果用戶端的網路連線中斷,您的應用程式會繼續運作 正確。
連接至 Firebase 資料庫的所有用戶端都會保有自己的內部版本 任何有效資料。寫入資料時,會寫入這個本機版本 首先。接著 Firebase 用戶端會將這些資料與遠端資料庫同步處理 並與其他用戶端共用。
因此,所有寫入資料庫的動作都會立即觸發本機事件, 任何資料都會寫入伺服器也就是說,您的應用程式 回應,無論網路延遲或連線。
連線恢復後,應用程式會收到一組適當的 以便用戶端與目前的伺服器狀態同步, 即可撰寫任何自訂程式碼
我們將在下列單元中進一步說明離線行為: 進一步瞭解線上和離線功能。