Chaque fois que j’utilise addListenerForSingleValueEvent
avec setPersistenceEnabled(true)
, je n’arrive qu’à obtenir une copie hors ligne locale de DataSnapshot
et non le DataSnapshot
mis à jour à partir du serveur.
Toutefois, si j’utilise addValueEventListener
avec setPersistenceEnabled(true)
, je peux obtenir la dernière copie de DataSnapshot
sur le serveur.
Est-ce normal pour addListenerForSingleValueEvent
car il recherche uniquement DataSnapshot
localement (hors ligne) et supprime son écouteur après avoir récupéré avec succès DataSnapshot
ONCE (hors ligne ou en ligne)?
Le client Firebase conserve une copie de toutes les données que vous écoutez activement en mémoire. Une fois le dernier écouteur déconnecté, les données sont vidées de la mémoire.
Si vous activez la persistance de disque dans une application Firebase Android avec:
Firebase.getDefaultConfig().setPersistenceEnabled(true);
Le client Firebase conservera une copie locale (sur le disque) de toutes les données récemment écoutées par l’application.
Supposons que vous ayez le ValueEventListener
suivant:
ValueEventListener listener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { System.out.println(snapshot.getValue()); } @Override public void onCancelled(FirebaseError firebaseError) { // No-op } };
Lorsque vous ajoutez un ValueEventListener
à un emplacement:
ref.addValueEventListener(listener); // OR ref.addListenerForSingleValueEvent(listener);
Si la valeur de l’emplacement se trouve dans le cache du disque local, le client Firebase appellera onDataChange()
immédiatement pour cette valeur à partir du cache local. Si c’est le cas, lancez également une vérification auprès du serveur pour demander des mises à jour de la valeur. Il peut ensuite appeler onDataChange()
nouveau si les données du serveur ont été modifiées depuis la dernière fois qu’il a été ajouté au cache.
addListenerForSingleValueEvent
Lorsque vous ajoutez un écouteur d’événement à valeur unique au même emplacement:
ref.addListenerForSingleValueEvent(listener);
Le client Firebase (comme dans la situation précédente) onDataChange()
immédiatement onDataChange()
pour la valeur du cache de disque local. Il n’invoquera plus le onDataChange()
, même si la valeur sur le serveur s’avère différente. Notez que les données mises à jour seront toujours demandées et renvoyées lors des demandes suivantes.
Cela a été traité précédemment dans Comment la synchronisation Firebase fonctionne-t-elle, avec des données partagées?
La meilleure solution consiste à utiliser addValueEventListener()
au lieu d’un écouteur d’événement à valeur unique. Un écouteur de valeur régulière recevra à la fois l’événement local immédiat et la mise à jour potentielle du serveur.
Pour contourner ce keepSynced(true)
vous pouvez également appeler keepSynced(true)
aux emplacements où vous utilisez un écouteur d’événement à valeur unique. Cela garantit que les données sont mises à jour chaque fois qu’elles changent, ce qui améliore considérablement les chances que votre écouteur d’événement à valeur unique voit la valeur actuelle.
Vous pouvez créer une transaction et l’abandonner, puis onComplete sera appelé en ligne (données nline) ou hors ligne (données mises en cache)
J’ai déjà créé une fonction qui ne fonctionnait que si la firebase database avait suffisamment de connexion pour faire de la synchronisation. J’ai résolu le problème en ajoutant un délai d’attente. Je vais travailler sur cela et tester si cela fonctionne. Peut-être qu’à l’avenir, lorsque j’aurai du temps libre, je créerai android lib et le publierai, mais d’ici là c’est le code de 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() 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 () 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) } }
Dans le code, si le délai est défini, j’établis un temporisateur qui appellera la transaction avec abort. Cette transaction sera appelée même hors ligne et fournira des données en ligne ou en cache (dans cette fonction, il y a de fortes chances que ces données soient mises en cache). Ensuite, j’appelle la transaction avec succès. OnComplete
sera appelé UNIQUEMENT si nous avons reçu une réponse de la firebase database firebase. Nous pouvons maintenant annuler le temporisateur (si non nul) et envoyer des données au rappel.
Cette implémentation fait en sorte que dev 99% soit certain que les données proviennent du cache ou sont en ligne.
Si vous voulez rendre la connexion plus rapide hors ligne (ne pas attendre stupidement avec le timeout lorsque la firebase database n’est pas connectée), vérifiez si la firebase database est connectée avant d’utiliser la fonction ci-dessus:
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"); } });
Lorsque Workinkg avec persistance activée, j’ai compté les fois où l’auditeur a reçu un appel à onDataChange () et arrêté d’écouter à 2 resockets. Travaillé pour moi, peut-être aide:
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); }