Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

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 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.

La cantidad de datos se limita a 5 MB por usuario de tu app, y no existe ningún cargo 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.

Nota: Para la mayoría de las apps, se debe usar la copia de seguridad automática a fin de implementar la operación de copia de seguridad y restablecimiento. Tu app solo puede implementar copias de seguridad automáticas o copias de seguridad de clave-valor, no ambas. Las primeras no requieren código y crean una copia de seguridad de archivos enteros, mientras que las segundas requieren que escribas un código para definir de forma explícita tu contenido de copia de seguridad en la forma de pares clave-valor.

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. Registra tu app en Android Backup Service.
  3. Define un agente de copia de seguridad mediante una de las siguientes opciones:
    1. Cómo extender BackupAgent

      La clase BackupAgent proporciona la interfaz central mediante la cual tu app se comunica 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.

    2. o

    3. Cómo extender 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

Este es el paso más fácil; por lo tanto, 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">
        <activity ... >
            ...
        </activity>
    </application>
</manifest>

Otro atributo que recomendamos usar es android:restoreAnyVersion, que toma un valor booleano para indicar si quieres restablecer los datos de 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 registrarse para usar Android Backup Service

Google ofrece un transporte alternativo mediante Android Backup Service para la mayoría de los dispositivos que ejecutan Android 2.2 o versiones posteriores.

Para que tu app realice una copia de seguridad con Android Backup Service, debes registrarla en el servicio a fin de recibir una clave del servicio de copia de seguridad y declararla en tu manifiesto de Android.

Para obtener la clave del servicio de copia de seguridad, regístrate en Android Backup Service. Cuando te registras, recibes una clave del servicio de copia de seguridad y el código XML <meta-data> apropiado para tu archivo de manifiesto de Android, que debes incluir como elemento secundario de <application>. Por ejemplo:

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

android:name debe ser "com.google.android.backup.api_key", y android:value debe ser la clave del servicio de copia de seguridad recibida por el registro de Android Backup Service.

Si tienes varias apps, debes registrar cada una con el nombre del paquete correspondiente.

Nota: No se garantiza que el transporte alternativo que proporciona Android Backup Service esté disponible en todos los dispositivos Android que admitan copias de seguridad. Algunos dispositivos admiten copias de seguridad con un transporte diferente. Es posible que algunos dispositivos no admitan la operación de copia de seguridad en absoluto, y no existe forma de que tu app sepa qué tipo de transporte se usa en el dispositivo. Sin embargo, si implementas una copia de seguridad para tu app, siempre debes incluir una clave de ese servicio para Android Backup Service a fin de que tu app pueda realizarla cuando el dispositivo use el transporte de Android Backup Service. Si el dispositivo no usa Android Backup Service, se ignora el elemento <meta-data> con la clave del servicio de copia de seguridad.

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ódigos 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 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);
    }
}

Se ve así. 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.

Nota: Los métodos de SharedPreferences son seguros para los subprocesos, por lo que puedes leer y escribir con seguridad el archivo de preferencias compartidas de tu agente de copia de seguridad y otras actividades.

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 instrucciones 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 instrucciones 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 instrucción sincronizada con este bloqueo cada vez que leas o escribas los archivos. Por ejemplo, a continuación, hay una instrucción 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 instrucciones 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 backup
    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 backup
    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);
    }
}

Eso es todo lo que necesitas a fin de crear una copia de seguridad de los datos y restablecerlos con BackupAgentHelper.

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, te recomendamos que extiendas BackupAgent de forma directa si necesitas hacerlo:

  • 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 a los efectos de 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.
  • 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. El método entrega tus datos de copia de seguridad, que tu app puede usar para restablecer su estado anterior, como se describe a continuación 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

Cuando llega el momento de crear una copia de seguridad de los datos de tu app, el administrador de copias de seguridad llama al método onBackup(). Aquí es donde debes proporcionar los datos de tu app al administrador de copias de seguridad a fin de 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() (consulta Cómo solicitar una copia de seguridad para obtener más información). 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 aplicaciones que la han solicitado desde que se realizó la última.

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
Es 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, a continuación, o desde onRestore(); en la siguiente sección se brinda más informació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.
data
Es 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 última marca de tiempo que modificaste 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().

Con estos parámetros, debes implementar tu método onBackup() para hacer 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 su última marca de tiempo modificada. 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 los 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 (y 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 tiene 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 última marca de tiempo modificada 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);
    
    

Precaución: Si se guardan los datos de tu app en un archivo, asegúrate de usar instrucciones sincronizadas cuando accedas a él, de forma que tu agente de copia de seguridad no lo lea mientras una actividad de tu app también lo esté escribiendo.

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.

Nota: Mientras desarrollas tu app, también puedes solicitar una operación de restablecimiento con la herramienta bmgr.

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

data
Es un 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 es compatible el formato de datos. 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. Ete 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 instrucción 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 arreglo de bytes de ese tamaño.
    2. Llama a readEntityData(), pásale el arreglo de bytes, que es donde irán los datos, y especifica el desplazamiento de inicio y el tamaño que se debe leer.
    3. Tu arreglo de bytes ahora está lleno, y puedes leer los datos y escribirlos 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 se 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 has elegido 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 sección Cómo verificar la versión de restablecimiento de datos.

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 lo 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. Este atributo es "true" o "false", e indica si quieres restablecer la app independientemente de la versión del conjunto de restablecimiento. El valor predeterminado es "false". Si defines esto 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 entran en conflicto.

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().

Precaución: Asegúrate de comprender las consecuencias de configurar android:restoreAnyVersion como "true" para tu app. Si cada versión de tu app que admite la copia de seguridad no tiene en cuenta de forma adecuada las variaciones del formato de tus datos durante onRestore(), posiblemente se guarden los datos del dispositivo en un formato incompatible con la versión instalada.

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. En consecuencia, el administrador de copias de seguridad llamará al método onBackup() de tu agente en el momento oportuno 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().

Nota: Mientras desarrollas tu app, puedes solicitar una copia de seguridad e iniciarla de forma inmediata con la herramienta bmgr.

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.

Nota: Mientras desarrollas tu app, puedes solicitar una operación de restablecimiento con la herramienta bmgr.

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.