在一个Android应用程序上,它必须在线下工作,大部分时间我在线时需要进行一些同步操作,即:
User myUser = MyclientFacade.getUser();
If (myUser.getScore > 10) {
DoSomething()
}
用户是由Firebase填充的POJO;
激活Firebase缓存时出现问题
Firebase.getDefaultConfig().setPersistenceEnabled(true);
并且用户已经在缓存中,并且第三方(甚至是其他设备)在Firebase DB上更新数据。实际上,当我查询Firebase以获取用户时,我首先从缓存中获取数据,然后使用Firebase服务器中的最新数据获取第二个更改事件,但为时已晚!
让我们看看同步方法MyclientFacade.getUser():
Public User getUser() {
Firebase ref = myFireBaseroot.child("User").child(uid);
ref.keepSynced(true);
/* try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
final CountDownLatch signal = new CountDownLatch(1);
ref.addListenerForSingleValueEvent(new ValueEventListener() {
//ref.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
this.user = dataSnapshot.getValue(User.class);
signal.countDown();
}
@Override
public void onCancelled(FirebaseError firebaseError) {
signal.countDown();
}
});
signal.await(5, TimeUnit.SECONDS);
ref.keepSynced(false);
return this.user;
}
如果我使用addValueEventListener
或addListenerForSingleValueEvent
与ref.keepSynced
混合,我会获得相同的行为:
我们说我的用户在缓存中的得分值为5,而Firebase DB的得分值为11。
当我拨打getUser
时,我将获得5分(Firebase首先要求缓存),因此我不会调用doSomething()
方法。
如果我取消注释我的示例中的Thread.sleep()
代码,Firebase缓存将有足够的时间进行更新,我的getUser
将返回正确的分数值(11)。
那么如何直接从服务器端直接询问最新值并绕过缓存?
答案 0 :(得分:31)
这个问题在我的应用程序中也给我带来了很多压力。
我尝试了所有内容,从更改adding the action for later execution
到.addListenerForSingleValueEvent()
到尝试创造性地使用.addValueEventListener()
来使用延迟(您在上面描述的.keepSynced()
方法)并没有真正做到一致地工作(即使是Thread.sleep()
方法,在生产应用程序中并不能真正接受,但并没有给我一致的结果。)
所以我做的是:在创建查询对象并在其上调用Thread.sleep()
后,我继续在节点I' m中编写模拟/令牌对象在该操作的完成监听器中查询和那么,在删除模拟对象后,我做了我想做的数据检索。
类似的东西:
.keepSynced()
到目前为止,这对我一直有效! (好吧,大约一天,所以带上一粒盐)。似乎在读取之前以某种方式执行写操作绕过缓存,这是有道理的。所以数据又恢复了新鲜感。
唯一的缺点是在读取操作之前的额外写入操作,这可能会导致一个小的延迟(显然使用一个小对象),但如果这是总是新数据的价格,我会接受它!
希望这有帮助!
答案 1 :(得分:14)
我发现的解决方法是使用Firebase的runTransaction()
方法。这似乎始终从服务器检索数据。
String firebaseUrl = "/some/user/datapath/";
final Firebase firebaseClient = new Firebase(firebaseUrl);
// Use runTransaction to bypass cached DataSnapshot
firebaseClient.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
// Return passed in data
return Transaction.success(mutableData);
}
@Override
public void onComplete(FirebaseError firebaseError, boolean success, DataSnapshot dataSnapshot) {
if (firebaseError != null || !success || dataSnapshot == null) {
System.out.println("Failed to get DataSnapshot");
} else {
System.out.println("Successfully get DataSnapshot");
//handle data here
}
}
});
答案 2 :(得分:2)
只需在应用程序类上的onCreate
方法上添加此代码即可。 (更改数据库引用)
示例:
public class MyApplication extendes Application{
@Override
public void onCreate() {
super.onCreate();
DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores");
scoresRef.keepSynced(true);
}
}
对我很好。
参考: https://firebase.google.com/docs/database/android/offline-capabilities
答案 3 :(得分:2)
我的解决方案是在加载时调用Database.database()。isPersistenceEnabled = true,然后在我需要刷新的任何节点上调用.keepSynced(true)。
这里的问题是,如果您在.keepSynced(true)之后立即查询您的节点,那么您可能会获得缓存而不是新数据。稍微蹩脚但功能性的工作:延迟查询节点一秒左右,以便为Firebase提供一些时间来获取新数据。如果用户处于离线状态,您将获得缓存。
哦,如果它是一个你不想在后台永远保持最新状态的节点,请记得在你完成时调用.keepSynced(false)。
答案 4 :(得分:2)
在 Flutter 上,以下是从 realtimeDb 强制获取最新快照的方法:
Future<DataSnapshot> _latestRtdbSnapshot(DatabaseReference dbRef) async {
// ! **NASTY HACK**: Write/delete a child of the query to force an update.
await dbRef.keepSynced(true);
await dbRef.child('refreshMock').remove();
final res = await dbRef.once();
dbRef.keepSynced(false);
return res;
}
答案 5 :(得分:1)
使用此行
FirebaseDatabase.getInstance()。purgeOutstandingWrites();
答案 6 :(得分:1)
我知道已经很晚了,但是对于面临同样问题的人,我终于找到了完美的解决方案。您可以使用以下方法检查数据是否来自缓存的内存:
if (documentSnapShot.metadata.isFromCache){
// data is coming from the cached memory
} else {
// data is coming from the server
}
这是Kotlin
中的完整代码段:
gameRoomRef.addSnapshotListener { documentSnapshot, fireStoreException ->
fireStoreException?.let {
return@addSnapshotListener
}
documentSnapshot?.let {
if (it.metadata.isFromCache)
// data is from cached memory
else
// data is from server
}