2015-12-28 140 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); 
}