在 Android 裝置上啟用離線功能

即使應用程式暫時遺失網路,Firebase 應用程式仍可正常運作 以獲得最佳效能和最安全的連線此外,Firebase 還提供可在本機保存資料的工具, 管理狀態及處理延遲情形

磁碟持續性

Firebase 應用程式會自動處理暫時的網路中斷問題。 離線時可存取快取資料,Firebase 會重新傳送任何寫入作業 並在恢復網路連線時執行動作

啟用磁碟持續性功能後,應用程式會將資料寫入本機 讓應用程式即使處於離線狀態,也能在離線時保持狀態 或是作業系統重新啟動應用程式

只需加入一行程式碼,即可啟用磁碟持續性。

Kotlin+KTX

Firebase.database.setPersistenceEnabled(true)

Java

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

持續性行為

啟用保留功能後,Firebase Realtime Database 用戶端的所有資料 會在線上留存於磁碟且可以離線時同步處理, 即使使用者或作業系統重新啟動應用程式也就是說, 透過儲存在快取中的本機資料,如線上般運作。 若是本機更新,則會繼續觸發接聽程式回呼。

Firebase Realtime Database 用戶端會自動保留所有佇列 在應用程式離線時執行的寫入作業。 啟用持續性時,此佇列也會被保存到磁碟中, 使用者可以使用 會重新啟動應用程式。應用程式重新連上網路後, 這項作業會傳送至 Firebase Realtime Database 伺服器。

如果應用程式使用 Firebase 驗證 Firebase Realtime Database 用戶端會保留使用者的驗證 符記 如果驗證權杖在應用程式離線時到期,用戶端會暫停 寫入作業,直到應用程式重新驗證使用者為止,否則 寫入作業可能會因安全性規則而失敗。

持續更新資料

Firebase Realtime Database 會同步處理並儲存 有效的事件監聽器資料另外,您可以保留特定位置 同步。

Kotlin+KTX

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

Java

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

Firebase Realtime Database 用戶端會自動下載 並保持工作表的一致性,即使參照沒有 有效的事件監聽器您可以使用 導入的功能

Kotlin+KTX

scoresRef.keepSynced(false)

Java

scoresRef.keepSynced(false);

根據預設,系統會快取先前同步處理資料的 10 MB。這應該是 可滿足大多數應用程式的需求如果快取超過設定的大小 Firebase Realtime Database 會清除近期最少使用的資料。 已同步的資料不會從快取中清除。

離線查詢資料

Firebase Realtime Database 會儲存查詢傳回的資料,以供使用 就能離線使用對於在離線狀態下建立的查詢 Firebase Realtime Database 會繼續處理先前載入的資料。 如果要求的資料尚未載入,Firebase Realtime Database 會載入 擷取自本機快取的資料網路連線恢復後 系統就會載入資料並顯示查詢

舉例來說,這段程式碼會查詢 Firebase Realtime Database 分中的四個項目

Kotlin+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

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

    // ...
});

假設使用者中斷連線並離線,然後重新啟動應用程式。 當應用程式處於離線狀態時,應用程式會查詢 同一個位置。這項查詢會成功傳回最後兩個項目 因為該應用程式已載入上述查詢中的所有四個項目。

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

    // ...
})

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 用戶端會觸發 「已新增子項」取得分數最高的兩隻恐龍 因此能保留快取狀態但不會產生因為應用程式含有 從未在連線時執行該查詢。

如果應用程式在離線時要求最後六個項目,則會傳回 「已新增子項」即可立即取得四個快取項目的事件當 裝置恢復連線,Firebase Realtime Database 用戶端會同步處理 與伺服器通訊,並得到最後兩個「新增子項」和 「value」事件。

離線處理交易

凡是在應用程式離線時執行的交易,都會排入佇列。 應用程式重新連上網路後,系統就會將交易傳送至 Realtime Database 伺服器連線。

管理在家狀態

在即時應用程式中,建議偵測用戶端 。例如,你可以設定 想將使用者標示為「離線」當用戶端中斷連線時。

Firebase 資料庫用戶端提供簡易基本功能,可協助您 用戶端與 Firebase 資料庫中斷連線時,會寫入資料庫 伺服器無論用戶端是否完全中斷連線,都會執行這些更新。 因此即使連線中斷,您依然可以用它們清理資料 或用戶端停止運作所有寫入作業,包括設定 並在連線中斷時執行更新、移除、移除等動作。

以下是在連線中斷時,使用 onDisconnect 原始版本:

Kotlin+KTX

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() 作業時,作業 位於 Firebase Realtime Database 伺服器上。伺服器會檢查 確保使用者可以執行要求的寫入事件,並通知 失效的應用程式。接著,伺服器 它會監控連線狀態如果連線逾時,或 由 Realtime Database 用戶端主動關閉,伺服器會檢查安全 次 (以確保作業仍然有效) 時,請叫用 活動。

應用程式可在寫入作業中使用回呼 確認 onDisconnect 已正確連接:

Kotlin+KTX

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

您也可以呼叫 .cancel() 來取消 onDisconnect 事件:

Kotlin+KTX

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

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 為 false,這不會 確保個別用戶端也會讀取 false。

在 Android 中,Firebase 會自動管理 降低頻寬用量和電池用量如果用戶端沒有活躍事件監聽器, 沒有待處理的寫入作業或 onDisconnect 且不會明確中斷連線 goOffline 方法, Firebase 會在閒置 60 秒後關閉連線。

處理延遲時間

伺服器時間戳記

Firebase Realtime Database 伺服器提供插入機制 在伺服器上產生的資料時間戳記 (以資料形式呈現)。這項功能結合了 onDisconnect,可讓您輕鬆可靠地記錄 Realtime Database 用戶端中斷連線的時間:

Kotlin+KTX

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 用戶端的價值 (以毫秒為單位) 與當地回報時間相加 (以毫秒為單位),進行估算 伺服器時間請注意,位移的精確度會受到 因此非常適合用於探索網路延遲 時鐘時間出現明顯差異 (> 1 秒)。

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

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」用戶端在線上。用戶端在下列情況會將這個位置設為 true 以及連線中斷時的時間戳記。這個時間戳記 表示特定使用者上次上線的時間。

請注意,應用程式應在使用者中斷連線前,將中斷作業排入佇列 線上標記,以避免客戶的 在能將兩個指令傳送至伺服器之前,網路連線中斷。

這類簡易的使用者線上狀態系統如下:

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

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