แอปพลิเคชัน 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 จะซิงโครไนซ์และจัดเก็บสำเนาข้อมูลในเครื่องสำหรับ Listener ที่ใช้งานอยู่ นอกจากนี้ คุณยังซิงค์ตำแหน่งที่เฉพาะเจาะจง ได้ด้วย
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 จะสร้าง เหตุการณ์ "child added" สำหรับไดโนเสาร์ 2 ตัวที่มีคะแนนสูงสุดโดยใช้ แคชที่เก็บไว้ แต่จะไม่สร้างเหตุการณ์ 'value' เนื่องจากแอปไม่เคยดำเนินการค้นหานั้นขณะออนไลน์
หากแอปขอรายการ 6 รายการล่าสุดขณะออฟไลน์ แอปจะได้รับ เหตุการณ์ 'child added' สำหรับรายการ 4 รายการที่แคชไว้ทันที เมื่อ อุปกรณ์กลับมาออนไลน์ ไคลเอ็นต์ Firebase Realtime Database จะซิงโครไนซ์ กับเซิร์ฟเวอร์และได้รับเหตุการณ์ "child added" 2 รายการสุดท้ายและเหตุการณ์ "value" สำหรับแอป
การจัดการธุรกรรมแบบออฟไลน์
ระบบจะจัดคิวธุรกรรมทั้งหมดที่ดำเนินการขณะที่แอปออฟไลน์อยู่ เมื่อแอปกลับมาเชื่อมต่อเครือข่ายได้อีกครั้ง ระบบจะส่งธุรกรรมไปยัง เซิร์ฟเวอร์Realtime Database
การจัดการสถานะการออนไลน์
ในแอปพลิเคชันแบบเรียลไทม์ การตรวจหาเมื่อไคลเอ็นต์ เชื่อมต่อและยกเลิกการเชื่อมต่อมักจะมีประโยชน์ ตัวอย่างเช่น คุณอาจ ต้องการทำเครื่องหมายผู้ใช้เป็น "ออฟไลน์" เมื่อไคลเอ็นต์ของผู้ใช้ยกเลิกการเชื่อมต่อ
ไคลเอ็นต์ฐานข้อมูล Firebase มีองค์ประกอบพื้นฐานที่เรียบง่ายซึ่งคุณใช้เขียนลงในฐานข้อมูลได้เมื่อไคลเอ็นต์ยกเลิกการเชื่อมต่อจากเซิร์ฟเวอร์ฐานข้อมูล 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"); } });