我认为对于您的问题,“离线”意味着客户端没有连接。在我的测试中,我通过启用飞行模式进行了模拟。
Firebase 离线功能在 user guide 中进行了描述。那里提供的一个对您的问题很重要的细节是:
Firebase 实时数据库客户端会自动保留一个队列
在您的应用离线时执行的所有写入操作...
当应用程序重新连接时,所有操作都发送到
Firebase 实时数据库服务器。
您可以使用以下代码查看此行为,该代码将完成侦听器添加到 setValue() 调用。在我的测试中,我将设备置于飞行模式(离线),运行代码,然后禁用飞行模式以重新上线。为每个setValue() 生成一条日志消息,确认写入操作已排队并在重新建立连接时发送。这就解释了为什么写入满足您的验证规则:客户端不会发送一个最终值为 5 的写入请求,而是发送五个具有原始递增值的请求。
您可以通过再次运行测试来确认规则是否有效,而无需先将test 的值重置回 0。每次写入都会失败。
此代码还演示了 Firebase 客户端如何处理客户端离线时所做的更改,这些更改后来被安全规则拒绝。离线时,在客户端缓存中进行更改,onDataChange() 回调触发新的(未验证的)值。稍后,当客户端上线并且更改被服务器拒绝时,onDataChange() 会再次使用之前的值触发。
final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("test");
ref.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "onDataChange: test=" + dataSnapshot.getValue(Integer.class));
}
@Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
final DatabaseReference.CompletionListener completionListener =
new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError == null) {
Log.d(TAG, "setValue() Success");
} else {
Log.d(TAG, "setValue() Failed " + databaseError.getMessage());
}
}
};
ref.setValue(1, completionListener);
ref.setValue(2, completionListener);
ref.setValue(3, completionListener);
ref.setValue(4, completionListener);
ref.setValue(5, completionListener);