Sincronizar um Realm em segundo plano - Java SDK
Se você precisar sincronizar dados quando seu aplicativo não estiver em execução, poderá sincronizar realms em um processo de background.
Pré-requisitos
Para começar a usar a sincronização em segundo plano, é necessário adicionar as seguintes dependências ao seu aplicativo Android:
androidx.work:work-runtime, para enfileirar tarefas
androidx.concurrent:concurrent-futures, para retornar os resultados da tarefa de um trabalhador em background
Exemplo
A sincronização em segundo plano requer duas coisas:
lógica de sincronização
uma tarefa agendada que executa periodicamente essa lógica.
Lógica de sincronização
Primeiro, escreva a lógica personalizada que sincroniza seu Realm. Trate essa lógica como uma conexão autônomo com seu backend. Como resultado, você precisará:
inicializar o Realm SDK
autenticar um usuário para abrir o Realm
Você pode usar as credenciais em cache de um usuário se o usuário tiver usado o aplicativo recentemente.
Abra o Realm e use SyncSession.downloadAllServerChanges() e SyncSession.uploadAllLocalChanges() para sincronizar o Realm totalmente com o backend.
Você pode executar essa lógica como um processo de background usando uma subclasse de ListenableWorker. Coloque sua lógica de sincronização no método startWork()
do seu trabalhador:
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" } }
Trabalhador
Para criar um operador que executa periodicamente a sincronização em segundo plano:
Crie um conjunto de restrições que especifique as condições necessárias para o seu funcionário.
Especifique com que frequência seu funcionário deve executar.
Anexe seu operador com o sistema operacional Android. Atribua a ele um identificador exclusivo para que você possa atualizar a tarefa no futuro.
Você pode criar a tarefa de sincronização em segundo plano dentro de uma subclasse Application em seu aplicativo para garantir que a lógica seja executada somente uma vez a cada execução do aplicativo.
Como a sincronização de um Realm usa dados, considere baixar as alterações em segundo plano apenas quando o dispositivo não estiver:
bateria fraca
usando um conjunto de dados com medição
Restrições de uso para descrever o ambiente em que sua sincronização em segundo plano é executada.
O intervalo repetido depende da frequência com que os dados são atualizados no Realm e com que frequência os usuários abrem sua aplicação. Se o Realm for atualizado com frequência ao longo do dia, considere definir um intervalo repetido de 1 a 3 horas. Se o Realm atualizar apenas um pequeno número de vezes por dia, é melhor definir um intervalo de repetição maior e apenas sincronizar em segundo plano uma ou duas vezes por dia.
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 )