如何绕过Firebasecaching刷新数据(在Android应用程序中)?

在Android应用程序中,大多数情况下我需要离线工作,当它在线时,为ie执行一些同步操作:

User myUser = MyclientFacade.getUser(); If (myUser.getScore > 10) { DoSomething() } 

用户是由Firebase填充的POJO;

当Firebasecaching被激活时,就会出现问题

 Firebase.getDefaultConfig().setPersistenceEnabled(true); 

并且用户已经在caching中,并且由第三方(甚至是另一个设备)在Firebase数据库上更新数据。 事实上,当我查询Firebase获取用户时,我首先从caching获取数据,然后从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混合,我获得相同的行为:

比方说,我的用户在caching中的分数值是5,Firebase DB是11。

当我打电话给getUser我将获得5分(Firebase询问caching首先),所以我不会调用doSomething()方法。

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

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

Solutions Collecting From Web of "如何绕过Firebasecaching刷新数据(在Android应用程序中)?"

这是一个问题,我的应用程序也给我造成了很大的压力。

我尝试了一切,从更改.addListenerForSingleValueEvent().addValueEventListener() ,试图创造性地使用.keepSynced()来使用延迟(上面描述的Thread.sleep()方法),并没有真正的工作一致(甚至Thread.sleep()方法,这在生产应用程序中并不真正可以接受,但没有给出一致的结果)。

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

就像是:

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

这一直工作到目前为止我! (好吧,一天左右,所以拿一粒盐)。 在读取之前,似乎要做一个写操作绕过caching,这是有道理的。 所以数据恢复了新鲜。

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

希望这可以帮助!

我发现的解决方法是使用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 } } }); 

我的解决scheme是在加载时调用Database.database()。isPersistenceEnabled = true,然后在我需要刷新的任何节点上调用.keepSynced(true)。

这里的问题是,如果你在.keepSynced(true)之后立即查询你的节点,那么你很可能得到caching而不是新的数据。 略微跛脚但function性的工作:延迟一秒钟左右的节点查询,以便给Firebase一些时间来获取新的数据。 如果用户处于离线状态,您将获得caching。

哦,如果这是一个你不想在后台保持最新状态的节点,记得当你完成后调用.keepSynced(false)。

我尝试了两个接受的解决scheme,我试着交易。 事务解决scheme更清洁,更好,但成功的OnComplete只有当数据库在线,所以你不能从caching加载调用。 但是您可以放弃事务,那么onComplete将在脱机时调用(使用caching的数据)。

我以前创build的function,只有当数据库连接到足够的同步。 我通过添加超时来解决问题。 我会研究这个,并testing它是否有效。 也许在将来,当我获得空闲时间的时候,我会创buildandroid lib并发布它,但到那时它就是kotlin中的代码:

 /** * @param databaseReference reference to parent database node * @param callback callback with mutable list which returns list of objects and boolean if data is from cache * @param timeOutInMillis if not set it will wait all the time to get data online. If set - when timeout occurs it will send data from cache if exists */ fun readChildrenOnlineElseLocal(databaseReference: DatabaseReference, callback: ((mutableList: MutableList<@kotlin.UnsafeVariance T>, isDataFromCache: Boolean) -> Unit), timeOutInMillis: Long? = null) { var countDownTimer: CountDownTimer? = null val transactionHandlerAbort = object : Transaction.Handler { //for cache load override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) { val listOfObjects = ArrayList<T>() data?.let { data.children.forEach { val child = it.getValue(aClass) child?.let { listOfObjects.add(child) } } } callback.invoke(listOfObjects, true) removeListener() } override fun doTransaction(p0: MutableData?): Transaction.Result { return Transaction.abort() } } val transactionHandlerSuccess = object : Transaction.Handler { //for online load override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) { countDownTimer?.cancel() val listOfObjects = ArrayList<T>() data?.let { data.children.forEach { val child = it.getValue(aClass) child?.let { listOfObjects.add(child) } } } callback.invoke(listOfObjects, false) removeListener() } override fun doTransaction(p0: MutableData?): Transaction.Result { return Transaction.success(p0) } } 

如果你想让它离线更快(当不明显数据库没有连接时,不要等待超时),那么在使用上面的函数之前检查数据库是否连接:

 DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected"); connectedRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { boolean connected = snapshot.getValue(Boolean.class); if (connected) { System.out.println("connected"); } else { System.out.println("not connected"); } } @Override public void onCancelled(DatabaseError error) { System.err.println("Listener was cancelled"); } }); 

只需将这些代码添加到应用程序类的 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