Copia de seguridad/restauración de Android: ¿cómo hacer una copia de seguridad de una base de datos interna?


He implementado un BackupAgentHelper usando el FileBackupHelper proporcionado para respaldar y restaurar la base de datos nativa que tengo. Esta es la base de datos que normalmente utiliza junto con ContentProviders y que reside en /data/data/yourpackage/databases/.

Uno pensaría que este es un caso común. Sin embargo, los documentos no tienen claro qué hacer: http://developer.android.com/guide/topics/data/backup.html. No existe BackupHelper específicamente para estas bases de datos típicas. Por lo tanto utilicé el FileBackupHelper, apuntó a mi .archivo db en " /databases/", introdujo bloqueos alrededor de cualquier operación de base de datos (como db.insert) en mi ContentProviders, e incluso intentó crear el directorio "/databases/" antes de onRestore() porque no existe después de la instalación.

He implementado una solución similar para SharedPreferences con éxito en una aplicación diferente en el pasado. Sin embargo, cuando pruebo mi nueva implementación en el emulador-2.2, veo que se está realizando una copia de seguridad a LocalTransport desde los registros, así como una restauración que se está realizando (y onRestore() llamada). Sin embargo, el archivo db en sí nunca es crear.

Tenga en cuenta que todo esto es después de una instalación, y antes del primer lanzamiento de la aplicación, después de que se haya realizado la restauración. Aparte de eso, mi estrategia de prueba se basó en http://developer.android.com/guide/topics/data/backup.html#Testing .

Tenga en cuenta que no estoy hablando de alguna base de datos sqlite que administro yo mismo, ni de hacer copias de seguridad en una tarjeta SD, un servidor propio o en otro lugar.

Vi una mención en los documentos acerca de las bases de datos aconsejando utilizar un costumbre BackupAgent pero no parece relacionado:

Sin embargo, es posible que desee ampliar BackupAgent directamente si usted necesita: * Copia de seguridad de datos en una base de datos. Si tiene una base de datos SQLite que desea restaurar cuando el usuario vuelve a instalar su aplicación, lo que necesita para construir un BackupAgent personalizado que lee los datos apropiados durante un operación de copia de seguridad, a continuación, crear su table and insert the data during a operación de restauración.

Algo de claridad favor.

Si realmente necesito hacerlo yo mismo hasta el nivel SQL, entonces estoy preocupado por los siguientes temas:

  • Abrir bases de datos y transacciones. No tengo idea de cómo cerrarlos desde una clase singleton fuera del flujo de trabajo de mi aplicación.

  • Cómo notificar al usuario que una copia de seguridad está en curso y la base de datos está bloqueada. Podría llevar mucho tiempo, así que podría necesitar mostrar una barra de progreso.

  • Cómo hacer lo mismo en restaurar. Según tengo entendido, la restauración puede ocurrir justo cuando el usuario ya ha comenzado a usar la aplicación (e ingresar datos en la base de datos). Por lo tanto, no puede presumir de solo restaurar los datos backupped en su lugar (eliminando los datos vacíos o antiguos). Tendrás que unirte de alguna manera, lo que para cualquier base de datos no trivial es imposible debido a los id.

  • Cómo actualizar la aplicación después de realizar la restauración sin que el usuario se quede atascado en algún - ahora-inalcanzable punto.

  • ¿Puedo estar seguro de que la base de datos ya se ha actualizado en la copia de seguridad o restauración? De lo contrario, el esquema esperado podría no coincidir.

Author: HitOdessit, 2011-03-12

6 answers

Un enfoque más limpio sería crear un BackupHelper personalizado:

public class DbBackupHelper extends FileBackupHelper {

    public DbBackupHelper(Context ctx, String dbName) {
        super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath());
    }
}

Y luego añadirlo a BackupAgentHelper:

public void onCreate() {
    addHelper(DATABASE, new DbBackupHelper(this, DB.FILE));
}
 20
Author: yanchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-12 09:28:46

Después de revisar mi pregunta, pude hacerla funcionar después de ver cómo lo hace ConnectBot. Gracias Kenny y Jeffrey!

En realidad es tan fácil como agregar:

FileBackupHelper hosts = new FileBackupHelper(this,
    "../databases/" + HostDatabase.DB_NAME);
addHelper(HostDatabase.DB_NAME, hosts);

A su BackupAgentHelper.

El punto que me faltaba era el hecho de que tendrías que usar una ruta relativa con "../databases/".

Aún así, esto no es de ninguna manera una solución perfecta. Los documentos de FileBackupHelper mencionan por ejemplo: "FileBackupHelper debe usarse solo con archivos de configuración pequeños, no grandes archivos binarios.", siendo este último el caso con las bases de datos SQLite.

Me gustaría obtener más sugerencias, ideas sobre lo que se espera de nosotros (cuál es la solución adecuada) y consejos sobre cómo esto podría romperse.

 33
Author: pjv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-23 07:18:06

Aquí hay una forma más limpia de hacer copias de seguridad de bases de datos como archivos. No hay rutas codificadas.

class MyBackupAgent extends BackupAgentHelper{
   private static final String DB_NAME = "my_db";

   @Override
   public void onCreate(){
      FileBackupHelper dbs = new FileBackupHelper(this, DB_NAME);
      addHelper("dbs", dbs);
   }

   @Override
   public File getFilesDir(){
      File path = getDatabasePath(DB_NAME);
      return path.getParentFile();
   }
}

Nota: reemplaza getFilesDir para que FileBackupHelper funcione en el directorio de bases de datos, no en el directorio de archivos.

Otra sugerencia: también puede usar databaseList para obtener todos los nombres de sus bases de datos y fuentes de esta lista (sin ruta principal) en FileBackupHelper. Entonces todos los DB de la aplicación se guardarían en la copia de seguridad.

 23
Author: Pointer Null,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-15 11:21:37

Usar FileBackupHelper para respaldar / restaurar sqlite db plantea algunas preguntas serias:
1. ¿Qué sucede si la aplicación utiliza el cursor recuperado de ContentProvider.query() y el agente de copia de seguridad intenta anular todo el archivo?
2. El link es un buen ejemplo de prueba perfecta (baja entrofia;). Desinstala la aplicación, instálela de nuevo y se restaura la copia de seguridad. Sin embargo la vida puede ser brutal. Echa un vistazo a link . Imaginemos el escenario cuando un usuario compra un nuevo dispositivo. Dado que no tiene su propio conjunto, el el agente de copia de seguridad utiliza el conjunto de otros dispositivos. La aplicación está instalada y su backupHelper recupera el archivo antiguo con el esquema de versión de la base de datos más bajo que el actual. SQLiteOpenHelper llama a onDowngrade con la implementación predeterminada:

public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    throw new SQLiteException("Can't downgrade database from version " +
            oldVersion + " to " + newVersion);
}

No importa lo que haga el usuario, no puede usar su aplicación en el nuevo dispositivo.

Sugiero usar ContentResolverpara obtener datos -> serializar (sin _id s) para la copia de seguridad y deserializar -> insertar datos para restaurar.

Nota: obtener/insertar datos se realiza a través de ContentResolver evitando así problemas de moneda corriente. La serialización se realiza en el BackupAgent. Si hace su propio cursorobject mapping serializar un elemento puede ser tan simple como implementar Serializable con transient field _id en la clase que representa su entidad.

También usaría bulk insert i. e.ContentProviderOperation ejemplo y CursorLoader.setUpdateThrottle para que la aplicación no se atasque con el reinicio del cargador en el cambio de datos durante el proceso de restauración de copia de seguridad.

Si sucede que estás en una situación de degradación, puede elegir entre cancelar la restauración de datos o restaurar y actualizar ContentResolver con campos relevantes para la versión degradada.

Estoy de acuerdo en que el tema no es fácil, no está bien explicado en los documentos y todavía quedan algunas preguntas como el tamaño de los datos masivos, etc.

Espero que esto ayude.

 7
Author: Piotr Bazan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-13 12:18:19

A partir de Android M, ahora hay una API de copia de seguridad/restauración de datos completos disponible para las aplicaciones. Esta nueva API incluye una especificación basada en XML en el manifiesto de la aplicación que permite al desarrollador describir qué archivos hacer una copia de seguridad de una manera semántica directa: "copia de seguridad de la base de datos llamada" mydata.db"'. Esta nueva API es mucho más fácil de usar para los desarrolladores: no tiene que realizar un seguimiento de las diferencias o solicitar un pase de copia de seguridad explícitamente, y la descripción XML de qué archivos hacer una copia de seguridad significa que a menudo no necesita escribir cualquier código.

(Usted puede involucrarse incluso en una operación de copia de seguridad/restauración de datos completos para obtener una devolución de llamada cuando se realiza la restauración, por ejemplo. Es flexible de esa manera.)

Consulte la sección Configuración de Copia de seguridad automática para aplicaciones en developer.android.com para una descripción de cómo usar la nueva API.

 3
Author: ctate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-27 04:39:13

Una opción será construirlo en la lógica de la aplicación sobre la base de datos. En realidad grita por tal Levell, creo. No estoy seguro de si ya lo estás haciendo, pero la mayoría de la gente (a pesar del enfoque del cursor del administrador de contenido de Android) introducirá algún mapeo OR, ya sea personalizado o un enfoque lite - lite. Y lo que preferiría hacer en este caso es:

  1. para asegurarse de que su aplicación funciona bien cuando la aplicación/datos se agrega en el fondo con nuevos datos añadido / eliminado mientras el aplicación ya iniciado
  2. para hacer algunos Java - > protobuf o simplemente java serialización mapeo y escribir su BackupHelper propio para leer los datos desde la corriente y simplemente añadirlo a base....

Así que en este caso, en lugar de hacerlo a nivel de bd, hazlo a nivel de aplicación.

 0
Author: Jarek Potiuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-19 20:37:51