Firebase离线功能和addListenerForSingleValueEvent

每当我添加addListenerForSingleValueEvent与setPersistenceEnabled(true),我只能设法得到一个本地的datasnapshot的离线副本,而不是从服务器更新的datasnapshot。

但是,如果我添加了setVersionEventListener和setPersistenceEnabled(true),我可以从服务器获取最新的datasnapshot副本。

这是正常的addListenerForSingleValueEvent,因为它只在本地搜索datasnapshot(离线),并成功检索数据快照ONCE(脱机或联机)后,删除其侦听器?

持久性如何工作

Firebase客户端会保留您在内存中正在收听的所有数据的副本。 一旦最后一个监听器断开,数据将从内存中清除。

如果您在Firebase Android应用程序中启用磁盘持久性,请执行以下操作:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 

Firebase客户端将保留应用程序最近收听的所有数据的本地副本(磁盘上)。

当你附加一个监听器会发生什么

假设你有以下的ValueEventListener

 ValueEventListener listener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { System.out.println(snapshot.getValue()); } @Override public void onCancelled(FirebaseError firebaseError) { // No-op } }; 

当您将ValueEventListener添加到位置时:

 ref.addValueEventListener(listener); // OR ref.addListenerForSingleValueEvent(listener); 

如果该位置的值位于本地磁盘缓存中,则Firebase客户端将立即从本地缓存中为该值调用onDataChange() 。 如果将然后也启动与服务器的检查,要求任何更新的价值。 如果服务器上次添加到缓存后发生数据更改,则可能会再次调用onDataChange()

当你使用addListenerForSingleValueEvent时会发生什么

将单个值事件侦听器添加到相同的位置时:

 ref.addListenerForSingleValueEvent(listener); 

Firebase客户端(就像之前的情况一样)会立即调用onDataChange()来获取本地磁盘缓存中的值。 即使服务器上的值不同,它也不会 onDataChange()调用onDataChange() 。 请注意,更新的数据仍然会被请求,并在随后的请求返回。

Firebase同步如何与共享数据一起工作?

解决方案和解决方法

最好的解决方案是使用addValueEventListener() ,而不是单值事件侦听器。 常规值监听器将同时获得来自服务器的即时本地事件和潜在更新。

作为解决方法,您还可以在使用单值事件侦听器的位置调用keepSynced(true) 。 这确保了数据在更改时被更新,这大大提高了单值事件侦听器看到当前值的机会。

您可以创建事务并中止它,然后在线(nline数据)或离线(缓存数据)时调用onComplete

我以前创建的功能,只有当数据库连接到足够的同步。 我通过添加超时来解决问题。 我会研究这个,并测试它是否有效。 也许在将来,当我获得空闲时间的时候,我会创建android 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) } 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) } override fun doTransaction(p0: MutableData?): Transaction.Result { return Transaction.success(p0) } } 

在代码中,如果超时设置,那么我设置了一个计时器,它会以异常终止来调用事务。 即使在离线情况下,这个事务也会被调用,并且会提供在线或者缓存的数据(在这个函数中这个数据被缓存的可能性很高)。 然后,我称之为成功的交易。 只有从Firebase数据库获得响应, OnComplete才会被调用。 我们现在可以取消定时器(如果不为空)并发送数据到回调。

这个实现使开发者99%确信数据来自缓存或者在线。

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

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