แอปพลิเคชัน Firebase จะทำงานได้แม้ว่าแอปของคุณจะขาดการเชื่อมต่อเครือข่ายชั่วคราว นอกจากนี้ Firebase ยังมีเครื่องมือสำหรับเก็บข้อมูลไว้ในเครื่อง จัดการการตรวจหาตนเอง และจัดการเวลาในการตอบสนอง
การเก็บข้อมูลไว้ในดิสก์
แอป Firebase จะจัดการการหยุดชะงักของเครือข่ายชั่วคราวโดยอัตโนมัติ ข้อมูลที่แคชไว้จะพร้อมใช้งานขณะออฟไลน์ และ Firebase จะส่งการเขียนซ้ำเมื่อมีการเชื่อมต่อเครือข่ายอีกครั้ง
เมื่อคุณเปิดใช้การเก็บข้อมูลไว้ในดิสก์ แอปจะเขียนข้อมูลลงในอุปกรณ์ในเครื่องเพื่อให้แอปคงสถานะไว้ได้ขณะออฟไลน์ แม้ว่าผู้ใช้หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม
คุณเปิดใช้การเก็บข้อมูลไว้ในดิสก์ได้โดยใช้โค้ดเพียงบรรทัดเดียว
FirebaseDatabase.instance.setPersistenceEnabled(true);
ลักษณะการทำงานของการเก็บข้อมูล
การเปิดใช้การเก็บข้อมูลจะทำให้ข้อมูลใดก็ตามที่ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงค์ขณะออนไลน์ยังคงอยู่ในดิสก์และพร้อมใช้งานแบบออฟไลน์ แม้ว่าผู้ใช้หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม ซึ่งหมายความว่าแอปจะทำงานเหมือนกับตอนออนไลน์โดยใช้ข้อมูลในเครื่องที่จัดเก็บไว้ในแคช การเรียกกลับของ Listener จะยังคงทำงานสำหรับการอัปเดตในเครื่อง
ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะเก็บคิวการดำเนินการเขียนทั้งหมดที่ดำเนินการขณะที่แอปออฟไลน์ไว้โดยอัตโนมัติ เมื่อเปิดใช้การเก็บข้อมูล คิวนี้จะยังคงอยู่ในดิสก์ด้วยเพื่อให้การเขียนทั้งหมดพร้อมใช้งานเมื่อผู้ใช้หรือระบบปฏิบัติการรีสตาร์ทแอป เมื่อแอปกลับมาเชื่อมต่ออีกครั้ง ระบบจะส่งการดำเนินการทั้งหมดไปยังเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase
หากแอปใช้ การตรวจสอบสิทธิ์ Firebase ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะเก็บโทเค็นการตรวจสอบสิทธิ์ของผู้ใช้ไว้เมื่อรีสตาร์ทแอป หากโทเค็นการตรวจสอบสิทธิ์หมดอายุขณะที่แอปออฟไลน์ ไคลเอ็นต์จะหยุดการดำเนินการเขียนชั่วคราวจนกว่าแอปจะตรวจสอบสิทธิ์ผู้ใช้อีกครั้ง ไม่เช่นนั้นการดำเนินการเขียนอาจล้มเหลวเนื่องจากกฎความปลอดภัย
อัปเดตข้อมูลอยู่เสมอ
ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงโครไนซ์และจัดเก็บสำเนาข้อมูลในเครื่องสำหรับ Listener ที่ใช้งานอยู่ นอกจากนี้ คุณยังซิงค์ตำแหน่งที่เฉพาะเจาะจงได้ด้วย
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);
ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะดาวน์โหลดข้อมูลในตำแหน่งเหล่านี้โดยอัตโนมัติและซิงค์ข้อมูลไว้เสมอ แม้ว่าการอ้างอิงจะไม่มี Listener ที่ใช้งานอยู่ก็ตาม คุณสามารถปิดการซิงโครไนซ์อีกครั้งได้โดยใช้โค้ดบรรทัดต่อไปนี้
scoresRef.keepSynced(false);
โดยค่าเริ่มต้น ระบบจะแคชข้อมูลที่ซิงค์ไว้ก่อนหน้านี้ 10 MB ซึ่งควรเพียงพอสำหรับแอปพลิเคชันส่วนใหญ่ หากแคชมีขนาดใหญ่กว่าขนาดที่กำหนดค่าไว้ ฐานข้อมูลเรียลไทม์ของ Firebase จะล้างข้อมูลที่ใช้ล่าสุด ระบบจะไม่ล้างข้อมูลที่ซิงค์ไว้จากแคช
การค้นหาข้อมูลแบบออฟไลน์
ฐานข้อมูลเรียลไทม์ของ Firebase จะจัดเก็บข้อมูลที่ส่งคืนจากการค้นหาเพื่อใช้เมื่อออฟไลน์ สำหรับการค้นหาที่สร้างขึ้นขณะออฟไลน์ ฐานข้อมูลเรียลไทม์ของ Firebase จะยังคงทำงานกับข้อมูลที่โหลดไว้ก่อนหน้านี้ หากข้อมูลที่ขอไม่ได้โหลดไว้ ฐานข้อมูลเรียลไทม์ของ Firebase จะโหลดข้อมูลจากแคชในเครื่อง เมื่อมีการเชื่อมต่อเครือข่ายอีกครั้ง ข้อมูลจะโหลดและแสดงผลการค้นหา
ตัวอย่างเช่น โค้ดนี้จะค้นหารายการ 4 รายการล่าสุดในฐานข้อมูลคะแนน
final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.orderByValue().limitToLast(4).onChildAdded.listen((event) {
debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});
สมมติว่าผู้ใช้ขาดการเชื่อมต่อ ไปออฟไลน์ และรีสตาร์ทแอป ขณะที่ยังออฟไลน์อยู่ แอปจะค้นหารายการ 2 รายการล่าสุดจากตำแหน่งเดียวกัน การค้นหานี้จะแสดงรายการ 2 รายการล่าสุดได้สำเร็จเนื่องจากแอปได้โหลดรายการทั้ง 4 รายการในการค้นหาข้างต้นแล้ว
scoresRef.orderByValue().limitToLast(2).onChildAdded.listen((event) {
debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});
ในตัวอย่างก่อนหน้า ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะแสดงเหตุการณ์ "เพิ่มรายการย่อย" สำหรับไดโนเสาร์ 2 ตัวที่มีคะแนนสูงสุดโดยใช้แคชที่เก็บไว้ แต่จะไม่แสดงเหตุการณ์ "ค่า" เนื่องจากแอปไม่เคยดำเนินการค้นหานั้นขณะออนไลน์
หากแอปขอรายการ 6 รายการล่าสุดขณะออฟไลน์ แอปจะได้รับเหตุการณ์ "เพิ่มรายการย่อย" สำหรับรายการ 4 รายการที่แคชไว้ทันที เมื่ออุปกรณ์กลับมาออนไลน์ ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงโครไนซ์กับเซิร์ฟเวอร์และได้รับเหตุการณ์ "เพิ่มรายการย่อย" 2 รายการสุดท้ายและเหตุการณ์ "ค่า" สำหรับแอป
การจัดการธุรกรรมแบบออฟไลน์
ระบบจะจัดคิวธุรกรรมทั้งหมดที่ดำเนินการขณะที่แอปออฟไลน์ เมื่อแอปกลับมาเชื่อมต่อเครือข่ายอีกครั้ง ระบบจะส่งธุรกรรมไปยังเซิร์ฟเวอร์ Realtime Database
ฐานข้อมูลเรียลไทม์ของ Firebase มีฟีเจอร์มากมายสำหรับการจัดการสถานการณ์ออฟไลน์และการเชื่อมต่อเครือข่าย ส่วนที่เหลือของคู่มือนี้ใช้ได้กับแอปของคุณไม่ว่าคุณจะเปิดใช้การเก็บข้อมูลไว้หรือไม่ก็ตาม
การจัดการการตรวจหาตนเอง
ในแอปพลิเคชันแบบเรียลไทม์ การตรวจหาเวลาที่ไคลเอ็นต์เชื่อมต่อและยกเลิกการเชื่อมต่อมักจะมีประโยชน์ ตัวอย่างเช่น คุณอาจต้องการทำเครื่องหมายผู้ใช้เป็น "ออฟไลน์" เมื่อไคลเอ็นต์ของผู้ใช้ยกเลิกการเชื่อมต่อ
ไคลเอ็นต์ฐานข้อมูล Firebase มีองค์ประกอบพื้นฐานที่เรียบง่ายซึ่งคุณใช้เขียนลงในฐานข้อมูลได้เมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อจากเซิร์ฟเวอร์ฐานข้อมูล Firebase การอัปเดตเหล่านี้จะเกิดขึ้นไม่ว่าไคลเอ็นต์จะยกเลิกการเชื่อมต่ออย่างถูกต้องหรือไม่ก็ตาม ดังนั้นคุณจึงวางใจได้ว่าการอัปเดตจะล้างข้อมูลแม้ว่าการเชื่อมต่อจะขาดหายไปหรือไคลเอ็นต์ขัดข้อง คุณสามารถดำเนินการเขียนทั้งหมด รวมถึงการตั้งค่า การอัปเดต และการนำออกได้เมื่อยกเลิกการเชื่อมต่อ
ต่อไปนี้เป็นตัวอย่างง่ายๆ ของการเขียนข้อมูลเมื่อยกเลิกการเชื่อมต่อโดยใช้องค์ประกอบพื้นฐาน onDisconnect
final presenceRef = FirebaseDatabase.instance.ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
วิธีการทำงานของ onDisconnect
เมื่อคุณสร้างการดำเนินการ onDisconnect() การดำเนินการจะอยู่ในเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase เซิร์ฟเวอร์จะตรวจสอบความปลอดภัยเพื่อให้แน่ใจว่าผู้ใช้สามารถดำเนินการเขียนเหตุการณ์ที่ขอได้ และแจ้งให้แอปของคุณทราบหากการดำเนินการไม่ถูกต้อง จากนั้นเซิร์ฟเวอร์จะตรวจสอบการเชื่อมต่อ หากการเชื่อมต่อหมดเวลาหรือไคลเอ็นต์ Realtime Database ปิดการเชื่อมต่ออย่างชัดแจ้ง เซิร์ฟเวอร์จะตรวจสอบความปลอดภัยอีกครั้ง (เพื่อให้แน่ใจว่าการดำเนินการยังคงถูกต้อง) แล้วเรียกใช้เหตุการณ์
try {
await presenceRef.onDisconnect().remove();
} catch (error) {
debugPrint("Could not establish onDisconnect event: $error");
}
คุณยังยกเลิกเหตุการณ์ onDisconnect ได้ด้วยการเรียกใช้ .cancel()
final onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();
การตรวจหาสถานะการเชื่อมต่อ
ฟีเจอร์ที่เกี่ยวข้องกับการตรวจหาตนเองหลายฟีเจอร์จำเป็นต้องให้แอปทราบว่าแอปออนไลน์หรือออฟไลน์ ฐานข้อมูลเรียลไทม์ของ Firebase มีตำแหน่งพิเศษที่ /.info/connected ซึ่งจะอัปเดตทุกครั้งที่สถานะการเชื่อมต่อของไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase เปลี่ยนไป ตัวอย่างเช่น
final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
final connected = event.snapshot.value as bool? ?? false;
if (connected) {
debugPrint("Connected.");
} else {
debugPrint("Not connected.");
}
});
/.info/connected เป็นค่าบูลีนที่ไม่ได้ซิงโครไนซ์ระหว่างไคลเอ็นต์ Realtime Database เนื่องจากค่าขึ้นอยู่กับสถานะของไคลเอ็นต์ กล่าวอีกนัยหนึ่งคือ หากไคลเอ็นต์หนึ่งอ่าน /.info/connected เป็นเท็จ ก็ไม่ได้หมายความว่าไคลเอ็นต์อื่นจะอ่านเป็นเท็จด้วย
การจัดการเวลาในการตอบสนอง
การประทับเวลาของเซิร์ฟเวอร์
เซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase มีกลไกในการแทรกการประทับเวลาที่สร้างขึ้นในเซิร์ฟเวอร์เป็นข้อมูล ฟีเจอร์นี้เมื่อรวมกับ onDisconnect จะช่วยให้คุณจดบันทึกเวลาที่ไคลเอ็นต์ Realtime Database ยกเลิกการเชื่อมต่อได้อย่างง่ายดายและเชื่อถือได้
final userLastOnlineRef =
FirebaseDatabase.instance.ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(ServerValue.timestamp);
ความคลาดเคลื่อนของนาฬิกา
แม้ว่า ServerValue.timestamp จะแม่นยำกว่ามากและเหมาะสำหรับการดำเนินการอ่าน/เขียนส่วนใหญ่ แต่บางครั้งก็มีประโยชน์ในการประมาณความคลาดเคลื่อนของนาฬิกาของไคลเอ็นต์เทียบกับเซิร์ฟเวอร์ของฐานข้อมูลเรียลไทม์ของ Firebase คุณสามารถแนบการเรียกกลับไปยังตำแหน่ง /.info/serverTimeOffset เพื่อรับค่าเป็นมิลลิวินาทีที่ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase เพิ่มลงในเวลาที่รายงานในเครื่อง (เวลา Epoch เป็นมิลลิวินาที) เพื่อประมาณเวลาของเซิร์ฟเวอร์ โปรดทราบว่าความแม่นยำของการชดเชยนี้อาจได้รับผลกระทบจากเวลาในการตอบสนองของเครือข่าย ดังนั้นจึงมีประโยชน์หลักๆ ในการค้นหาความคลาดเคลื่อนขนาดใหญ่ (> 1 วินาที) ในเวลาของนาฬิกา
final offsetRef = FirebaseDatabase.instance.ref(".info/serverTimeOffset");
offsetRef.onValue.listen((event) {
final offset = event.snapshot.value as num? ?? 0.0;
final estimatedServerTimeMs =
DateTime.now().millisecondsSinceEpoch + offset;
});
แอปตรวจหาตนเองตัวอย่าง
การรวมการดำเนินการยกเลิกการเชื่อมต่อกับการตรวจสอบสถานะการเชื่อมต่อและการประทับเวลาของเซิร์ฟเวอร์จะช่วยให้คุณสร้างระบบการตรวจหาตนเองของผู้ใช้ได้ ในระบบนี้ ผู้ใช้แต่ละรายจะจัดเก็บข้อมูลในตำแหน่งฐานข้อมูลเพื่อระบุว่าไคลเอ็นต์ Realtime Database ออนไลน์อยู่หรือไม่ ไคลเอ็นต์จะตั้งค่าตำแหน่งนี้เป็นจริงเมื่อออนไลน์และตั้งค่าการประทับเวลาเมื่อยกเลิกการเชื่อมต่อ การประทับเวลานี้จะระบุเวลาล่าสุดที่ผู้ใช้รายนั้นออนไลน์
โปรดทราบว่าแอปควรจัดคิวการดำเนินการยกเลิกการเชื่อมต่อก่อนที่จะทำเครื่องหมายผู้ใช้เป็นออนไลน์ เพื่อหลีกเลี่ยงสภาวะการแข่งขันในกรณีที่การเชื่อมต่อเครือข่ายของไคลเอ็นต์ขาดหายไปก่อนที่จะส่งคำสั่งทั้ง 2 รายการไปยังเซิร์ฟเวอร์ได้
// Since I can connect from multiple devices, we store each connection
// instance separately any time that connectionsRef's value is null (i.e.
// has no children) I am offline.
final myConnectionsRef =
FirebaseDatabase.instance.ref("users/joe/connections");
// Stores the timestamp of my last disconnect (the last time I was seen online)
final lastOnlineRef =
FirebaseDatabase.instance.ref("/users/joe/lastOnline");
final connectedRef = FirebaseDatabase.instance.ref(".info/connected");
connectedRef.onValue.listen((event) {
final connected = event.snapshot.value as bool? ?? false;
if (connected) {
final con = myConnectionsRef.push();
// When this device disconnects, remove it.
con.onDisconnect().remove();
// When I disconnect, update the last time I was seen online.
lastOnlineRef.onDisconnect().set(ServerValue.timestamp);
// Add this device to my connections list.
// This value could contain info about the device or a timestamp too.
con.set(true);
}
});