Cómo crear una copia de seguridad de los pares clave-valor con Android Backup Service

Android Backup Service ofrece el servicio de copia de seguridad y restablecimiento del almacenamiento en la nube para datos de pares clave-valor en tu app para Android. Durante una operación de copia de seguridad de pares clave-valor, esos datos de la app se envían al transporte alternativo del dispositivo. Si el dispositivo usa el transporte alternativo predeterminado de Google, los datos se envían a Android Backup Service y se archivan.

Los datos se limitan a 5 MB por usuario de tu app. No se aplican cargos por almacenar datos de copia de seguridad.

Para obtener una descripción general de las opciones de copia de seguridad de Android y orientación sobre qué datos se deben incluir en una copia de seguridad y restablecimiento, consulta Descripción general de la copia de seguridad de datos.

Cómo implementar copias de seguridad de clave-valor

Para crear una copia de seguridad de los datos de tu app, debes implementar un agente de ese tipo. El administrador de copias de seguridad llama a ese agente tanto durante esa actividad como durante el restablecimiento.

Para implementar un agente de copia de seguridad, debes hacer lo siguiente:

  1. Declara el agente de copia de seguridad en tu archivo de manifiesto con el atributo android:backupAgent.

  2. Define un agente de copia de seguridad mediante una de las siguientes acciones:

    • Extendiendo BackupAgent

      La clase BackupAgent proporciona la interfaz central que tu app usa para comunicarse con el administrador de copias de seguridad. Si extiendes esa clase de forma directa, debes anular onBackup() y onRestore() para procesar las operaciones de copia de seguridad y restablecimiento de tus datos.

    • Extendiendo BackupAgentHelper

      La clase BackupAgentHelper proporciona un wrapper práctico alrededor de la clase BackupAgent, lo que minimiza la cantidad de código que debes escribir. En tu BackupAgentHelper, debes usar uno o más objetos auxiliares, que crean copias de seguridad y restablecen automáticamente ciertos tipos de datos para que no tengas que implementar onBackup() ni onRestore(). A menos que necesites tener el control total de las copias de seguridad de tu app, te recomendamos que uses BackupAgentHelper para procesarlas.

      Actualmente, Android ofrece asistentes de copia de seguridad que las crean y que también realizan restablecimientos de archivos completos desde SharedPreferences y desde el almacenamiento interno.

Cómo declarar el agente de copia de seguridad en el manifiesto

Cuando hayas decidido el nombre de la clase para tu agente de copia de seguridad, decláralo en tu manifiesto con el atributo android:backupAgent en la etiqueta <application>.

Por ejemplo:

<manifest ... >
    ...
    <application android:label="MyApplication"
                 android:backupAgent="MyBackupAgent">
        <meta-data android:name="com.google.android.backup.api_key"
            android:value="unused" />
        <activity ... >
            ...
        </activity>
    </application>
</manifest>

Para ofrecer compatibilidad en dispositivos más antiguos, te recomendamos agregar la clave de API <meta-data> a tu archivo de manifiesto de Android. Android Backup Service ya no requiere una clave del servicio, pero es posible que algunos dispositivos más antiguos busquen una clave cuando se creen copias de seguridad. Configura el objeto android:name en com.google.android.backup.api_key y el objeto android:value en unused.

El atributo android:restoreAnyVersion toma un valor booleano para indicar si quieres restablecer los datos de la app independientemente de su versión actual en comparación con la versión que produjo los datos de copia de seguridad. El valor predeterminado es false. Para obtener más información, consulta Cómo verificar la versión de restablecimiento de datos.

Cómo extender BackupAgentHelper

Debes compilar tu agente de copia de seguridad usando BackupAgentHelper si deseas crear una copia de seguridad de los archivos completos (desde SharedPreferences o desde el almacenamiento interno). Compilar tu agente de copia de seguridad con BackupAgentHelper requiere mucho menos código que extender BackupAgent, ya que no tienes que implementar onBackup() ni onRestore().

Tu implementación de BackupAgentHelper debe usar uno o más asistentes de copia de seguridad. Un asistente de copia de seguridad es un componente especializado que invoca BackupAgentHelper a fin de realizar operaciones de copia de seguridad y restablecimiento para un tipo particular de datos. El framework de Android actualmente proporciona dos asistentes diferentes:

Puedes incluir varios asistentes en tu BackupAgentHelper, pero solo se necesita uno para cada tipo de datos. Es decir, si tienes varios archivos SharedPreferences, solo necesitas un SharedPreferencesBackupHelper.

Para cada asistente que quieras agregar a tu BackupAgentHelper, debes hacer lo siguiente durante tu método onCreate():

  1. Crea una instancia de la clase de asistente que quieras. En el constructor de clases, debes especificar los archivos para los que quieres crear copias de seguridad.
  2. Llama a addHelper() a fin de agregar el asistente a tu BackupAgentHelper.

En las siguientes secciones, se describe cómo crear un agente de copia de seguridad con cada uno de los asistentes disponibles.

Cómo crear copias de seguridad de SharedPreferences

Cuando creas una instancia de SharedPreferencesBackupHelper, debes incluir el nombre de uno o más archivos SharedPreferences.

Por ejemplo, para crear una copia de seguridad de un archivo SharedPreferences llamado user_preferences, un agente completo de copia de seguridad que usa BackupAgentHelper se ve de la siguiente manera:

Kotlin

// The name of the SharedPreferences file
const val PREFS = "user_preferences"

// A key to uniquely identify the set of backup data
const val PREFS_BACKUP_KEY = "prefs"

class MyPrefsBackupAgent : BackupAgentHelper() {
    override fun onCreate() {
        // Allocate a helper and add it to the backup agent
        SharedPreferencesBackupHelper(this, PREFS).also {
            addHelper(PREFS_BACKUP_KEY, it)
        }
    }
}

Java

public class MyPrefsBackupAgent extends BackupAgentHelper {
    // The name of the SharedPreferences file
    static final String PREFS = "user_preferences";

    // A key to uniquely identify the set of backup data
    static final String PREFS_BACKUP_KEY = "prefs";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        SharedPreferencesBackupHelper helper =
                new SharedPreferencesBackupHelper(this, PREFS);
        addHelper(PREFS_BACKUP_KEY, helper);
    }
}

SharedPreferencesBackupHelper incluye todos los códigos necesarios para crear copias de seguridad y restablecer un archivo SharedPreferences.

Cuando el administrador de copias de seguridad llama a onBackup() y onRestore(), BackupAgentHelper llama a tus asistentes para realizar copias de seguridad y restablecimientos de tus archivos específicos.

Cómo crear copias de seguridad de otros archivos

Cuando creas una instancia de FileBackupHelper, debes incluir el nombre de uno o más de los archivos que se guardan en el almacenamiento interno de tu app (según lo que especifica getFilesDir(), que es la misma ubicación en la que openFileOutput() escribe archivos).

Por ejemplo, para crear copias de seguridad de dos archivos llamados scores y stats, un agente de copia de seguridad que usa BackupAgentHelper se ve de la siguiente manera:

Kotlin

// The name of the file
const val TOP_SCORES = "scores"
const val PLAYER_STATS = "stats"
// A key to uniquely identify the set of backup data
const val FILES_BACKUP_KEY = "myfiles"

class MyFileBackupAgent : BackupAgentHelper() {
    override fun onCreate() {
        // Allocate a helper and add it to the backup agent
        FileBackupHelper(this, TOP_SCORES, PLAYER_STATS).also {
            addHelper(FILES_BACKUP_KEY, it)
        }
    }
}

Java

public class MyFileBackupAgent extends BackupAgentHelper {
    // The name of the file
    static final String TOP_SCORES = "scores";
    static final String PLAYER_STATS = "stats";

    // A key to uniquely identify the set of backup data
    static final String FILES_BACKUP_KEY = "myfiles";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        FileBackupHelper helper = new FileBackupHelper(this,
                TOP_SCORES, PLAYER_STATS);
        addHelper(FILES_BACKUP_KEY, helper);
    }
}

FileBackupHelper incluye todo el código necesario para crear una copia de seguridad y restablecer los archivos guardados en el almacenamiento interno de tu app.

Sin embargo, leer y escribir en archivos del almacenamiento interno no es seguro para los subprocesos. Para asegurarte de que tu agente de copia de seguridad no lea ni escriba archivos al mismo tiempo que las actividades, debes usar sentencias sincronizadas cada vez que leas o escribas. Por ejemplo, en cualquier actividad en la que lees y escribes el archivo, debes usar un objeto como el bloqueo intrínseco para las sentencias sincronizadas:

Kotlin

// Object for intrinsic lock
companion object {
    val sDataLock = Any()
}

Java

// Object for intrinsic lock
static final Object sDataLock = new Object();

Luego, crea una sentencia sincronizada con este bloqueo cada vez que leas o escribas los archivos. Por ejemplo, a continuación, hay una sentencia sincronizada para escribir la puntuación más reciente de un juego en un archivo:

Kotlin

try {
    synchronized(MyActivity.sDataLock) {
        val dataFile = File(filesDir, TOP_SCORES)
        RandomAccessFile(dataFile, "rw").apply {
            writeInt(score)
        }
    }
} catch (e: IOException) {
    Log.e(TAG, "Unable to write to file")
}

Java

try {
    synchronized (MyActivity.sDataLock) {
        File dataFile = new File(getFilesDir(), TOP_SCORES);
        RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
        raFile.writeInt(score);
    }
} catch (IOException e) {
    Log.e(TAG, "Unable to write to file");
}

Debes sincronizar tus sentencias de lectura con el mismo bloqueo.

Luego, en tu BackupAgentHelper, debes anular onBackup() y onRestore() para sincronizar las operaciones de copia de seguridad y restablecimiento con el mismo bloqueo intrínseco. En el ejemplo anterior de MyFileBackupAgent, necesitas los siguientes métodos:

Kotlin

@Throws(IOException::class)
override fun onBackup(
        oldState: ParcelFileDescriptor,
        data: BackupDataOutput,
        newState: ParcelFileDescriptor
) {
    // Hold the lock while the FileBackupHelper performs back up
    synchronized(MyActivity.sDataLock) {
        super.onBackup(oldState, data, newState)
    }
}

@Throws(IOException::class)
override fun onRestore(
        data: BackupDataInput,
        appVersionCode: Int,
        newState: ParcelFileDescriptor
) {
    // Hold the lock while the FileBackupHelper restores the file
    synchronized(MyActivity.sDataLock) {
        super.onRestore(data, appVersionCode, newState)
    }
}

Java

@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
          ParcelFileDescriptor newState) throws IOException {
    // Hold the lock while the FileBackupHelper performs back up
    synchronized (MyActivity.sDataLock) {
        super.onBackup(oldState, data, newState);
    }
}

@Override
public void onRestore(BackupDataInput data, int appVersionCode,
        ParcelFileDescriptor newState) throws IOException {
    // Hold the lock while the FileBackupHelper restores the file
    synchronized (MyActivity.sDataLock) {
        super.onRestore(data, appVersionCode, newState);
    }
}

Cómo extender BackupAgent

La mayoría de las apps no deberían extender la clase BackupAgent de forma directa, sino que deberían extender BackupAgentHelper para aprovechar las clases de ayuda integradas que crean copias de seguridad y restablecen tus archivos automáticamente. Sin embargo, puedes extender BackupAgent directamente para que haga lo siguiente:

  • Controla la versión de tu formato de datos. Por ejemplo, si prevés la necesidad de revisar el formato en el que escribes los datos de tu app, puedes crear un agente de copia de seguridad para hacer una verificación cruzada de la versión de tu app durante una operación de restablecimiento y realizar las tareas de compatibilidad necesarias si la versión del dispositivo es diferente de la de los datos de la copia de seguridad. Para obtener más información, consulta Cómo verificar la versión de restablecimiento de datos.
  • Especifica qué datos requieren copia de seguridad. En vez de crear una copia de seguridad de un archivo completo, puedes especificar qué datos requieren esa copia y cómo se restablecerá cada parte en el dispositivo. Eso también puede ayudarte a administrar diferentes versiones, ya que lees y escribes tus datos como entidades únicas en vez de archivos completos.
  • Crea una copia de seguridad de datos en una base de datos. Si tienes una base de datos SQLite que quieres restablecer cuando el usuario reinstala tu app, debes crear un BackupAgent personalizado que lea los datos adecuados durante una operación de copia de seguridad. Luego, crea la tabla e inserta los datos durante la operación de restablecimiento.

Si no necesitas realizar ninguna de las tareas anteriores y quieres crear una copia de seguridad de los archivos desde SharedPreferences o desde el almacenamiento interno, consulta Cómo extender BackupAgentHelper.

Métodos obligatorios

Cuando creas un BackupAgent, debes implementar los siguientes métodos de devolución de llamada:

onBackup()
El administrador de copias de seguridad llama a este método después de que solicitas una copia de seguridad. En este método, lees los datos de tu app desde el dispositivo y pasas los datos que deseas copiar al administrador de copias de seguridad, como se describe a continuación en Cómo crear una copia de seguridad.
onRestore()

El administrador de copias de seguridad llama a este método durante una operación de restablecimiento. Este método entrega tus datos de copia de seguridad, que tu app puede usar para restablecer su estado anterior, como se describe en Cómo realizar un restablecimiento.

El sistema llama a este método para restablecer cualquier dato de copia de seguridad cuando el usuario reinstala tu app, pero esta también puede solicitar un restablecimiento.

Cómo crear una copia de seguridad

Una solicitud de copia de seguridad no genera una llamada inmediata a tu método onBackup(). En cambio, el administrador de copias de seguridad espera el momento adecuado; luego, crea una copia de seguridad para todas las apps que la solicitaron desde que se realizó la última copia. Aquí es donde debes proporcionar los datos de tu app al administrador de copias de seguridad para poder guardarlos en el almacenamiento en la nube.

Solo el administrador de copias de seguridad puede llamar al método onBackup() de tu agente. Cada vez que cambien los datos de tu app y quieras realizar una copia de seguridad, deberás solicitar esa operación llamando a dataChanged(). Para obtener más información, consulta Cómo solicitar una copia de seguridad.

Sugerencia: Mientras desarrollas tu app, puedes iniciar una operación inmediata de copia de seguridad desde el administrador de copias de seguridad mediante la herramienta bmgr.

Cuando el administrador de copias de seguridad llama a tu método onBackup(), pasa tres parámetros:

oldState
Un ParcelFileDescriptor abierto y de solo lectura que apunta al último estado de copia de seguridad proporcionado por tu app. No se trata de los datos de copia de seguridad del almacenamiento en la nube, sino de una representación local de los datos de la copia de seguridad que se creó la última vez que se llamó a onBackup(), como se define en newState, o desde onRestore(). onRestore() se trata en la siguiente sección. Como onBackup() no te permite leer datos de copia de seguridad existentes en el almacenamiento en la nube, puedes usar esa representación local para determinar si estos cambiaron desde que se creó la última copia.
data
Un objeto BackupDataOutput, que usas para entregar tus datos al administrador de copias de seguridad.
newState
Es un ParcelFileDescriptor abierto de lectura/escritura que apunta a un archivo en el que debes escribir una representación de los datos que entregaste a data. Una representación puede ser tan simple como la marca de tiempo de la última modificación en tu archivo. Este objeto se mostrará como oldState la próxima vez que el administrador de copias de seguridad llame a tu método onBackup(). Si no escribes tus datos de copia de seguridad en newState, oldState apuntará a un archivo vacío la próxima vez que el administrador de copias de seguridad llame a onBackup().

Usa estos parámetros a fin de utilizar tu método onBackup() para que realice lo siguiente:

  1. Comprueba si cambiaron tus datos desde la última copia de seguridad. Para ello, compara oldState con los datos actuales. La forma en que lees los datos en oldState depende de cómo los escribiste originalmente en newState (consulta el paso 3). La manera más fácil de registrar el estado de un archivo es con la marca de tiempo de su última modificación. Por ejemplo, así puedes leer y comparar una marca de tiempo de oldState:

    Kotlin

    val instream = FileInputStream(oldState.fileDescriptor)
    val dataInputStream = DataInputStream(instream)
    try {
       // Get the last modified timestamp from the state file and data file
       val stateModified = dataInputStream.readLong()
       val fileModified: Long = dataFile.lastModified()
       if (stateModified != fileModified) {
           // The file has been modified, so do a backup
           // Or the time on the device changed, so be safe and do a backup
       } else {
           // Don't back up because the file hasn't changed
           return
       }
    } catch (e: IOException) {
       // Unable to read state file... be safe and do a backup
    }
    

    Java

    // Get the oldState input stream
    FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
    DataInputStream in = new DataInputStream(instream);
    
    try {
       // Get the last modified timestamp from the state file and data file
       long stateModified = in.readLong();
       long fileModified = dataFile.lastModified();
    
       if (stateModified != fileModified) {
           // The file has been modified, so do a backup
           // Or the time on the device changed, so be safe and do a backup
       } else {
           // Don't back up because the file hasn't changed
           return;
       }
    } catch (IOException e) {
       // Unable to read state file... be safe and do a backup
    }
    

    Si nada ha cambiado y no necesitas crear una copia de seguridad, continúa con el paso 3.

  2. Si se modificaron tus datos en comparación con oldState, escribe los datos actuales en data para crear una copia de seguridad en el almacenamiento en la nube.

    Debes escribir cada fragmento de datos como una "entidad" en BackupDataOutput. Una entidad es un registro de datos binarios plano que se identifica mediante una string de clave única. Por lo tanto, el conjunto de datos para el que creas la copia de seguridad es conceptualmente un conjunto de pares clave-valor.

    Para agregar una entidad a tu conjunto de datos de copia de seguridad, debes hacer lo siguiente:

    1. Llama a writeEntityHeader() pasando una clave de string única para los datos que estás por escribir y el tamaño de esos datos.

    2. Llama a writeEntityData() pasando un búfer de bytes que contenga tus datos y la cantidad de bytes para escribir desde el búfer (que deben coincidir con el tamaño que se pasó a writeEntityHeader()).

    Por ejemplo, el siguiente código aplana algunos datos en un flujo de bytes y lo escribe en una sola entidad:

    Kotlin

    val buffer: ByteArray = ByteArrayOutputStream().run {
       DataOutputStream(this).apply {
           writeInt(playerName)
           writeInt(playerScore)
       }
       toByteArray()
    }
    val len: Int = buffer.size
    data.apply {
       writeEntityHeader(TOPSCORE_BACKUP_KEY, len)
       writeEntityData(buffer, len)
    }
    

    Java

    // Create buffer stream and data output stream for our data
    ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
    DataOutputStream outWriter = new DataOutputStream(bufStream);
    // Write structured data
    outWriter.writeUTF(playerName);
    outWriter.writeInt(playerScore);
    // Send the data to the Backup Manager via the BackupDataOutput
    byte[] buffer = bufStream.toByteArray();
    int len = buffer.length;
    data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len);
    data.writeEntityData(buffer, len);
    

    Realiza esta acción para cada dato que requiera una copia de seguridad. Tú eliges la forma de dividir tus datos en entidades. Incluso tienes la posibilidad de usar solo una entidad.

  3. Ya sea que crees o no una copia de seguridad (en el paso 2), escribe una representación de los datos actuales en newState ParcelFileDescriptor. El administrador de copias de seguridad retiene este objeto a nivel local como una representación de los datos que cuentan con esa copia. Te devuelve esto como oldState la próxima vez que llama a onBackup() para que puedas determinar si se necesita otra copia de seguridad (como se procesó en el paso 1). Si no escribes el estado actual de los datos en este archivo, oldState estará vacío durante la próxima devolución de llamada.

    En el siguiente ejemplo, se guarda una representación de los datos actuales en newState mediante el uso de la marca de tiempo la última modificación del archivo:

    Kotlin

    val modified = dataFile.lastModified()
    FileOutputStream(newState.fileDescriptor).also {
       DataOutputStream(it).apply {
           writeLong(modified)
       }
    }
    

    Java

    FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
    DataOutputStream out = new DataOutputStream(outstream);
    
    long modified = dataFile.lastModified();
    out.writeLong(modified);
    

Cómo realizar un restablecimiento

Cuando sea necesario restablecer los datos de tu app, el administrador de copias de seguridad llamará al método onRestore() del agente de copias de seguridad. Cuando llama a ese método, el administrador de copias de seguridad entrega tus datos para que puedas restablecerlos en el dispositivo.

Solo el administrador de copias de seguridad puede llamar a onRestore(), lo que sucede automáticamente cuando el sistema instala tu app y encuentra datos de copia de seguridad existentes.

Cuando el administrador de copias de seguridad llama a tu método onRestore(), pasa tres parámetros:

data
Un objeto BackupDataInput, que te permite leer tus datos de copia de seguridad.
appVersionCode
Es un número entero que representa el valor del atributo de manifiesto android:versionCode de tu app, igual que cuando se creó una copia de seguridad de estos datos. Puedes usar esta opción para hacer una verificación cruzada de la versión actual de la app y determinar si el formato de datos es compatible. Para descubrir cómo usar esto para procesar diferentes versiones de datos de restablecimiento, consulta la siguiente sección Cómo verificar la versión de los datos de restablecimiento.
newState
Es un ParcelFileDescriptor abierto de lectura/escritura que apunta a un archivo en el que debes escribir el último estado de copia de seguridad que se proporcionó con data. Este objeto se mostrará como oldState la próxima vez que se llame a onBackup(). Recuerda que también debes escribir el mismo objeto newState en la devolución de llamada onBackup(); hacerlo también aquí garantiza que el objeto oldState dado a onBackup() sea válido incluso la primera vez que se llame a onBackup() después de restablecer el dispositivo.

En tu implementación de onRestore(), debes llamar a readNextHeader() en data para iterar a través de todas las entidades del conjunto de datos. Para cada entidad encontrada, haz lo siguiente:

  1. Obtén la clave de entidad con getKey().
  2. Compara la clave de entidad con una lista de valores clave conocidos que debes haber declarado como strings finales estáticas dentro de la clase BackupAgent. Cuando la clave coincida con una de las strings de claves conocidas, escríbela en una sentencia para extraer los datos de la entidad y guardarlos en el dispositivo:

    1. Obtén el tamaño de los datos de la entidad con getDataSize() y crea un array de bytes de ese tamaño.
    2. Llama a readEntityData(), pásale el array de bytes, que es donde irán los datos, y especifica el desplazamiento de inicio y el tamaño que se debe leer.
    3. Ahora tu array de bytes está lleno. Lee los datos y escríbelos en el dispositivo como quieras.
  3. Después de leer y escribir tus datos en el dispositivo, escribe su estado en el parámetro newState de la misma manera que lo hiciste durante onBackup().

Por ejemplo, a continuación se muestra cómo puedes restablecer los datos de los que se realizó una copia de seguridad según el ejemplo de la sección anterior:

Kotlin

@Throws(IOException::class)
override fun onRestore(data: BackupDataInput, appVersionCode: Int,
                       newState: ParcelFileDescriptor) {
    with(data) {
        // There should be only one entity, but the safest
        // way to consume it is using a while loop
        while (readNextHeader()) {
            when(key) {
                TOPSCORE_BACKUP_KEY -> {
                    val dataBuf = ByteArray(dataSize).also {
                        readEntityData(it, 0, dataSize)
                    }
                    ByteArrayInputStream(dataBuf).also {
                        DataInputStream(it).apply {
                            // Read the player name and score from the backup data
                            playerName = readUTF()
                            playerScore = readInt()
                        }
                        // Record the score on the device (to a file or something)
                        recordScore(playerName, playerScore)
                    }
                }
                else -> skipEntityData()
            }
        }
    }

    // Finally, write to the state blob (newState) that describes the restored data
    FileOutputStream(newState.fileDescriptor).also {
        DataOutputStream(it).apply {
            writeUTF(playerName)
            writeInt(mPlayerScore)
        }
    }
}

Java

@Override
public void onRestore(BackupDataInput data, int appVersionCode,
                      ParcelFileDescriptor newState) throws IOException {
    // There should be only one entity, but the safest
    // way to consume it is using a while loop
    while (data.readNextHeader()) {
        String key = data.getKey();
        int dataSize = data.getDataSize();

        // If the key is ours (for saving top score). Note this key was used when
        // we wrote the backup entity header
        if (TOPSCORE_BACKUP_KEY.equals(key)) {
            // Create an input stream for the BackupDataInput
            byte[] dataBuf = new byte[dataSize];
            data.readEntityData(dataBuf, 0, dataSize);
            ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
            DataInputStream in = new DataInputStream(baStream);

            // Read the player name and score from the backup data
            playerName = in.readUTF();
            playerScore = in.readInt();

            // Record the score on the device (to a file or something)
            recordScore(playerName, playerScore);
        } else {
            // We don't know this entity key. Skip it. (Shouldn't happen.)
            data.skipEntityData();
        }
    }

    // Finally, write to the state blob (newState) that describes the restored data
    FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
    DataOutputStream out = new DataOutputStream(outstream);
    out.writeUTF(playerName);
    out.writeInt(mPlayerScore);
}

En este ejemplo, no se usa el parámetro appVersionCode que se pasó a onRestore(). Sin embargo, quizás quieras usarlo si decidiste crear una copia de seguridad cuando la versión del usuario de la app realmente haya retrocedido (por ejemplo, si el usuario pasó de la versión 1.5 a la 1.0). Para obtener más información, consulta la siguiente sección.

Cómo verificar la versión de restablecimiento de datos

Cuando el administrador de copias de seguridad guarda tus datos en el almacenamiento en la nube, incluye automáticamente la versión de tu app, según lo define el atributo android:versionCode del archivo de manifiesto. Antes de que el administrador de copias de seguridad llame a tu agente para restablecer tus datos, este observa la android:versionCode de la app instalada y la compara con el valor registrado en el conjunto de datos de restablecimiento. Si la versión registrada en el conjunto de datos de restablecimiento es más nueva que la versión de la app del dispositivo, entonces el usuario cambió a una versión inferior de su app. En este caso, el administrador de copias de seguridad anulará la operación de restablecimiento de tu app y no llamará al método onRestore(), dado que el conjunto de restablecimiento no se considera significativo para una versión anterior.

Puedes anular ese comportamiento con el atributo android:restoreAnyVersion. Configura este atributo como true para indicar que quieres restablecer la app sin importar la versión del conjunto de restablecimiento. El valor predeterminado es false. Si lo defines como true, el administrador de copias de seguridad, ignorará android:versionCode y llamará a tu método onRestore() en todos los casos. De esta manera, puedes verificar manualmente la diferencia de versión en tu método onRestore() y tomar las medidas necesarias para que los datos sean compatibles si las versiones no coinciden.

A fin de ayudarte a procesar diferentes versiones durante una operación de restablecimiento, el método onRestore() te pasa el código de versión incluido con el conjunto de datos de restablecimiento como el parámetro appVersionCode. Luego, puedes consultar el código de versión actual de la app en el campo PackageInfo.versionCode. Por ejemplo:

Kotlin

val info: PackageInfo? = try {
    packageManager.getPackageInfo(packageName, 0)
} catch (e: PackageManager.NameNotFoundException) {
    null
}

val version: Int = info?.versionCode ?: 0

Java

PackageInfo info;
try {
    String name = getPackageName();
    info = getPackageManager().getPackageInfo(name, 0);
} catch (NameNotFoundException nnfe) {
    info = null;
}

int version;
if (info != null) {
    version = info.versionCode;
}

Después, simplemente compara el version adquirido de PackageInfo con el appVersionCode que se pasó a onRestore().

Cómo solicitar una copia de seguridad

Puedes solicitar una operación de copia de seguridad en cualquier momento llamando a dataChanged(). Este método notifica al administrador de copias de seguridad que quieres hacer una con tu agente. Luego, el administrador de copias de seguridad llamará al método onBackup() de tu agente en el futuro. Por lo general, debes solicitar una copia de seguridad cada vez que cambien tus datos (por ejemplo, cuando el usuario cambia una preferencia de la app que quieres conservar en la copia). Si llamas a dataChanged() varias veces consecutivas, antes de que el administrador de copias de seguridad solicite una a tu agente, este último recibirá una sola llamada a onBackup().

Cómo solicitar un restablecimiento

Durante la vida útil normal de tu app, no es necesario que solicites una operación de restablecimiento. El sistema verifica automáticamente los datos de copia de seguridad y realiza un restablecimiento cuando se instala tu app.

Cómo migrar a la copia de seguridad automática

Puedes hacer la transición de tu app a copias de seguridad de datos completos configurando android:fullBackupOnly como true en el elemento <application> del archivo de manifiesto. Cuando se ejecuta en un dispositivo con Android 5.1 (nivel de API 22) o versiones anteriores, tu app ignora ese valor del manifiesto y continúa creando copias de seguridad de pares clave-valor. Cuando se ejecuta en un dispositivo con Android 6.0 (nivel de API 23) o versiones posteriores, tu app crea copias de seguridad automáticas en vez de copias de seguridad de pares clave-valor.

Privacidad del usuario

En Google, somos muy conscientes de la confianza que los usuarios depositan en nosotros y de nuestra responsabilidad de proteger su privacidad. Google transmite de forma segura los datos de copia de seguridad hacia sus servidores y desde ellos a fin de ofrecer funciones de copia de seguridad y restablecimiento. Google trata esos datos como información personal de acuerdo con su Política de Privacidad.

Además, los usuarios pueden inhabilitar la función de copia de seguridad de datos a través de la configuración de copia de seguridad del sistema Android. Cuando un usuario inhabilita la copia de seguridad, Android Backup Service borra todos los datos de este tipo que se hayan guardado. Un usuario puede volver a habilitar la copia de seguridad del dispositivo, pero Android Backup Service no restablecerá ningún dato borrado con anterioridad.