Docs Menu
Docs Home
/ /
Sincronizar datos

Restablecimiento del cliente - SDK de Java

Tip

Obtenga más información sobre los restablecimientos de clientes

Para conocer las causas y las estrategias para manejar los restablecimientos de clientes, consulte Sincronización Página de restablecimiento de cliente.

El SDK lee y escribe en un archivo de dominio en el dispositivo. Al usar Atlas Device Sync, este dominio local se sincroniza con el backend de la aplicación. Algunas condiciones pueden impedir que el dominio se sincronice con el backend. En este caso, se produce un error de restablecimiento del cliente.

Este error significa que debe restablecer el archivo de dominio en la aplicación cliente. Los clientes en este estado pueden seguir ejecutándose y guardando datos localmente. Hasta que restablezca el cliente, el dominio no se sincronizará con el backend.

Elija una estrategia de restablecimiento de cliente para gestionar los errores de restablecimiento. Estas estrategias restauran el dominio a un estado sincronizable, pero tienen desventajas:

  • Descartar cambios no sincronizados. Restaurar la sincronización descartando los cambios locales desde la última sincronización. Mantiene los detectores de cambios.

  • Recuperar manualmente cambios no sincronizados: Mueva el dominio no sincronizable y descargue una nueva copia. Invalida los detectores de cambios.

Ambas opciones permiten escribir lógica personalizada para recuperar los cambios locales. Ninguna de las dos opciones puede recuperar los cambios locales automáticamente.

Descartar los cambios no sincronizados es una alternativa menos compleja a la recuperación manual. Sin embargo, esta estrategia no puede gestionar todos los errores de restablecimiento de cliente. Debe mantener un controlador de restablecimiento manual de cliente como alternativa.

Nuevo en la versión 10.10.0.

Descartar cambios no sincronizados es una estrategia de restablecimiento de cliente proporcionada por el SDK. Esta estrategia requiere un código mínimo y realiza un restablecimiento sin cerrar el dominio ni perder notificaciones.

Elimina todos los cambios locales realizados desde la última sincronización exitosa. Esto incluye cualquier dato ya escrito en el dominio, pero aún no sincronizado con el backend de la aplicación. No utilice esta estrategia si su aplicación no puede eliminar los datos no sincronizados.

La opción Descartar cambios no sincronizados no permite gestionar cambios de esquema disruptivos o destructivos. Cuando se producen cambios disruptivos, el SDK recurre al modo de recuperación manual.

Para usar esta estrategia, pase una instancia de DiscardUnsyncedChangesStrategy al método de construcción defaultSyncClientResetStrategy() cuando cree una instancia de su AppSu instancia DiscardUnsyncedChangesStrategy debe implementar los siguientes métodos:

  • onBeforeReset()El SDK llama a este bloque cuando recibe un error de reinicio del cliente desde el backend. Esto ocurre antes de que el SDK ejecute la estrategia de reinicio del cliente.

  • onAfterReset()El SDK llama a este bloque tras ejecutar correctamente esta estrategia. Este bloque proporciona una copia congelada del dominio original. También devuelve una instancia activa del dominio en estado sincronizable.

  • onError()El SDK llama a este método durante un cambio de esquema importante. Su comportamiento es similar al de defaultClientResetStrategy().

El siguiente ejemplo implementa esta estrategia:

String appID = YOUR_APP_ID; // replace this with your App ID
App app = new App(new AppConfiguration.Builder(appID)
.defaultSyncClientResetStrategy(new DiscardUnsyncedChangesStrategy() {
@Override
public void onBeforeReset(Realm realm) {
Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath());
}
@Override
public void onAfterReset(Realm before, Realm after) {
Log.w("EXAMPLE", "Finished client reset for " + before.getPath());
}
@Override
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()
)

Importante

Los cambios importantes en el esquema requieren una actualización del esquema de la aplicación

Después de un cambio de esquema importante:

  • Todos los clientes deben realizar un reinicio del cliente.

  • Debe actualizar los modelos de cliente afectados por el cambio de esquema importante.

La estrategia de descartar cambios no sincronizados no puede gestionar cambios importantes. Debe gestionar manualmente el restablecimiento del cliente en el método onError(). Este ejemplo descarta manualmente los cambios no sincronizados para gestionar el restablecimiento del 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() {
@Override
public void onBeforeReset(Realm realm) {
Log.w("EXAMPLE", "Beginning client reset for " + realm.getPath());
}
@Override
public void onAfterReset(Realm before, Realm after) {
Log.w("EXAMPLE", "Finished client reset for " + before.getPath());
}
@Override
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()
)

Tip

La recuperación manual reemplaza el controlador obsoleto SyncSession.ClientResetHandler. Los clientes que usan el controlador obsoleto pueden actualizar a la recuperación manual sin cambios lógicos.

No recomendamos la recuperación manual del cliente. Requiere:

  • Cantidades sustanciales de código

  • Concesiones de esquema

  • Lógica de resolución de conflictos complejos.

Para obtener más información, consulte la Guía avanzada para la recuperación de datos del restablecimiento manual del cliente.

Puede probar manualmente el manejo de restablecimiento del cliente de su aplicación finalizando y volviendo a habilitar la sincronización del dispositivo.

Al finalizar y reactivar Sync, los clientes que se conectaron previamente con Sync no podrán hacerlo hasta que restablezcan el cliente. Al finalizar Sync, se eliminan los metadatos del servidor que permiten la sincronización. El cliente debe descargar una nueva copia del dominio desde el servidor. El servidor envía un error de restablecimiento de cliente a estos clientes. Por lo tanto, al finalizar Sync, se activa la condición de restablecimiento de cliente.

Para probar el manejo del restablecimiento del cliente:

  1. Escriba datos desde una aplicación cliente y espere a que se sincronicen.

  2. Termina y vuelve a habilitar Device Sync.

  3. Ejecute la aplicación cliente nuevamente. La aplicación debería mostrar un error de restablecimiento de cliente al intentar conectarse al servidor.

Advertencia

Mientras itera sobre la gestión del restablecimiento de clientes en su aplicación cliente, es posible que tenga que finalizar y volver a habilitar la sincronización repetidamente. Esto impide que todos los clientes existentes puedan sincronizar hasta después de completar un restablecimiento. Para evitar esto en producción, pruebe la gestión del restablecimiento de clientes en un entorno de desarrollo.

Volver

Manejar errores de sincronización

En esta página