web-dev-qa-db-fra.com

Problème hors ligne avec Firestore vs Firebase

J'ai converti une de mes applications vers le nouveau Firestore. Je fais des choses comme enregistrer un document sur un clic de bouton, puis dans l'écouteur onSuccess pour passer à une activité différente.

J'utilise également le fait que Firestore enregistre les tâches de renvoi des opérations de sauvegarde, pour les regrouper à l'aide de Tasks.whenAll:

val allTasks = Tasks.whenAll(
       createSupporter(supporter),,
       setStreetLookup(makeStreetKey(supporter.street_name)),
       updateCircleChartForUser(statusChange, createMode = true), 
       updateStatusCountForUser(statusChange))

      allTasks.addOnSuccessListener(this@SignUpActivity, successListener)
      allTasks.addOnFailureListener(this@SignUpActivity, onFailureListener)

Enfin, je récupère l’identifiant du document lors d’une sauvegarde réussie et le stocke dans des préférences ou dans une base de données locale pour une utilisation ultérieure (dans la variable onSuccessListener)

Tout cela fonctionne très bien. Jusqu'à ce qu'il y ait une perte de connectivité réseau . Ensuite, tout tombe en morceaux, car les tâches ne se terminent jamais et les écouteurs onSuccess/onFailure/onComplete ne sont jamais appelés. Donc, l'application se bloque.

Je travaille autour de cela en vérifiant la disponibilité du réseau avant chaque sauvegarde, puis en effectuant une solution de contournement en créant des tâches sans aucun écouteur. Je génère également un identifiant de document localement à l'aide d'un générateur d'UUID. 

BTW n’était pas comme cela que l’application fonctionnait avec l’ancienne base de feu. Dans ce cas, tout fonctionnait bien en mode hors connexion et je voyais des documents se synchroniser chaque fois que l'application se mettait en ligne. 

Mon solution de rechange pour Firestore semble un bidouillage terrible. Quelqu'un a-t-il proposé une meilleure solution?

Voir la base de données connexe La base de données Firestore sur les rappels d'insertion/suppression de documents qui ne sont pas appelés en l'absence de connexionaddOnCompleteListener n'est pas appelé hors connexion avec Cloud Firestore

8
Dutch Masters

Cloud Firestore nous fournit une fonctionnalité permettant de gérer les données hors connexion, mais vous devez utiliser «Instantané» (QuerySnapshot, DocumentSnapshot) pour traiter ce cas, malheureusement, il n'est pas bien documenté. Voici un exemple de code (j'utilise Kotlin Android) pour gérer les cas avec Snapshot:

MISE À JOUR DES DONNÉES:

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)

      }
  })

AJOUTER DES DONNÉES:

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)

     }
 })

SUPRIMMER LES DONNÉES:

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()
       }
   })

Vous pouvez voir un exemple de code ici: https://github.com/sabithuraira/KotlinFirestore et article de blog http://blog.farifam.com/2017/11/28/Android-kotlin-management-offline -firestore-data-synchronise-automatiquement/

12
Sabit Huraira

En cas de perte de connectivité réseau (il n'y a pas de connexion réseau sur la machine utilisateur), ni onSuccess() ni onFailure() ne sont déclenchés. Ce comportement est logique, car la tâche n'est considérée comme terminée que lorsque les données ont été validées (ou rejetées) sur le serveur Firebase. La méthode onComplete(Task<T> task) est également appelée uniquement lorsque la tâche est terminée. Donc, en cas d'absence de connexion Internet, ni onComplete n'est déclenché.

Il n'est pas nécessaire de vérifier la disponibilité du réseau avant chaque sauvegarde. Il existe une solution de contournement qui peut facilement vous aider à voir si le client Firestore ne peut pas se connecter au serveur Firebase, qui est par enabling debug logging:

FirebaseFirestore.setLoggingEnabled(true);

Les opérations qui écrivent des données dans la base de données Firestore sont définies sur signal completion une fois qu'elles ont été validées dans le backend. En conséquence, cela fonctionne comme prévu: lorsqu'ils sont hors ligne, ils ne signalent pas l'achèvement.

Notez que les clients Firestore garantissent en interne que vous pouvez lire vos propres écritures même si vous n'attendez pas la fin de la tâche après la suppression.

2
Alex Mamo

J'ai découvert comment faire en utilisant info à http://blog.farifam.com . En gros, vous devez utiliser SnapshotListeners au lieu de OnSuccess écouteurs pour le travail hors connexion. ne peut pas utiliser les tâches de Google car elles ne rivaliseront pas hors connexion.

Au lieu de cela (puisque les tâches sont essentiellement des promesses), j’ai utilisé la bibliothèque Kotlin Kovenant qui permet d’attacher les auditeurs aux promesses. Une solution est que vous devez configurer Kovenant pour permettre la résolution multiple d'une promesse, car l'écouteur d'événements peut être appelé deux fois (une fois lorsque les données sont ajoutées au cache local et une fois lorsqu'elles sont synchronisées sur le serveur).

Voici un exemple d'extrait de code, avec des écouteurs de succès/échec, qui s'exécute en ligne et hors ligne.

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
    }
})
1
Dutch Masters