แอปพลิเคชัน Firebase จะทำงานได้แม้ว่าแอปจะสูญเสียการเชื่อมต่อเครือข่ายชั่วคราว นอกจากนี้ Firebase ยังมีเครื่องมือสำหรับจัดเก็บข้อมูลในเครื่องอย่างถาวร จัดการสถานะออนไลน์ และจัดการเวลาในการตอบสนอง
ความต่อเนื่องของดิสก์
แอป Firebase จะจัดการการหยุดชะงักของเครือข่ายชั่วคราวโดยอัตโนมัติ ข้อมูลที่แคชจะพร้อมใช้งานขณะออฟไลน์ และ Firebase จะส่งคำสั่งเขียนซ้ำ เมื่อการเชื่อมต่อเครือข่ายกลับมาเป็นปกติ
เมื่อเปิดใช้การคงอยู่ของดิสก์ แอปจะเขียนข้อมูลในเครื่องไปยัง อุปกรณ์เพื่อให้แอปคงสถานะไว้ได้ขณะออฟไลน์ แม้ว่าผู้ใช้ หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม
คุณเปิดใช้การคงอยู่ของดิสก์ได้ด้วยโค้ดเพียงบรรทัดเดียว
Kotlin
Firebase.database.setPersistenceEnabled(true)
Java
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
พฤติกรรมความต่อเนื่อง
การเปิดใช้การคงอยู่จะทำให้ข้อมูลใดก็ตามที่ไคลเอ็นต์ Firebase Realtime Database จะซิงค์ขณะออนไลน์ยังคงอยู่ในดิสก์และพร้อมใช้งานแบบออฟไลน์ แม้ว่าผู้ใช้หรือระบบปฏิบัติการจะรีสตาร์ทแอปก็ตาม ซึ่งหมายความว่าแอปจะทำงานเหมือนกับตอนออนไลน์โดยใช้ข้อมูลในเครื่องที่จัดเก็บไว้ในแคช การเรียกกลับของ Listener จะยังคงทริกเกอร์สำหรับการอัปเดตในเครื่องต่อไป
ไคลเอ็นต์ Firebase Realtime Database จะเก็บคิวของการดำเนินการเขียนทั้งหมดที่ดำเนินการขณะแอปออฟไลน์โดยอัตโนมัติ เมื่อเปิดใช้การคงอยู่ ระบบจะบันทึกคิวนี้ลงในดิสก์ด้วยเพื่อให้การเขียนทั้งหมด พร้อมใช้งานเมื่อผู้ใช้หรือระบบปฏิบัติการ รีสตาร์ทแอป และเมื่อแอปกลับมาเชื่อมต่อได้อีกครั้ง ระบบจะส่งการดำเนินการทั้งหมดไปยังเซิร์ฟเวอร์ Firebase Realtime Database
หากแอปใช้ การตรวจสอบสิทธิ์ Firebase ไคลเอ็นต์ Firebase Realtime Database จะเก็บโทเค็นการตรวจสอบสิทธิ์ ของผู้ใช้ไว้เมื่อรีสตาร์ทแอป หากโทเค็นการให้สิทธิ์หมดอายุขณะที่แอปออฟไลน์ ไคลเอ็นต์จะหยุดการดำเนินการเขียนชั่วคราวจนกว่าแอปจะตรวจสอบสิทธิ์ผู้ใช้อีกครั้ง ไม่เช่นนั้นการดำเนินการเขียนอาจล้มเหลวเนื่องจากกฎความปลอดภัย
การอัปเดตข้อมูลอยู่เสมอ
ซึ่งจะFirebase Realtime Databaseซิงค์และจัดเก็บสำเนาข้อมูลในเครื่อง สำหรับผู้ฟังที่ใช้งานอยู่ นอกจากนี้ คุณยังซิงค์ตำแหน่งที่เฉพาะเจาะจง ได้ด้วย
Kotlin
val scoresRef = Firebase.database.getReference("scores") scoresRef.keepSynced(true)
Java
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores"); scoresRef.keepSynced(true);
Firebase Realtime Databaseไคลเอ็นต์จะดาวน์โหลดข้อมูลโดยอัตโนมัติที่ ตำแหน่งเหล่านี้และซิงค์ข้อมูลไว้แม้ว่าการอ้างอิงจะไม่มี Listener ที่ใช้งานอยู่ก็ตาม คุณปิดการซิงค์ได้อีกครั้งโดยใช้บรรทัดโค้ดต่อไปนี้
Kotlin
scoresRef.keepSynced(false)
Java
scoresRef.keepSynced(false);
โดยค่าเริ่มต้น ระบบจะแคชข้อมูลที่ซิงค์ไว้ก่อนหน้านี้ 10 MB ซึ่งควรเพียงพอสำหรับแอปพลิเคชันส่วนใหญ่ หากแคชมีขนาดใหญ่เกินกว่าที่กำหนดค่าไว้ Firebase Realtime Database จะล้างข้อมูลที่ใช้ล่าสุดน้อยที่สุด ระบบจะไม่ล้างข้อมูลที่ซิงค์ไว้จากแคช
การค้นหาข้อมูลแบบออฟไลน์
Firebase Realtime Database จัดเก็บข้อมูลที่ส่งคืนจากการค้นหาเพื่อใช้ เมื่อออฟไลน์ สําหรับคําค้นหาที่สร้างขึ้นขณะออฟไลน์ Firebase Realtime Database จะยังคงทํางานกับข้อมูลที่โหลดไว้ก่อนหน้านี้ หากข้อมูลที่ขอโหลดไม่สำเร็จ Firebase Realtime Database จะโหลด ข้อมูลจากแคชในเครื่อง เมื่อการเชื่อมต่อเครือข่ายพร้อมใช้งานอีกครั้ง ระบบจะโหลดข้อมูลและแสดงผลการค้นหา
เช่น โค้ดนี้จะค้นหารายการ 4 รายการสุดท้ายในFirebase Realtime Databaseของคะแนน
Kotlin
val scoresRef = Firebase.database.getReference("scores") scoresRef.orderByValue().limitToLast(4).addChildEventListener(object : ChildEventListener { override fun onChildAdded(snapshot: DataSnapshot, previousChild: String?) { Log.d(TAG, "The ${snapshot.key} dinosaur's score is ${snapshot.value}") } // ... })
Java
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores"); scoresRef.orderByValue().limitToLast(4).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, String previousChild) { Log.d(TAG, "The " + snapshot.getKey() + " dinosaur's score is " + snapshot.getValue()); } // ... });
สมมติว่าผู้ใช้ขาดการเชื่อมต่อ ออฟไลน์ และรีสตาร์ทแอป ขณะที่ยังออฟไลน์อยู่ แอปจะค้นหารายการ 2 รายการล่าสุดจาก ตำแหน่งเดียวกัน การค้นหานี้จะแสดงรายการ 2 รายการสุดท้าย เนื่องจากแอปโหลดรายการทั้ง 4 รายการในการค้นหาข้างต้นแล้ว
Kotlin
scoresRef.orderByValue().limitToLast(2).addChildEventListener(object : ChildEventListener { override fun onChildAdded(snapshot: DataSnapshot, previousChild: String?) { Log.d(TAG, "The ${snapshot.key} dinosaur's score is ${snapshot.value}") } // ... })
Java
scoresRef.orderByValue().limitToLast(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, String previousChild) { Log.d(TAG, "The " + snapshot.getKey() + " dinosaur's score is " + snapshot.getValue()); } // ... });
ในตัวอย่างก่อนหน้า Firebase Realtime Databaseไคลเอ็นต์จะสร้าง เหตุการณ์ "เพิ่มบุตรหลาน" สำหรับไดโนเสาร์ 2 ตัวที่มีคะแนนสูงสุดโดยใช้ แคชที่คงอยู่ แต่จะไม่ทําให้เกิดเหตุการณ์ "value" เนื่องจากแอปไม่เคยเรียกใช้คําค้นหานั้นขณะออนไลน์
หากแอปขอรายการ 6 รายการล่าสุดขณะออฟไลน์ แอปจะได้รับเหตุการณ์ "เพิ่มรายการย่อย" สำหรับรายการที่แคชไว้ 4 รายการทันที เมื่อ อุปกรณ์กลับมาออนไลน์ Firebase Realtime Database ไคลเอ็นต์จะซิงค์ กับเซิร์ฟเวอร์และรับเหตุการณ์ "เพิ่มบุตรหลาน" 2 รายการสุดท้ายและเหตุการณ์ "value" สำหรับแอป
การจัดการธุรกรรมแบบออฟไลน์
ระบบจะจัดคิวธุรกรรมที่ดำเนินการขณะแอปออฟไลน์ เมื่อแอปกลับมาเชื่อมต่อเครือข่ายได้อีกครั้ง ระบบจะส่งธุรกรรมไปยังเซิร์ฟเวอร์Realtime Database
การจัดการการตรวจหาบุคคลในบ้าน
ในแอปพลิเคชันแบบเรียลไทม์ การตรวจหาเมื่อไคลเอ็นต์ เชื่อมต่อและยกเลิกการเชื่อมต่อมักจะมีประโยชน์ เช่น คุณอาจ ต้องการทำเครื่องหมายผู้ใช้เป็น "ออฟไลน์" เมื่อไคลเอ็นต์ของผู้ใช้ยกเลิกการเชื่อมต่อ
ไคลเอ็นต์ฐานข้อมูล Firebase มี Primitive อย่างง่ายที่คุณใช้เขียนไปยังฐานข้อมูลได้เมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อจากเซิร์ฟเวอร์ฐานข้อมูล Firebase การอัปเดตเหล่านี้จะเกิดขึ้นไม่ว่าไคลเอ็นต์จะยกเลิกการเชื่อมต่ออย่างถูกต้องหรือไม่ คุณจึงใช้การอัปเดตเหล่านี้เพื่อล้างข้อมูลได้แม้ว่าการเชื่อมต่อจะขาดหาย หรือไคลเอ็นต์จะขัดข้องก็ตาม การดำเนินการเขียนทั้งหมด รวมถึงการตั้งค่า การอัปเดต และการนำออก สามารถดำเนินการได้เมื่อมีการยกเลิกการเชื่อมต่อ
ต่อไปนี้คือตัวอย่างง่ายๆ ของการเขียนข้อมูลเมื่อยกเลิกการเชื่อมต่อโดยใช้
onDisconnect ดั้งเดิม
Kotlin
val presenceRef = Firebase.database.getReference("disconnectmessage") // Write a string when this client loses connection presenceRef.onDisconnect().setValue("I disconnected!")
Java
DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage"); // Write a string when this client loses connection presenceRef.onDisconnect().setValue("I disconnected!");
วิธีการทำงานของ onDisconnect
เมื่อคุณสร้างการดำเนินการ onDisconnect() การดำเนินการจะ
อยู่ในเซิร์ฟเวอร์ Firebase Realtime Database เซิร์ฟเวอร์จะตรวจสอบความปลอดภัยเพื่อให้แน่ใจว่าผู้ใช้สามารถดำเนินการเขียนเหตุการณ์ที่ขอได้ และจะแจ้งให้แอปของคุณทราบหากไม่ถูกต้อง
จากนั้นเซิร์ฟเวอร์จะ
ตรวจสอบการเชื่อมต่อ หากการเชื่อมต่อหมดเวลาหรือRealtime Databaseไคลเอ็นต์ปิดการเชื่อมต่อโดยตรง
เซิร์ฟเวอร์จะตรวจสอบความปลอดภัยอีกครั้ง (เพื่อให้แน่ใจว่าการดำเนินการยังคงถูกต้อง)
แล้วเรียกใช้เหตุการณ์
แอปของคุณสามารถใช้การเรียกกลับในการดำเนินการเขียน
เพื่อให้แน่ใจว่าได้แนบ onDisconnect อย่างถูกต้อง
Kotlin
presenceRef.onDisconnect().removeValue { error, reference -> error?.let { Log.d(TAG, "could not establish onDisconnect event: ${error.message}") } }
Java
presenceRef.onDisconnect().removeValue(new DatabaseReference.CompletionListener() { @Override public void onComplete(DatabaseError error, @NonNull DatabaseReference reference) { if (error != null) { Log.d(TAG, "could not establish onDisconnect event:" + error.getMessage()); } } });
คุณยังยกเลิกonDisconnectได้โดยโทรหา .cancel()
Kotlin
val onDisconnectRef = presenceRef.onDisconnect() onDisconnectRef.setValue("I disconnected") // ... // some time later when we change our minds // ... onDisconnectRef.cancel()
Java
OnDisconnect onDisconnectRef = presenceRef.onDisconnect(); onDisconnectRef.setValue("I disconnected"); // ... // some time later when we change our minds // ... onDisconnectRef.cancel();
การตรวจหาสถานะการเชื่อมต่อ
สำหรับฟีเจอร์ที่เกี่ยวข้องกับการแสดงตนหลายๆ อย่าง การที่แอป
ทราบว่าแอปออนไลน์หรือออฟไลน์เมื่อใดจะเป็นประโยชน์ Firebase Realtime Database
มีตำแหน่งพิเศษที่ /.info/connected ซึ่ง
จะอัปเดตทุกครั้งที่Firebase Realtime Database สถานะการเชื่อมต่อของไคลเอ็นต์
เปลี่ยนแปลง ตัวอย่างเช่น
Kotlin
val connectedRef = Firebase.database.getReference(".info/connected") connectedRef.addValueEventListener(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val connected = snapshot.getValue(Boolean::class.java) ?: false if (connected) { Log.d(TAG, "connected") } else { Log.d(TAG, "not connected") } } override fun onCancelled(error: DatabaseError) { Log.w(TAG, "Listener was cancelled") } })
Java
DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected"); connectedRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { boolean connected = snapshot.getValue(Boolean.class); if (connected) { Log.d(TAG, "connected"); } else { Log.d(TAG, "not connected"); } } @Override public void onCancelled(@NonNull DatabaseError error) { Log.w(TAG, "Listener was cancelled"); } });
/.info/connected เป็นค่าบูลีนที่ไม่ได้ซิงค์ระหว่างไคลเอ็นต์ Realtime Database เนื่องจากค่าขึ้นอยู่กับสถานะของไคลเอ็นต์ กล่าวอีกนัยหนึ่งคือ หากไคลเอ็นต์หนึ่งอ่าน /.info/connected เป็นเท็จ ก็ไม่ได้หมายความว่าไคลเอ็นต์อื่นจะอ่านเป็นเท็จด้วย
ใน Android นั้น Firebase จะจัดการสถานะการเชื่อมต่อโดยอัตโนมัติเพื่อลดการใช้แบนด์วิดท์และการใช้งานแบตเตอรี่ เมื่อไคลเอ็นต์ไม่มี Listener ที่ใช้งานอยู่
ไม่มีการเขียนที่รอดำเนินการหรือonDisconnect
การดำเนินการ และไม่ได้ถูกตัดการเชื่อมต่ออย่างชัดเจนโดยเมธอด
goOffline
Firebase จะปิดการเชื่อมต่อหลังจากไม่มีการใช้งานเป็นเวลา 60 วินาที
เวลาในการตอบสนอง
การประทับเวลาของเซิร์ฟเวอร์
Firebase Realtime Database เซิร์ฟเวอร์มีกลไกในการแทรก
การประทับเวลาที่สร้างขึ้นในเซิร์ฟเวอร์เป็นข้อมูล ฟีเจอร์นี้เมื่อใช้ร่วมกับ
onDisconnect จะช่วยให้คุณจดบันทึกเวลาที่ไคลเอ็นต์ Realtime Database ยกเลิกการเชื่อมต่อได้อย่างง่ายดายและเชื่อถือได้
Kotlin
val userLastOnlineRef = Firebase.database.getReference("users/joe/lastOnline") userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)
Java
DatabaseReference userLastOnlineRef = FirebaseDatabase.getInstance().getReference("users/joe/lastOnline"); userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);
ความคลาดเคลื่อนของนาฬิกา
แม้ว่า firebase.database.ServerValue.TIMESTAMP จะมีความแม่นยำมากกว่า
และเหมาะสำหรับการดำเนินการอ่าน/เขียนส่วนใหญ่
แต่บางครั้งก็อาจมีประโยชน์ในการประมาณค่าความคลาดเคลื่อนของนาฬิกาไคลเอ็นต์
เมื่อเทียบกับเซิร์ฟเวอร์ของ Firebase Realtime Database คุณ
สามารถแนบการเรียกกลับไปยังตำแหน่ง /.info/serverTimeOffset
เพื่อรับค่าเป็นมิลลิวินาทีที่ไคลเอ็นต์ Firebase Realtime Database
เพิ่มลงในเวลาที่รายงานในเครื่อง (เวลา Epoch เป็นมิลลิวินาที) เพื่อประมาณ
เวลาของเซิร์ฟเวอร์ โปรดทราบว่าความแม่นยำของออฟเซ็ตนี้อาจได้รับผลกระทบจาก
เวลาในการตอบสนองของเครือข่าย ดังนั้นจึงมีประโยชน์หลักๆ ในการค้นหาความคลาดเคลื่อนขนาดใหญ่ (> 1 วินาที) ในเวลาของนาฬิกา
Kotlin
val offsetRef = Firebase.database.getReference(".info/serverTimeOffset") offsetRef.addValueEventListener(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val offset = snapshot.getValue(Double::class.java) ?: 0.0 val estimatedServerTimeMs = System.currentTimeMillis() + offset } override fun onCancelled(error: DatabaseError) { Log.w(TAG, "Listener was cancelled") } })
Java
DatabaseReference offsetRef = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset"); offsetRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { double offset = snapshot.getValue(Double.class); double estimatedServerTimeMs = System.currentTimeMillis() + offset; } @Override public void onCancelled(@NonNull DatabaseError error) { Log.w(TAG, "Listener was cancelled"); } });
แอปตรวจหาการปรากฏตัวตัวอย่าง
การรวมการดำเนินการยกเลิกการเชื่อมต่อกับการตรวจสอบสถานะการเชื่อมต่อและ การประทับเวลาของเซิร์ฟเวอร์จะช่วยให้คุณสร้างระบบการแสดงสถานะของผู้ใช้ได้ ในระบบนี้ ผู้ใช้แต่ละรายจะจัดเก็บข้อมูลไว้ในตำแหน่งฐานข้อมูลเพื่อระบุว่าไคลเอ็นต์ Realtime Database ออนไลน์หรือไม่ ไคลเอ็นต์จะตั้งค่าตำแหน่งนี้เป็นจริงเมื่อ ออนไลน์และตั้งค่าการประทับเวลาเมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อ การประทับเวลานี้ ระบุเวลาล่าสุดที่ผู้ใช้ที่ระบุออนไลน์
โปรดทราบว่าแอปควรจัดคิวการดำเนินการยกเลิกการเชื่อมต่อก่อนที่จะ ทำเครื่องหมายผู้ใช้ว่าออนไลน์ เพื่อหลีกเลี่ยงการแข่งขันในกรณีที่ไคลเอ็นต์ สูญเสียการเชื่อมต่อเครือข่ายก่อนที่จะส่งทั้ง 2 คำสั่งไปยังเซิร์ฟเวอร์ได้
ระบบการแสดงข้อมูลของผู้ใช้แบบง่ายมีดังนี้
Kotlin
// 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 val database = Firebase.database val myConnectionsRef = database.getReference("users/joe/connections") // Stores the timestamp of my last disconnect (the last time I was seen online) val lastOnlineRef = database.getReference("/users/joe/lastOnline") val connectedRef = database.getReference(".info/connected") connectedRef.addValueEventListener(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val connected = snapshot.getValue<Boolean>() ?: false if (connected) { val con = myConnectionsRef.push() // When this device disconnects, remove it con.onDisconnect().removeValue() // When I disconnect, update the last time I was seen online lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP) // Add this device to my connections list // this value could contain info about the device or a timestamp too con.setValue(java.lang.Boolean.TRUE) } } override fun onCancelled(error: DatabaseError) { Log.w(TAG, "Listener was cancelled at .info/connected") } })
Java
// 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 FirebaseDatabase database = FirebaseDatabase.getInstance(); final DatabaseReference myConnectionsRef = database.getReference("users/joe/connections"); // Stores the timestamp of my last disconnect (the last time I was seen online) final DatabaseReference lastOnlineRef = database.getReference("/users/joe/lastOnline"); final DatabaseReference connectedRef = database.getReference(".info/connected"); connectedRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { boolean connected = snapshot.getValue(Boolean.class); if (connected) { DatabaseReference con = myConnectionsRef.push(); // When this device disconnects, remove it con.onDisconnect().removeValue(); // When I disconnect, update the last time I was seen online lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP); // Add this device to my connections list // this value could contain info about the device or a timestamp too con.setValue(Boolean.TRUE); } } @Override public void onCancelled(@NonNull DatabaseError error) { Log.w(TAG, "Listener was cancelled at .info/connected"); } });