Redefinição do cliente - Java SDK
Nesta página
Dica
Consulte também: Saiba mais sobre reinício do cliente
Para saber mais sobre as causas e estratégias para lidar com as redefinições do cliente , consulte a página Sincronizar redefinições docliente .
O SDK lê e grava em um Arquivo de Realm no dispositivo. Quando você utiliza o Atlas Device Sync, este domínio local é sincronizado com o backend do aplicativo. Algumas condições podem fazer com que o Realm não consiga sincronizar com o backend. Quando isso ocorre, você recebe um erro de reinício do cliente.
Este erro significa que você deve redefinir o Arquivo de Realm na aplicação cliente. Clientes neste estado podem continuar executando e salvando dados localmente. Até que você execute o reinício do cliente, o Realm não sincroniza com o backend.
Escolha uma estratégia de reinício do cliente para lidar com erros de reinício do cliente. Essas estratégias restauram o Realm para um estado sincronizável, mas têm desvantagens:
Descartar alterações não sincronizadas. Restaure a sincronização descartando alterações locais desde a última sincronização. Mantém os ouvintes de alterações.
Recuperar manualmente alterações não sincronizadas:. Mova o Realm não sincronizável e baixe uma nova cópia. Invalida os ouvintes de alteração.
Ambas as opções permitem escrever lógica personalizada para recuperar alterações locais. Nenhuma das opções pode recuperar alterações locais para você.
Descartar alterações não sincronizadas é uma alternativa menos complexa à recuperação manual. No entanto, essa estratégia não pode lidar com todos os erros de reinício do cliente. Você deve manter um manipulador de reinício do cliente como um fallback.
Descartar alterações não sincronizadas
Novidade na versão 10.10.0.
Descartar alterações não sincronizadas é uma estratégia de reinício do cliente fornecida pelo SDK. Essa estratégia requer um mínimo de código. Essa estratégia executa um reinício sem fechar o domínio ou perder notificações.
Ele exclui todas as alterações locais feitas desde a última sincronização bem-sucedida. Isso inclui todos os dados já gravados no Realm, mas ainda não sincronizados com o backend da aplicação. Não use essa estratégia se o aplicativo não puder perder dados não sincronizados.
O descarte de alterações não sincronizadas não pode lidar com alterações de esquema significativas ou destrutivas. Quando ocorrem alterações significativas, o SDK retorna ao modo de recuperação manual .
Para utilizar esta estratégia, passe uma instância de DiscardUnsyncedChangesStrategy para o método de construtor defaultSyncClientResetStrategy() quando você instanciar seu App
. Sua instância DiscardUnsyncedChangesStrategy
deve implementar os seguintes métodos:
onBeforeReset()
. O SDK chama esse bloco quando recebe um reinício do cliente do back-end. Isso ocorre antes que o SDK execute a estratégia de reinício do cliente.onAfterReset()
. O SDK chama esse bloco após executar com sucesso essa estratégia. Este bloco fornece uma cópia congelada do Realm original. Ele também retorna uma instância ativa do Realm em um estado sincronizável.onError()
. O SDK chama esse método durante uma alteração de esquema de quebra. Comporta-se de forma semelhante a defaultClientResetStrategy().
O exemplo a seguir implementa essa estratégia:
String appID = YOUR_APP_ID; // replace this with your App ID App app = new App(new AppConfiguration.Builder(appID) .defaultSyncClientResetStrategy(new DiscardUnsyncedChangesStrategy() { public void onBeforeReset(Realm realm) { Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath()); } public void onAfterReset(Realm before, Realm after) { Log.w("EXAMPLE", "Finished client reset for " + before.getPath()); } public void onError(SyncSession session, ClientResetRequiredError error) { Log.e("EXAMPLE", "Couldn't handle the client reset automatically." + " Falling back to manual recovery: " + error.getErrorMessage()); handleManualReset(session.getUser().getApp(), session, error); } }) .build());
val appID: String = YOUR_APP_ID // replace this with your App ID val app = App( AppConfiguration.Builder(appID) .defaultSyncClientResetStrategy(object : DiscardUnsyncedChangesStrategy { override fun onBeforeReset(realm: Realm) { Log.w("EXAMPLE", "Beginning client reset for " + realm.path) } override fun onAfterReset(before: Realm, after: Realm) { Log.w("EXAMPLE", "Finished client reset for " + before.path) } override fun onError(session: SyncSession, error: ClientResetRequiredError) { Log.e( "EXAMPLE", "Couldn't handle the client reset automatically." + " Falling back to manual recovery: " + error.errorMessage ) handleManualReset(session.user.app, session, error) } }) .build() )
Descartar alterações não sincronizadas após interromper as alterações do esquema
Importante
As alterações de esquema de ruptura exigem uma atualização de esquema de aplicativo
Após uma alteração significativa no esquema:
Todos os clientes devem fazer um reinício do cliente.
Você deve atualizar os modelos de cliente afetados pela alteração do esquema de quebra.
A estratégia de descartar alterações não sincronizadas não consegue lidar com alteração interruptiva. Você deve lidar manualmente com o reinício do cliente no método onError()
. Este exemplo descarta manualmente as alterações não sincronizadas para lidar com o reinício do cliente:
String appID = YOUR_APP_ID; // replace this with your App ID App app = null; AtomicReference<App> globalApp = new AtomicReference<>(app); // accessing the app from within the lambda below requires an effectively final object app = new App(new AppConfiguration.Builder(appID) .defaultSyncClientResetStrategy(new DiscardUnsyncedChangesStrategy() { public void onBeforeReset(Realm realm) { Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath()); } public void onAfterReset(Realm before, Realm after) { Log.w("EXAMPLE", "Finished client reset for " + before.getPath()); } public void onError(SyncSession session, ClientResetRequiredError error) { Log.e("EXAMPLE", "Couldn't handle the client reset automatically." + " Falling back to manual client reset execution: " + error.getErrorMessage()); // close all instances of your realm -- this application only uses one globalRealm.close(); try { Log.w("EXAMPLE", "About to execute the client reset."); // execute the client reset, moving the current realm to a backup file error.executeClientReset(); Log.w("EXAMPLE", "Executed the client reset."); } catch (IllegalStateException e) { Log.e("EXAMPLE", "Failed to execute the client reset: " + e.getMessage()); // The client reset can only proceed if there are no open realms. // if execution failed, ask the user to restart the app, and we'll client reset // when we first open the app connection. AlertDialog restartDialog = new AlertDialog.Builder(activity) .setMessage("Sync error. Restart the application to resume sync.") .setTitle("Restart to Continue") .create(); restartDialog.show(); } // open a new instance of the realm. This initializes a new file for the new realm // and downloads the backend state. Do this in a background thread so we can wait // for server changes to fully download. ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> { Realm newRealm = Realm.getInstance(globalConfig); // ensure that the backend state is fully downloaded before proceeding try { globalApp.get().getSync().getSession(globalConfig).downloadAllServerChanges(10000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } Log.w("EXAMPLE", "Downloaded server changes for a fresh instance of the realm."); newRealm.close(); }); // execute the recovery logic on a background thread try { executor.awaitTermination(20000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } }) .build()); globalApp.set(app);
val appID = YOUR_APP_ID // replace this with your App ID var app: App? = null app = App( AppConfiguration.Builder(appID) .defaultSyncClientResetStrategy(object : DiscardUnsyncedChangesStrategy { override fun onBeforeReset(realm: Realm) { Log.w("EXAMPLE", "Beginning client reset for " + realm.path) } override fun onAfterReset(before: Realm, after: Realm) { Log.w("EXAMPLE", "Finished client reset for " + before.path) } override fun onError(session: SyncSession, error: ClientResetRequiredError) { Log.e( "EXAMPLE", "Couldn't handle the client reset automatically." + " Falling back to manual client reset execution: " + error.errorMessage ) // close all instances of your realm -- this application only uses one globalRealm!!.close() try { Log.w("EXAMPLE", "About to execute the client reset.") // execute the client reset, moving the current realm to a backup file error.executeClientReset() Log.w("EXAMPLE", "Executed the client reset.") } catch (e: java.lang.IllegalStateException) { Log.e("EXAMPLE", "Failed to execute the client reset: " + e.message) // The client reset can only proceed if there are no open realms. // if execution failed, ask the user to restart the app, and we'll client reset // when we first open the app connection. val restartDialog = AlertDialog.Builder(activity) .setMessage("Sync error. Restart the application to resume sync.") .setTitle("Restart to Continue") .create() restartDialog.show() } // open a new instance of the realm. This initializes a new file for the new realm // and downloads the backend state. Do this in a background thread so we can wait // for server changes to fully download. val executor = Executors.newSingleThreadExecutor() executor.execute { val newRealm = Realm.getInstance(globalConfig) // ensure that the backend state is fully downloaded before proceeding try { app!!.sync.getSession(globalConfig) .downloadAllServerChanges( 10000, TimeUnit.MILLISECONDS ) } catch (e: InterruptedException) { e.printStackTrace() } Log.w( "EXAMPLE", "Downloaded server changes for a fresh instance of the realm." ) newRealm.close() } // execute the recovery logic on a background thread try { executor.awaitTermination(20000, TimeUnit.MILLISECONDS) } catch (e: InterruptedException) { e.printStackTrace() } } }) .build() )
Recuperar manualmente alterações não sincronizadas
Dica
A recuperação manual substitui o SyncSession.ClientResetHandler
obsoleto. Os clientes que usam o manipulador obsoleto podem atualizar para a recuperação manual sem alterações lógicas.
Não recomendamos a recuperação manual do reinício do cliente. Isso requer:
Quantidades substanciais de código
Concessões de esquema
Lógica complexa de resolução de conflitos.
Para saber mais, consulte o Guia Avançado para Recuperação manual de dados de reinício do cliente.
Tratamento de reinício do cliente de teste
Você pode testar manualmente o tratamento de redefinição do cliente do seu aplicativo encerrando e reativando o Device Sync.
Quando você encerra e reativa a sincronização, os clientes que se conectaram anteriormente à sincronização não conseguem se conectar até depois de fazerem um reinício do cliente. O encerramento da sincronização exclui os metadados do servidor que permitem ao cliente sincronizar. O cliente deve baixar uma nova cópia do Realm do servidor. O servidor envia um erro de reinício do cliente para esses clientes. Então, ao encerrar a sincronização, você trigger a condição de reinício do cliente.
Para testar o tratamento de reinício do cliente:
Escreva dados de um aplicativo cliente e aguarde a sincronização.
Encerre e reative o Realm Mobile Sync.
Run the client app again. O aplicativo deve receber um erro de reinício do cliente ao tentar se conectar ao servidor.
Aviso
Enquanto você itera sobre o tratamento de redefinição do cliente em seu aplicativo cliente, talvez seja necessário encerrar e reativar a sincronização repetidamente. Encerrar e reativar a sincronização impede que todos os clientes existentes sincronizem até depois de concluir a redefinição do cliente. Para evitar isso na produção, teste o tratamento de redefinição do cliente em um ambiente de desenvolvimento.