Cómo evitar la caché Firebase para actualizar los datos (en la aplicación para Android)?

En una aplicación de Android que debe funcionar sin conexión la mayor parte del tiempo que necesito, cuando está en línea, para hacer algunas operaciones síncronas, por ejemplo:

User myUser = MyclientFacade.getUser(); If (myUser.getScore > 10) { DoSomething() } 

Donde el usuario es un POJO llenado por Firebase;

El problema se produce cuando se activa la caché Firebase

 Firebase.getDefaultConfig().setPersistenceEnabled(true); 

Y el usuario ya está en caché y los datos son actualizados en Firebase DB por un tercero (o incluso por otro dispositivo). De hecho, cuando consulta Firebase para obtener el usuario, obtengo primero los datos del caché y más tarde un segundo evento de cambio con los últimos datos del servidor Firebase, pero es demasiado tarde.

Veamos el método síncrono MyclientFacade.getUser ():

 Public User getUser() { Firebase ref = myFireBaseroot.child("User").child(uid); ref.keepSynced(true); /* try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }*/ final CountDownLatch signal = new CountDownLatch(1); ref.addListenerForSingleValueEvent(new ValueEventListener() { //ref.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { this.user = dataSnapshot.getValue(User.class); signal.countDown(); } @Override public void onCancelled(FirebaseError firebaseError) { signal.countDown(); } }); signal.await(5, TimeUnit.SECONDS); ref.keepSynced(false); return this.user; } 

addValueEventListener el mismo comportamiento si uso addValueEventListener o addListenerForSingleValueEvent mezclado con ref.keepSynced :

Digamos que el valor de puntuación de mi usuario en caché es 5 y de Firebase DB es 11.

Cuando llamo a getUser la puntuación de 5 (Firebase ask cache first) así que no llamaré el método doSomething() .

Si descomente el código Thread.sleep() de mi ejemplo, el caché de Firebase tendrá suficiente tiempo para ser actualizado y mi getUser devolverá el valor de puntuación correcto (11).

Entonces, ¿cómo puedo pedir directamente el último valor directamente desde el lado del servidor y evitar la caché?

5 Solutions collect form web for “Cómo evitar la caché Firebase para actualizar los datos (en la aplicación para Android)?”

Este fue un problema que me estaba causando mucho estrés en mi aplicación también.

Lo intenté todo, desde cambiar .addListenerForSingleValueEvent() a .addValueEventListener() a intentar usar creativamente .keepSynced() con un delay (el método Thread.sleep() que has descrito anteriormente) y nada funcionó de manera consistente (incluso el Thread.sleep() método, que no era realmente aceptable en una aplicación de producción no me dio resultados coherentes).

Entonces lo que hice fue esto: después de crear un objeto Query y llamar a .keepSynced() en él, entonces procedo a escribir un objeto simulado / token en el nodo que estoy consultando y ENTONCES en el oyente de finalización de esa operación, hago los datos Recuperación que quiero hacer, después de eliminar el objeto de simulación.

Algo como:

  MockObject mock = new MockObject(); mock.setIdentifier("delete!"); final Query query = firebase.child("node1").child("node2"); query.keepSynced(true); firebase.child("node1").child("node2").child("refreshMock") .setValue(mock, new CompletionListener() { @Override public void onComplete(FirebaseError error, Firebase afb) { query.addListenerForSingleValueEvent(new ValueEventListener() { public void onDataChange(DataSnapshot data) { // get identifier and recognise that this data // shouldn't be used // it's also probably a good idea to remove the // mock object // using the removeValue() method in its // speficic node so // that the database won't be saddled with a new // one in every // read operation } public void onCancelled(FirebaseError error) { } }); } }); } 

Esto ha funcionado constantemente hasta ahora para mí! (Bueno, por un día o así, así que tomar esto con un grano de sal). Parece que hacer una operación de escritura antes de leer de alguna manera evita la caché, lo que tiene sentido. Así que los datos vuelven frescos.

El único inconveniente es la operación de escritura extra antes de la operación de lectura, lo que puede causar un pequeño retraso (obviamente, utilizar un objeto pequeño), pero si ese es el precio de los datos siempre frescos, lo tomaré!

¡Espero que esto ayude!

Una solución que he descubierto está utilizando el método runTransaction() Firebase. Parece que siempre recupera los datos del servidor.

 String firebaseUrl = "/some/user/datapath/"; final Firebase firebaseClient = new Firebase(firebaseUrl); // Use runTransaction to bypass cached DataSnapshot firebaseClient.runTransaction(new Transaction.Handler() { @Override public Transaction.Result doTransaction(MutableData mutableData) { // Return passed in data return Transaction.success(mutableData); } @Override public void onComplete(FirebaseError firebaseError, boolean success, DataSnapshot dataSnapshot) { if (firebaseError != null || !success || dataSnapshot == null) { System.out.println("Failed to get DataSnapshot"); } else { System.out.println("Successfully get DataSnapshot"); //handle data here } } }); 

Simplemente agregue este código al método onCreate en su clase de aplicación . (Cambiar la referencia de base de datos)

Ejemplo:

 public class MyApplication extendes Application{ @Override public void onCreate() { super.onCreate(); DatabaseReference scoresRef = FirebaseDatabase.getInstance().getReference("scores"); scoresRef.keepSynced(true); } } 

Funciona bien para mí.

Referencia: https://firebase.google.com/docs/database/android/offline-capabilities

Mi solución era llamar Database.database (). IsPersistenceEnabled = true en load up, luego llamar a .keepSynced (true) en cualquier nodo que necesitaba actualizado.

El problema aquí es que si consulta el nodo justo después de .keepSynced (true), es probable que obtenga el caché en lugar de los datos nuevos. Ligeramente cojo pero funcional trabajo alrededor: retrasar su consulta del nodo durante un segundo o así a fin de dar Firebase algún tiempo para obtener los nuevos datos. En su lugar, obtendrá la caché si el usuario está desconectado.

Ah, y si es un nodo que no quieres mantener actualizado en segundo plano para siempre, recuerda llamar .keepSynced (false) cuando hayas terminado.

Intenté ambas soluciones aceptadas y probé la transacción. Solución de transacción es más limpio y más agradable, pero éxito OnComplete se llama sólo cuando db está en línea, por lo que no se puede cargar desde la memoria caché. Pero usted puede abortar la transacción, entonces onComplete se llamará cuando fuera de línea (con datos en caché) aswell.

He creado previamente la función que trabajó solamente si la base de datos consiguió el lomng de la conexión bastante para hacer synch. Fijé el problema añadiendo tiempo de espera. Voy a trabajar en esto y probar si esto funciona. Tal vez en el futuro, cuando tenga tiempo libre, crearé android lib y lo publicaré, pero para entonces es el código 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<T>() data?.let { data.children.forEach { val child = it.getValue(aClass) child?.let { listOfObjects.add(child) } } } callback.invoke(listOfObjects, true) removeListener() } 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) removeListener() } override fun doTransaction(p0: MutableData?): Transaction.Result { return Transaction.success(p0) } } 

Si quieres hacerlo más rápido para fuera de línea (para no esperar estúpidamente con el tiempo de espera cuando obviamente la base de datos no está conectado), a continuación, compruebe si la base de datos está conectado antes de usar la función anterior:

 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"); } }); 
  • Android - Firebase jobdispatcher
  • ¿Qué sucede realmente cuando la persistencia está habilitada en Firebase?
  • Cómo obtener la cadena de base de datos firebase en tiempo real que contienen en clave única
  • Error SERVICE_VERSION_UPDATE_REQUIRED con Firebase Analytics en Android
  • FirebaseUI y Firebase, ¿cuál es la diferencia?
  • Firebase onMessageReceived no llamado cuando la aplicación está en segundo plano
  • Firebase Deep-link abrir juego de la tienda incluso cuando la aplicación está instalada
  • Firebase (FCM) cómo obtener el token
  • Enviar notificación push desde el servidor al dispositivo android en Java
  • Configuración de Proguard para la biblioteca Firebase-UI
  • Android: la aplicación de indexación de aplicaciones de Firebase no se muestra en las sugerencias de autocompletar de google
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.