หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

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

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

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

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

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

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

Java

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

โคตรลิน + KTX

Firebase.database.setPersistenceEnabled(true)

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

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

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

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

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

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

Java

DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);

โคตรลิน + KTX

val scoresRef = Firebase.database.getReference("scores")
scoresRef.keepSynced(true)

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

Java

scoresRef.keepSynced(false);

โคตรลิน + KTX

scoresRef.keepSynced(false)

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

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

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

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

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

    // ...
});

โคตรลิน + KTX

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

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

    // ...
});

โคตรลิน + KTX

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

    // ...
})

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

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

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

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

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

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

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

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

Java

DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!");

โคตรลิน + KTX

val presenceRef = Firebase.database.getReference("disconnectmessage")
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!")

วิธีการทำงานของ onDisconnect

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

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

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

โคตรลิน + KTX

presenceRef.onDisconnect().removeValue { error, reference ->
    error?.let {
        Log.d(TAG, "could not establish onDisconnect event: ${error.message}")
    }
}

นอกจากนี้ยังสามารถยกเลิกเหตุการณ์ onDisconnect ได้โดยการโทร .cancel ():

Java

OnDisconnect onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.setValue("I disconnected");
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel();

โคตรลิน + KTX

val onDisconnectRef = presenceRef.onDisconnect()
onDisconnectRef.setValue("I disconnected")
// ...
// some time later when we change our minds
// ...
onDisconnectRef.cancel()

กำลังตรวจจับสถานะการเชื่อมต่อ

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

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

โคตรลิน + KTX

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

/.info/connected เป็นค่าบูลีนซึ่งไม่ซิงโครไนซ์ระหว่างไคลเอ็นต์ฐานข้อมูลแบบเรียลไทม์เนื่องจากค่านี้ขึ้นอยู่กับสถานะของไคลเอ็นต์ กล่าวอีกนัยหนึ่งคือถ้าไคลเอนต์หนึ่งอ่าน /.info/connected เป็นเท็จจะไม่รับประกันว่าไคลเอนต์แยกต่างหากจะอ่านเท็จด้วย

ใน Android Firebase จะจัดการสถานะการเชื่อมต่อโดยอัตโนมัติเพื่อลดแบนด์วิดท์และการใช้แบตเตอรี่ เมื่อไคลเอนต์ไม่มีผู้ฟังที่ใช้งานอยู่ไม่มีการดำเนินการเขียนที่รอดำเนินการหรือ onDisconnect และไม่ได้ตัดการเชื่อมต่ออย่างชัดเจนโดยวิธีการ goOffline Firebase จะปิดการเชื่อมต่อหลังจากไม่มีการใช้งาน 60 วินาที

การจัดการเวลาแฝง

การประทับเวลาของเซิร์ฟเวอร์

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

Java

DatabaseReference userLastOnlineRef = FirebaseDatabase.getInstance().getReference("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);

โคตรลิน + KTX

val userLastOnlineRef = Firebase.database.getReference("users/joe/lastOnline")
userLastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)

นาฬิกาเอียง

ในขณะที่ firebase.database.ServerValue.TIMESTAMP มีความแม่นยำกว่ามากและเป็นที่นิยมสำหรับการดำเนินการอ่าน / เขียนส่วนใหญ่บางครั้งอาจมีประโยชน์ในการประมาณนาฬิกาของไคลเอ็นต์ที่เอียงตามเซิร์ฟเวอร์ของ Firebase Realtime Database คุณสามารถแนบการเรียกกลับไปยังตำแหน่ง /.info/serverTimeOffset เพื่อรับค่าเป็นมิลลิวินาทีที่ไคลเอ็นต์ฐานข้อมูลเรียลไทม์ของ Firebase เพิ่มลงในเวลาที่รายงานในเครื่อง (เวลาในหน่วยมิลลิวินาที) เพื่อประมาณเวลาของเซิร์ฟเวอร์ โปรดทราบว่าความแม่นยำของออฟเซ็ตนี้อาจได้รับผลกระทบจากเวลาในการตอบสนองของเครือข่ายดังนั้นจึงมีประโยชน์เป็นหลักในการค้นหาความคลาดเคลื่อนของเวลานาฬิกาที่มีขนาดใหญ่ (> 1 วินาที)

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

โคตรลิน + KTX

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

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

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

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

นี่คือระบบแสดงตนของผู้ใช้ที่เรียบง่าย:

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(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(DatabaseError error) {
        Log.w(TAG, "Listener was cancelled at .info/connected");
    }
});

โคตรลิน + KTX

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