Si necesita sincronizar datos cuando su aplicación no se está ejecutando, puede sincronizar reinos en un proceso en segundo plano.
Requisitos previos
Para comenzar con la sincronización en segundo plano, debe agregar las siguientes dependencias a su aplicación de Android:
androidx.work:tiempo de ejecución del trabajo, para poner en cola trabajos
androidx.concurrent:concurrent-futures, para devolver los resultados del trabajo de un trabajador en segundo plano
Ejemplo
La sincronización en segundo plano requiere dos cosas:
lógica de sincronización
un trabajo programado que ejecuta periódicamente esa lógica.
Lógica de sincronización
Primero, escribe la lógica personalizada que sincroniza tu dominio. Considera esta lógica como una conexión independiente con tu backend. Por lo tanto, necesitarás:
inicializar el SDK de Realm
autenticar a un usuario para abrir el reino
Puede utilizar las credenciales almacenadas en caché de un usuario si el usuario utilizó la aplicación recientemente.
Abra el reino, luego use SyncSession.downloadAllServerChanges() y SyncSession.uploadAllLocalChanges() para sincronizar completamente el reino con el backend.
Puedes ejecutar esta lógica como un proceso en segundo plano usando una subclase de ListenableWorker. Coloca tu lógica de sincronización en el startWork() método de su trabajador:
import android.annotation.SuppressLint; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; import androidx.concurrent.futures.ResolvableFuture; import androidx.work.ListenableWorker; import androidx.work.WorkerParameters; import com.google.common.util.concurrent.ListenableFuture; import java.util.concurrent.TimeUnit; import io.realm.Realm; import io.realm.mongodb.App; import io.realm.mongodb.AppConfiguration; import io.realm.mongodb.Credentials; import io.realm.mongodb.User; import io.realm.mongodb.sync.SyncConfiguration; public class RealmBackgroundWorker extends ListenableWorker { static final String UNIQUE_WORK_NAME = "RealmBackgroundWorker"; private ResolvableFuture<Result> future; public RealmBackgroundWorker( Context context, WorkerParameters workerParams) { super(context, workerParams); } public ListenableFuture<Result> startWork() { future = ResolvableFuture.create(); Realm.init(this.getApplicationContext()); String appID = YOUR_APP_ID; // replace this with your App ID App app = new App(new AppConfiguration.Builder(appID).build()); Credentials credentials = Credentials.anonymous(); app.loginAsync(credentials, it -> { if (it.isSuccess()) { Log.v("EXAMPLE", "Successfully authenticated."); User user = app.currentUser(); SyncConfiguration config = new SyncConfiguration.Builder(user, "PARTITION") .build(); Realm.getInstanceAsync(config, new Realm.Callback() { public void onSuccess(Realm realm) { Log.v("EXAMPLE", "Successfully opened a realm for background synchronization."); try { app.getSync().getSession(config).downloadAllServerChanges(); app.getSync().getSession(config).uploadAllLocalChanges(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } else { Log.e("EXAMPLE", "Failed login: " + it.getError().getErrorMessage()); } }); return future; } }
import android.annotation.SuppressLint import android.content.Context import android.util.Log import androidx.concurrent.futures.ResolvableFuture import androidx.work.ListenableWorker import androidx.work.WorkerParameters import com.google.common.util.concurrent.ListenableFuture import io.realm.Realm import io.realm.mongodb.App import io.realm.mongodb.AppConfiguration import io.realm.mongodb.Credentials import io.realm.mongodb.User import io.realm.mongodb.sync.SyncConfiguration import java.util.concurrent.TimeUnit class RealmBackgroundWorker(context: Context, workerParams: WorkerParameters) : ListenableWorker(context, workerParams) { private lateinit var future: ResolvableFuture<Result> override fun startWork(): ListenableFuture<Result> { future = ResolvableFuture.create() Realm.init(this.applicationContext) val appID = YOUR_APP_ID // replace this with your App ID val app = App(AppConfiguration.Builder(appID).build()) val credentials = Credentials.anonymous() app.loginAsync(credentials) { it: App.Result<User?> -> if (it.isSuccess) { Log.v("EXAMPLE", "Successfully authenticated.") val user = app.currentUser() val config = SyncConfiguration.Builder(user, "PARTITION") .build() Realm.getInstanceAsync(config, object : Realm.Callback() { override fun onSuccess(realm: Realm) { Log.v("EXAMPLE", "Successfully opened a realm for background synchronization.") try { app.sync.getSession(config).downloadAllServerChanges() app.sync.getSession(config).uploadAllLocalChanges() } catch (e: InterruptedException) { e.printStackTrace() } } }) } else { Log.e("EXAMPLE", "Failed login: " + it.error.errorMessage) } } return future } companion object { const val UNIQUE_WORK_NAME = "RealmBackgroundWorker" } }
Obrero
Para crear un trabajador que realice periódicamente una sincronización en segundo plano:
Crea un conjunto de restricciones que especifique las condiciones necesarias para tu trabajador.
Especifique con qué frecuencia debe ejecutarse su trabajador.
Ponga en cola a su trabajador con el sistema operativo Android. Asígnele un identificador único para poder actualizar el trabajo en el futuro.
Puede crear el trabajo de sincronización en segundo plano dentro de una subclase de aplicación en su aplicación para garantizar que la lógica solo se ejecute una vez cada vez que se ejecuta su aplicación.
Dado que la sincronización de un reino utiliza datos, debes considerar descargar los cambios en segundo plano únicamente cuando el dispositivo no esté:
poca batería
utilizando una fuente de datos medida
Utilice restricciones para describir el entorno donde se ejecuta la sincronización en segundo plano.
El intervalo de repetición depende de la frecuencia con la que se actualizan los datos en el dominio y de la frecuencia con la que los usuarios abren la aplicación. Si el dominio se actualiza con frecuencia a lo largo del día, considere establecer un intervalo de repetición de 1a3 horas. Si el dominio solo se actualiza pocas veces al día, es recomendable establecer un intervalo de repetición mayor y sincronizar en segundo plano solo una o dos veces al día.
Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build(); PeriodicWorkRequest backgroundRealmSync = new PeriodicWorkRequest .Builder(RealmBackgroundWorker.class, // repeat every 12 hours 12, TimeUnit.HOURS, // execute job at any point during that 12 hour period 12, TimeUnit.HOURS) .setConstraints(constraints) .build(); // enqueue the work job, replacing it with the most recent version if we update it WorkManager.getInstance(this).enqueueUniquePeriodicWork( RealmBackgroundWorker.UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.REPLACE, backgroundRealmSync);
val constraints: Constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build() val backgroundRealmSync: PeriodicWorkRequest = PeriodicWorkRequest.Builder( RealmBackgroundWorker::class.java, // repeat every 12 hours 12, TimeUnit.HOURS, // execute job at any point during that 12 hour period 12, TimeUnit.HOURS ) .setConstraints(constraints) .build() // enqueue the work job, replacing it with the most recent version if we update it WorkManager.getInstance(this).enqueueUniquePeriodicWork( RealmBackgroundWorker.UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.REPLACE, backgroundRealmSync )