Offline issue with Firestore vs Firebase

前端 未结 4 1575
梦谈多话
梦谈多话 2020-12-31 09:44

I converted one of my apps to the new Firestore. I am doing things like saving a document on a button click, and then in the onSuccess listener, going to a diff

相关标签:
4条回答
  • 2020-12-31 10:16

    For offline support you need to set Source.CACHE

    docRef.get(Source.CACHE).addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<DocumentSnapshot> task) {
            if (task.isSuccessful()) {
                // Document found in the offline cache
                DocumentSnapshot document = task.getResult();
    
            } else {
                //error
            }
        }
    });
    
    0 讨论(0)
  • Cloud Firestore provide us feature for handle offline data but you need to use “Snapshot” (QuerySnapshot, DocumentSnapshot) to handle this case, unfortunately it not documented well. This is some code example (I use Kotlin Android) to handle case using Snapshot:

    UPDATE DATA:

    db.collection("members").document(id)
      .addSnapshotListener(object : EventListener<DocumentSnapshot> {
          override fun onEvent(snapshot: DocumentSnapshot?,
                               e: FirebaseFirestoreException?) {
              if (e != null) {
                  Log.w(ContentValues.TAG, "Listen error", e)
                  err_msg.text = e.message
                  err_msg.visibility = View.VISIBLE;
                  return
              }
              snapshot?.reference?.update(data)
    
          }
      })
    

    ADD DATA:

    db.collection("members").document()
     .addSnapshotListener(object : EventListener<DocumentSnapshot> {
         override fun onEvent(snapshot: DocumentSnapshot?,
                              e: FirebaseFirestoreException?) {
             if (e != null) {
                 Log.w(ContentValues.TAG, "Listen error", e)
                 err_msg.text = e.message
                 err_msg.visibility = View.VISIBLE;
                 return
             }
             snapshot?.reference?.set(data)
    
         }
     })
    

    DELETE DATA:

    db.collection("members").document(list_member[position].id)
       .addSnapshotListener(object : EventListener<DocumentSnapshot> {
           override fun onEvent(snapshot: DocumentSnapshot?,
                                e: FirebaseFirestoreException?) {
               if (e != null) {
                   Log.w(ContentValues.TAG, "Listen error", e)
                   return
               }
               snapshot?.reference?.delete()
           }
       })
    

    You can see code example here: https://github.com/sabithuraira/KotlinFirestore and blog post http://blog.farifam.com/2017/11/28/android-kotlin-management-offline-firestore-data-automatically-sync-it/

    0 讨论(0)
  • 2020-12-31 10:19

    I found out how to do it using info at http://blog.farifam.com. Basically you must use SnapshotListeners instead of OnSuccess listeners for offline work. Also, you cannot use Google's tasks because they won't compete offline.

    Instead (since Tasks are basically Promises), I used the Kotlin Kovenant library which can attach listeners to promises. One wrinke is that you must configure Kovenant to allow multiple resolution for a promise, since the event listener can be called twice (once when the data is added to the local cache, and once when it is synced to the server).

    Here is an example snippet of code, with success/failure listeners, that runs both online and offline.

    val deferred = deferred<DocumentSnapshot, Exception>() // create a deferred, which holds a promise
    // add listeners
    deferred.promise.success { Log.v(TAG, "Success! docid=" + it.id) }
    deferred.promise.fail { Log.v(TAG, "Sorry, no workie.") }
    
    val executor: Executor = Executors.newSingleThreadExecutor()
    val docRef = FF.getInstance().collection("mydata").document("12345")
    val data = mapOf("mykey" to "some string")
    
    docRef.addSnapshotListener(executor, EventListener<DocumentSnapshot> { snap: DocumentSnapshot?, e: FirebaseFirestoreException? ->
        val result = if (e == null) Result.of(snap) else Result.error(e)
        result.failure {
            deferred.reject(it) // reject promise, will fire listener
        }
        result.success { snapshot ->
            snapshot.reference.set(data)
            deferred.resolve(snapshot) // resolve promise, will fire listener
        }
    })
    
    0 讨论(0)
  • 2020-12-31 10:24

    When there is a loss of network connectivity (there is no network connection on user device), neither onSuccess() nor onFailure() are triggered. This behavior makes sense, since the task is considered completed only when the data has been committed (or rejected) on the Firebase server. So onSuccess() will fire only when the task completes successfully.

    There is no need to check for network availability before each save. There is workaround which easily can help you see if the Firestore client indeed can't connect to the Firebase server, which is by enabling debug logging:

    FirebaseFirestore.setLoggingEnabled(true);
    

    Operations that write data to the Firestore database are defined to signal completion once they've actually committed to the backend. As a result this is working as intended: while offline they won't signal completion.

    Note that the Firestore clients internally guarantee that you can read your own writes even if you don't wait for completion of the task from delete. The Firestore client is designed to continue functioning fine without an internet connection. So writing/deleting to the database without an internet connection is (by design) possible.

    0 讨论(0)
提交回复
热议问题