เปิดใช้งานความสามารถออฟไลน์

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

แอปพลิเคชัน Firebase ทำงานได้แม้ว่าแอปของคุณจะขาดการเชื่อมต่อเครือข่ายชั่วคราว นอกจากนี้ Firebase ยังมีเครื่องมือสำหรับการคงข้อมูลในเครื่อง การจัดการสถานะ และการจัดการเวลาแฝง

ความคงอยู่ของดิสก์

แอป Firebase จะจัดการการหยุดชะงักของเครือข่ายชั่วคราวโดยอัตโนมัติ ข้อมูลแคชพร้อมใช้งานขณะออฟไลน์ และ Firebase จะส่งการเขียนอีกครั้งเมื่อมีการกู้คืนการเชื่อมต่อเครือข่าย

เมื่อคุณเปิดใช้งานการคงอยู่ของดิสก์ แอปของคุณจะเขียนข้อมูลในเครื่องไปยังอุปกรณ์เพื่อให้แอปของคุณสามารถรักษาสถานะขณะออฟไลน์ แม้ว่าผู้ใช้หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม

คุณสามารถเปิดใช้งานการคงอยู่ของดิสก์ด้วยโค้ดเพียงบรรทัดเดียว

FirebaseDatabase.instance.setPersistenceEnabled(true);

พฤติกรรมการคงอยู่

เมื่อเปิดใช้งานการคงอยู่ ข้อมูลใดๆ ที่ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงค์ในขณะที่ออนไลน์ยังคงอยู่ในดิสก์และพร้อมใช้งานแบบออฟไลน์ แม้ว่าผู้ใช้หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม ซึ่งหมายความว่าแอปของคุณทำงานเหมือนออนไลน์โดยใช้ข้อมูลในเครื่องที่จัดเก็บไว้ในแคช การโทรกลับของผู้ฟังจะยังคงเริ่มทำงานสำหรับการอัปเดตในเครื่อง

ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะเก็บคิวของการดำเนินการเขียนทั้งหมดที่ดำเนินการในขณะที่แอปของคุณออฟไลน์โดยอัตโนมัติ เมื่อเปิดใช้งานการคงอยู่ คิวนี้จะยังคงอยู่ในดิสก์ ดังนั้นการเขียนทั้งหมดของคุณจะพร้อมใช้งานเมื่อผู้ใช้หรือระบบปฏิบัติการรีสตาร์ทแอป เมื่อแอปสามารถเชื่อมต่อได้อีกครั้ง การดำเนินการทั้งหมดจะถูกส่งไปยังเซิร์ฟเวอร์ Firebase Realtime Database

หากแอปของคุณใช้การ ตรวจสอบสิทธิ์ Firebase ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะคงโทเค็นการตรวจสอบสิทธิ์ของผู้ใช้ในการรีสตาร์ทแอป หากโทเค็นการตรวจสอบความถูกต้องหมดอายุในขณะที่แอปของคุณออฟไลน์ ไคลเอ็นต์จะหยุดการเขียนชั่วคราวจนกว่าแอปจะตรวจสอบสิทธิ์ผู้ใช้อีกครั้ง มิฉะนั้น การดำเนินการเขียนอาจล้มเหลวเนื่องจากกฎความปลอดภัย

การรักษาข้อมูลให้สดใหม่

ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงโครไนซ์และจัดเก็บสำเนาข้อมูลในเครื่องสำหรับผู้ฟังที่ใช้งานอยู่ นอกจากนี้ คุณสามารถซิงค์สถานที่เฉพาะได้

final scoresRef = FirebaseDatabase.instance.ref("scores");
scoresRef.keepSynced(true);

ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะดาวน์โหลดข้อมูลที่ตำแหน่งเหล่านี้โดยอัตโนมัติและซิงค์ข้อมูลแม้ว่าข้อมูลอ้างอิงจะไม่มีการฟังที่ใช้งานอยู่ก็ตาม คุณสามารถปิดการซิงโครไนซ์กลับด้วยรหัสบรรทัดต่อไปนี้

scoresRef.keepSynced(false);

โดยค่าเริ่มต้น 10MB ของข้อมูลที่ซิงค์ก่อนหน้านี้จะถูกแคชไว้ ซึ่งน่าจะเพียงพอสำหรับการใช้งานส่วนใหญ่ หากแคชเกินขนาดที่กำหนดค่าไว้ ฐานข้อมูลเรียลไทม์ของ Firebase จะล้างข้อมูลที่ใช้ไปอย่างน้อยที่สุดเมื่อเร็วๆ นี้ ข้อมูลที่ซิงค์ไว้จะไม่ถูกล้างออกจากแคช

การสืบค้นข้อมูลออฟไลน์

ฐานข้อมูลเรียลไทม์ของ Firebase จัดเก็บข้อมูลที่ส่งคืนจากการสืบค้นเพื่อใช้เมื่อออฟไลน์ สำหรับการสืบค้นที่สร้างขณะออฟไลน์ ฐานข้อมูลเรียลไทม์ของ Firebase จะยังคงทำงานต่อไปสำหรับข้อมูลที่โหลดไว้ก่อนหน้านี้ หากยังไม่ได้โหลดข้อมูลที่ร้องขอ ฐานข้อมูลเรียลไทม์ของ Firebase จะโหลดข้อมูลจากแคชในเครื่อง เมื่อมีการเชื่อมต่อเครือข่ายอีกครั้ง ข้อมูลจะโหลดและจะแสดงข้อความค้นหา

ตัวอย่างเช่น รหัสนี้จะสอบถามสี่รายการสุดท้ายในฐานข้อมูลของคะแนน:

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

สมมติว่าผู้ใช้ขาดการเชื่อมต่อ ออฟไลน์ และรีสตาร์ทแอป ขณะที่ออฟไลน์ แอปจะสอบถามสองรายการสุดท้ายจากตำแหน่งเดียวกัน ข้อความค้นหานี้จะส่งคืนสองรายการสุดท้ายได้สำเร็จ เนื่องจากแอปได้โหลดทั้งสี่รายการในข้อความค้นหาด้านบนนี้

scoresRef.orderByValue().limitToLast(2).onChildAdded.listen((event) {
  debugPrint("The ${event.snapshot.key} dinosaur's score is ${event.snapshot.value}.");
});

ในตัวอย่างก่อนหน้านี้ ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะเพิ่มเหตุการณ์ 'เด็กที่เพิ่ม' สำหรับไดโนเสาร์สองตัวที่มีคะแนนสูงสุด โดยใช้แคชที่คงอยู่ แต่จะไม่ทำให้เกิดเหตุการณ์ 'มูลค่า' เนื่องจากแอปไม่เคยดำเนินการค้นหานั้นขณะออนไลน์

หากแอปขอหกรายการสุดท้ายขณะออฟไลน์ แอปจะได้รับเหตุการณ์ 'เด็กที่เพิ่ม' สำหรับรายการที่แคชสี่รายการทันที เมื่ออุปกรณ์กลับมาออนไลน์อีกครั้ง ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase จะซิงโครไนซ์กับเซิร์ฟเวอร์และรับเหตุการณ์ 'ลูกที่เพิ่ม' และ 'ค่า' สองรายการสุดท้ายสำหรับแอป

การจัดการธุรกรรมออฟไลน์

ธุรกรรมใดๆ ที่ดำเนินการในขณะที่แอปออฟไลน์ จะถูกจัดคิว เมื่อแอปเชื่อมต่อเครือข่ายได้อีกครั้ง ธุรกรรมจะถูกส่งไปยังเซิร์ฟเวอร์ Realtime Database

ฐานข้อมูลเรียลไทม์ของ Firebase มีคุณสมบัติมากมายในการจัดการกับสถานการณ์ออฟไลน์และการเชื่อมต่อเครือข่าย คู่มือที่เหลือนี้ใช้กับแอปของคุณไม่ว่าคุณจะเปิดใช้งานการคงอยู่หรือไม่ก็ตาม

การจัดการการแสดงตน

ในแอปพลิเคชันเรียลไทม์ มักจะมีประโยชน์ในการตรวจสอบเมื่อไคลเอ็นต์เชื่อมต่อและตัดการเชื่อมต่อ ตัวอย่างเช่น คุณอาจต้องการทำเครื่องหมายผู้ใช้เป็น 'ออฟไลน์' เมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อ

ไคลเอ็นต์ฐานข้อมูล Firebase จัดเตรียมข้อมูลพื้นฐานอย่างง่ายที่คุณสามารถใช้เพื่อเขียนไปยังฐานข้อมูลเมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อจากเซิร์ฟเวอร์ฐานข้อมูล Firebase การอัปเดตเหล่านี้เกิดขึ้นไม่ว่าไคลเอ็นต์จะยกเลิกการเชื่อมต่อทั้งหมดหรือไม่ก็ตาม คุณจึงวางใจได้ในการล้างข้อมูลแม้ว่าการเชื่อมต่อจะหลุดหรือไคลเอ็นต์ขัดข้อง การดำเนินการเขียนทั้งหมด รวมถึงการตั้งค่า การอัปเดต และการลบ สามารถทำได้เมื่อตัดการเชื่อมต่อ

ต่อไปนี้คือตัวอย่างง่ายๆ ของการเขียนข้อมูลเมื่อขาดการเชื่อมต่อโดยใช้ onDisconnect primitive:

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 Realtime Database เปลี่ยนแปลง นี่คือตัวอย่าง:

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 Realtime Database เพิ่มไปยังเวลาที่รายงานในเครื่อง (เวลาของยุคในหน่วยมิลลิวินาที) เพื่อประมาณเวลาของเซิร์ฟเวอร์ โปรดทราบว่าความแม่นยำของออฟเซ็ตนี้อาจได้รับผลกระทบจากเวลาแฝงของเครือข่าย และดังนั้นจึงมีประโยชน์ในเบื้องต้นสำหรับการค้นหาความคลาดเคลื่อนขนาดใหญ่ (> 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;
});

ตัวอย่างการแสดงตน App

ด้วยการรวมการดำเนินการยกเลิกการเชื่อมต่อเข้ากับการตรวจสอบสถานะการเชื่อมต่อและการประทับเวลาของเซิร์ฟเวอร์ คุณสามารถสร้างระบบการแสดงตนของผู้ใช้ได้ ในระบบนี้ ผู้ใช้แต่ละรายจัดเก็บข้อมูลที่ตำแหน่งฐานข้อมูลเพื่อระบุว่าไคลเอ็นต์ Realtime Database ออนไลน์อยู่หรือไม่ ลูกค้าตั้งค่าตำแหน่งนี้เป็นจริงเมื่อออนไลน์และประทับเวลาเมื่อยกเลิกการเชื่อมต่อ การประทับเวลานี้ระบุเวลาล่าสุดที่ผู้ใช้ที่ระบุออนไลน์

โปรดทราบว่าแอปของคุณควรจัดคิวการดำเนินการยกเลิกการเชื่อมต่อก่อนที่ผู้ใช้จะถูกทำเครื่องหมายว่าออนไลน์ เพื่อหลีกเลี่ยงสภาวะการแข่งขันในกรณีที่การเชื่อมต่อเครือข่ายของไคลเอ็นต์ขาดหายไปก่อนที่จะส่งคำสั่งทั้งสองไปยังเซิร์ฟเวอร์

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