如何绕过Firebase缓存刷新数据(在Android应用中)?

时间:2016-02-17 10:54:48

标签: android firebase firebase-realtime-database

在一个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;
}

如果我使用addValueEventListeneraddListenerForSingleValueEventref.keepSynced混合,我会获得相同的行为:

我们说我的用户在缓存中的得分值为5,而Firebase DB的得分值为11。

当我拨打getUser时,我将获得5分(Firebase首先要求缓存),因此我不会调用doSomething()方法。

如果我取消注释我的示例中的Thread.sleep()代码,Firebase缓存将有足够的时间进行更新,我的getUser将返回正确的分数值(11)。

那么如何直接从服务器端直接询问最新值并绕过缓存?

7 个答案:

答案 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
               
            }