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

Thi*_*ryC 34 android firebase firebase-realtime-database

在Android应用程序上,必须在大多数时间离线工作,当它在线时,为ie做一些同步操作:

User myUser =  MyclientFacade.getUser();
If (myUser.getScore > 10) {
    DoSomething() 
}
Run Code Online (Sandbox Code Playgroud)

用户是Firebase填写的POJO;

激活Firebase缓存时会出现此问题

Firebase.getDefaultConfig().setPersistenceEnabled(true);
Run Code Online (Sandbox Code Playgroud)

并且用户已经在缓存中,并且第三方(甚至是其他设备)在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;
}
Run Code Online (Sandbox Code Playgroud)

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

假设我的用户在缓存中的得分值为5,而Firebase DB中的得分值为11.

当我打电话时,getUser我将获得5分(Firebase首先要求缓存),所以我不会调用该doSomething()方法.

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

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

Ant*_*427 34

这个问题在我的应用程序中也给我带来了很大的压力.

我尝试了所有的东西,从改变.addListenerForSingleValueEvent().addValueEventListener()尝试创造性.keepSynced()地使用延迟(Thread.sleep()你已经描述过的方法)并没有真正有效地工作(即使Thread.sleep()是在生产应用程序中不能真正接受的方法也没有给我一致结果).

所以我做的是:在创建一个Query对象并调用.keepSynced()它之后,我继续在我正在查询的节点中编写一个mock/token对象,然后在该操作的完成监听器中,我做了我想要的数据检索删除模拟对象后执行操作.

就像是:

 MockObject mock = new MockObject();
    mock.setIdentifier("delete!");

    final Query query = firebase.child("node1").child("node2");

    query.keepSynced(true);

    firebase.child("node1").child("node2").child("refreshMock")
            .setValue(mock, new CompletionListener() {

                @Override
                public void onComplete(FirebaseError error, Firebase afb) {

                    query.addListenerForSingleValueEvent(new ValueEventListener() {

                        public void onDataChange(DataSnapshot data) {

                            // get identifier and recognise that this data
                            // shouldn't be used
                            // it's also probably a good idea to remove the
                            // mock object
                            // using the removeValue() method in its
                            // speficic node so
                            // that the database won't be saddled with a new
                            // one in every
                            // read operation

                        }

                        public void onCancelled(FirebaseError error) {
                        }

                    });

                }

            });
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,这对我来说始终如一!(好吧,大约一天,所以带上一粒盐).似乎在读取之前以某种方式执行写操作绕过缓存,这是有道理的.所以数据又恢复了新鲜感.

唯一的缺点是在读取操作之前的额外写入操作,这可能会导致一个小的延迟(显然使用一个小对象),但如果这是总是新鲜数据的价格,我会接受它!

希望这可以帮助!

  • 我真的不明白,为什么Firebase的架构如此奇怪.有没有办法做到这一点?:/ (3认同)
  • _refreshMock_ 不必是我们节点的子节点。它可以是任何查询,例如 firebase.child("any_node").child("refreshMock") 也可以 (2认同)

Rob*_*ert 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
            }
        }
    });
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但是,如果您处于离线状态,则在重新上线之前无法完成.结束将其包装在超时中并回退到addListenerForSingleValueEvent,并在它重新联机时丢弃该值. (2认同)