2015-12-28 49 views
44

Bất cứ khi nào tôi sử dụng addListenerForSingleValueEvent với setPersistenceEnabled(true), tôi chỉ quản lý để có được một bản sao ẩn địa phương của DataSnapshotKHÔNG các cập nhật DataSnapshot từ máy chủ.căn cứ hỏa lực ngoại tuyến Capabilities và addListenerForSingleValueEvent

Tuy nhiên, nếu tôi sử dụng addValueEventListener với setPersistenceEnabled(true), tôi có thể nhận bản sao mới nhất của DataSnapshot từ máy chủ.

Đây có phải là bình thường đối với addListenerForSingleValueEvent vì nó chỉ tìm kiếm DataSnapshot tại địa phương (ngoại tuyến) và loại bỏ người nghe của mình sau khi lấy thành công DataSnapshotONCE (hoặc offline hoặc online)?

Trả lời

57

Tính bền vững hoạt động

Ứng dụng khách Firebase giữ bản sao tất cả dữ liệu bạn đang tích cực nghe trong bộ nhớ. Khi người nghe cuối ngắt kết nối, dữ liệu sẽ bị xóa khỏi bộ nhớ.

Nếu bạn kích hoạt kiên trì đĩa trong một ứng dụng căn cứ hỏa lực Android với:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 

Các căn cứ hỏa lực khách hàng sẽ giữ một bản sao cục bộ (trên đĩa) của tất cả các dữ liệu mà ứng dụng gần đây đã lắng nghe.

gì xảy ra khi bạn đính kèm một người biết lắng nghe

Giả sử bạn có sau ValueEventListener:

ValueEventListener listener = new ValueEventListener() { 
    @Override 
    public void onDataChange(DataSnapshot snapshot) { 
     System.out.println(snapshot.getValue()); 
    } 

    @Override 
    public void onCancelled(FirebaseError firebaseError) { 
     // No-op 
    } 
}; 

Khi bạn thêm một ValueEventListener đến một vị trí:

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

Nếu giá trị của vị trí nằm trong bộ nhớ cache trên đĩa cục bộ, ứng dụng khách Firebase sẽ gọi ngay lập tức onDataChange() cho giá trị đó từ bộ nhớ cache cục bộ. Nếu sau đó sẽ bắt đầu kiểm tra với máy chủ, để yêu cầu bất kỳ cập nhật nào cho giá trị. Sau đó, có thể sẽ gọi lại onDataChange() nếu có thay đổi dữ liệu trên máy chủ kể từ lần cuối cùng được thêm vào bộ nhớ cache.

gì xảy ra khi bạn sử dụng addListenerForSingleValueEvent

Khi bạn thêm một sự kiện nghe giá trị duy nhất với cùng vị trí:

ref.addListenerForSingleValueEvent(listener); 

Các khách hàng căn cứ hỏa lực sẽ (như trong tình hình trước) ngay lập tức gọi onDataChange() cho giá trị từ bộ đệm đĩa cục bộ. Nó sẽ không phải gọi thêm onDataChange() bất kỳ lần nào nữa, ngay cả khi giá trị trên máy chủ hóa ra khác. Lưu ý rằng dữ liệu cập nhật vẫn sẽ được yêu cầu và trả lại cho các yêu cầu tiếp theo.

này được bao phủ trước đó trong How does Firebase sync work, with shared data?

Giải pháp và cách giải quyết

Giải pháp tốt nhất là sử dụng addValueEventListener(), thay vì một người biết lắng nghe sự kiện duy nhất có giá trị. Trình nghe giá trị thông thường sẽ nhận được cả sự kiện cục bộ tức thời và cập nhật tiềm năng từ máy chủ.

Giải pháp thay thế bạn cũng có thể call keepSynced(true) trên các vị trí mà bạn sử dụng trình xử lý sự kiện có giá trị một lần. Điều này đảm bảo rằng dữ liệu được cập nhật bất cứ khi nào nó thay đổi, giúp cải thiện đáng kể khả năng trình xử lý sự kiện giá trị đơn của bạn sẽ thấy giá trị hiện tại.

+2

Cảm ơn bạn. Tôi hiểu ngay từ câu trả lời của liên kết .. –

+3

Cảm ơn bạn đã minh họa hoàn hảo. Nhưng keepSynced (true) và addValueEventListener sẽ luôn giữ kết nối mở. Ngược lại với keepSynced (false) và addListenerForSingleValueEvent sẽ cho phép firebase ngắt kết nối sau một thời gian. Làm thế nào tôi có thể bắt buộc cập nhật thủ công một lần? –

+0

Một hành vi bất tiện, nó làm cho thử nghiệm hầu như không thể. –

0

Bạn có thể tạo giao dịch và hủy bỏ nó, sau đó onComplete sẽ được gọi khi trực tuyến (nline dữ liệu) hoặc offline (dữ liệu lưu trữ)

tôi trước đây tạo ra chức năng mà chỉ làm việc nếu cơ sở dữ liệu có kết nối lomng đủ để làm đồng bộ. Tôi đã khắc phục sự cố bằng cách thêm thời gian chờ. Tôi sẽ làm việc này và kiểm tra nếu điều này hoạt động. Có lẽ trong tương lai, khi tôi nhận được thời gian rảnh rỗi, tôi sẽ tạo lib android và xuất bản nó, nhưng lúc đó là mã trong 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) 
      } 
     } 

Trong mã nếu thời gian ra được thiết lập sau đó tôi thiết lập hẹn giờ mà sẽ gọi giao dịch với hủy bỏ. Giao dịch này sẽ được gọi ngay cả khi ngoại tuyến và sẽ cung cấp dữ liệu trực tuyến hoặc được lưu vào bộ nhớ cache (trong chức năng này có khả năng là dữ liệu này được lưu trong bộ nhớ cache một). Sau đó, tôi gọi giao dịch thành công. OnComplete sẽ được gọi là CHỈ nếu chúng tôi nhận được phản hồi từ cơ sở dữ liệu firebase. Bây giờ chúng ta có thể hủy hẹn giờ (nếu không null) và gửi dữ liệu đến gọi lại.

Việc triển khai này giúp đảm bảo 99% dữ liệu là từ bộ nhớ cache hoặc là dữ liệu trực tuyến.

Nếu bạn muốn làm cho nó nhanh hơn cho ẩn (để không chờ đợi một cách ngớ ngẩn với thời gian chờ khi rõ ràng là cơ sở dữ liệu không được kết nối) sau đó kiểm tra xem cơ sở dữ liệu được kết nối trước khi sử dụng chức năng trên:

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

Khi workinkg với tính kiên trì được bật, tôi đã tính số lần người nghe nhận được cuộc gọi đến onDataChange() và dừng lại để nghe 2 lần. Làm việc cho tôi, có thể giúp:

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); 
} 
Các vấn đề liên quan