2015-12-28 99 views
44

每当我使用addListenerForSingleValueEventsetPersistenceEnabled(true),我只能设法从服务器获取的DataSnapshot更新DataSnapshot本地脱机副本。火力地堡离线功能和addListenerForSingleValueEvent

但是,如果我使用addValueEventListenersetPersistenceEnabled(true),我可以从服务器上获得DataSnapshot的最新副本。

这是正常的,addListenerForSingleValueEvent,因为它仅搜索DataSnapshot本地(离线),并在成功获取DataSnapshotONCE(脱机或联机)删除其监听器?

回答

57

持久性如何工作

的火力地堡客户端保持所有数据你积极听取内存的副本。一旦最后一个监听器断开连接,数据将从内存中清除。

如果启用在火力地堡Android应用程序的磁盘使用持久性:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 

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

当你连接一个监听

假设你有以下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); 

的火力地堡客户端(如在以前的情况)立即调用onDataChange()为来自本地磁盘缓存的值。它将而不是再次调用onDataChange(),即使服务器上的值原来不同。请注意,更新的数据仍然会被请求并在随后的请求中返回。

这在How does Firebase sync work, with shared data?

解及变通方法

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

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

+2

谢谢。我现在从链接的答案了解到.. –

+3

感谢您的完美插图。但keepSynced(true)和addValueEventListener将始终保持打开的连接。与keepSynced(false)相反,addListenerForSingleValueEvent将允许firebase在某段时间后断开连接。我如何强制一次手动更新? –

+0

这是一种不方便的行为,它使测试几乎不可能。 –

0

您可以创建交易,并放弃它,那么的onComplete将在网上(n线数据)或脱机(缓存数据)

我以前创建仅当数据库有连接器LOMNG足以做到同步其工作函数调用。我通过添加超时来解决问题。我会研究这个并测试它是否有效。也许在未来,当我得到自由的时候,我将创建Android的lib和发布,但那时它是在科特林代码:

/** 
    * @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"); 
    } 
}); 
0

当workinkg启用了持久性,我计算了监听者接到对onDataChange()的调用的时间,并停止了2次监听。为我工作,也许帮助:

private int timesRead; 
private ValueEventListener listener; 
private DatabaseReference ref; 

private void readFB() { 
    timesRead = 0; 
    if (ref == null) { 
     ref = mFBDatabase.child("URL"); 
    } 

    if (listener == null) { 
     listener = new ValueEventListener() { 
      @Override 
      public void onDataChange(DataSnapshot dataSnapshot) { 
       //process dataSnapshot 

       timesRead++; 
       if (timesRead == 2) { 
        ref.removeEventListener(listener); 
       } 
      } 

      @Override 
      public void onCancelled(DatabaseError databaseError) { 
      } 
     }; 
    } 
    ref.removeEventListener(listener); 
    ref.addValueEventListener(listener); 
}