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

Implementé BackupAgentHelper utilizando BackupAgentHelper proporcionado para hacer una copia de seguridad y restaurar la base de datos nativa que tengo. Esta es la base de datos que normalmente usa junto con ContentProviders y que reside en /data/data/yourpackage/databases/ .

Uno podría pensar 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 hay BackupHelper específicamente para estas bases de datos típicas. Por lo tanto, utilicé FileBackupHelper , lo apunté a mi archivo .db en ” /databases/ “, introduje lockings alrededor de cualquier operación de db (como db.insert ) en mis ContentProviders , e incluso intenté crear el directorio ” /databases/ ” antes onRestore() porque no existe después de la instalación.

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

Tenga en cuenta que esto es todo 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 .

También tenga en cuenta que no estoy hablando de una base de datos sqlite que administro, ni de realizar copias de seguridad en SDcard, servidor propio o en otro lugar.

Vi una mención en los documentos sobre las bases de datos que aconsejaban usar un BackupAgent personalizado, pero no parece estar relacionado:

Sin embargo, es posible que desee extender BackupAgent directamente si necesita: * Copia de seguridad de datos en una base de datos. Si tiene una base de datos SQLite que desea restaurar cuando el usuario reinstala su aplicación, debe crear un BackupAgent personalizado que lea los datos apropiados durante una operación de respaldo, luego cree su tabla e inserte los datos durante una operación de restauración.

Algo de claridad por favor

Si realmente necesito hacerlo yo mismo hasta el nivel de SQL, me preocupan los siguientes temas:

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

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

  • Cómo hacer lo mismo en la restauración. Según tengo entendido, la restauración puede ocurrir justo cuando el usuario ya comenzó a usar la aplicación (y a ingresar datos en la base de datos). Por lo tanto, no puede presumir simplemente restaurar los datos respaldados 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 un punto ahora inalcanzable.

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

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 agréguelo a BackupAgentHelper :

 public void onCreate() { addHelper(DATABASE, new DbBackupHelper(this, DB.FILE)); } 

Después de revisar mi pregunta, pude hacer que funcionara 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í, esta no es una solución perfecta. Los documentos para FileBackupHelper mencionan, por ejemplo: ” FileBackupHelper debe usarse solo con pequeños archivos de configuración, no grandes archivos binarios “, siendo este último el caso de las bases de datos SQLite.

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

Aquí hay una manera más limpia de respaldar las bases de datos como archivos. Sin caminos codificados

 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: anula getFilesDir para que FileBackupHelper funcione en las bases de datos dir, no en los archivos dir.

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

Utilizar FileBackupHelper para hacer una copia de seguridad / 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 enlace es un buen ejemplo de prueba perfecta (baja entropía;). Desinstala la aplicación, instálela de nuevo y la copia de seguridad se restaura. Sin embargo, la vida puede ser brutal. Eche un vistazo al enlace . Imaginemos el escenario cuando un usuario compra un dispositivo nuevo. Como no tiene su propio conjunto, el agente de copia de seguridad usa el conjunto de otros dispositivos. La aplicación está instalada y su backupHelper recupera archivos viejos con un esquema de versión db menor que el actual. SQLiteOpenHelper invoca 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, él / ella no puede usar su aplicación en el nuevo dispositivo.

Sugeriría usar ContentResolver para obtener datos -> serializar (sin _id s) para hacer una copia de seguridad y deserializar -> insertar datos para restaurar.

Nota: los datos get / insert se hacen a través de ContentResolver evitando así problemas de cuncurrency. La serialización se realiza en su BackupAgent. Si realiza su propio mapeo de cursor <->, serializar un elemento puede ser tan simple como implementar Serializable con el campo transient _id en la clase que representa su entidad.

También utilizaría la inserción masiva, es decir, el ejemplo de ContentProviderOperation y CursorLoader.setUpdateThrottle para que la aplicación no se CursorLoader.setUpdateThrottle atascada al reiniciar el cargador en el cambio de datos durante el proceso de restauración de la copia de seguridad.

Si se encuentra en una situación de baja, puede optar por 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 aún quedan algunas preguntas como el tamaño de los datos masivos, etc.

Espero que esto ayude.

A partir de Android M, ahora hay una API de copia de seguridad / restauración de datos completa 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 respaldar 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 es necesario realizar un seguimiento de los diffs o solicitar un pase de respaldo explícitamente, y la descripción XML de los archivos que se deben respaldar significa que a menudo no es necesario escribir ningún código en absoluto.

(Puede involucrarse incluso en una operación de copia de seguridad / restauración de datos completos para obtener una callback cuando ocurre la restauración, por ejemplo. Es flexible de esa manera).

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

Una opción será construirlo en la lógica de la aplicación sobre la base de datos. En realidad grita por tal nivel, creo. No estoy seguro de si ya lo está haciendo, pero la mayoría de la gente (a pesar del enfoque del cursor del administrador de contenido de Android) presentará algunos mapas de ORM, ya sea de forma personalizada o de algún modo orm-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 agregan en segundo plano con nuevos datos agregados / eliminados mientras la aplicación ya se inició
  2. para hacer Java-> protobuf o simplemente mapeo de serialización java y escribir su propio BackupHelper para leer los datos de la secuencia y simplemente agregarlo a la base de datos ….

Entonces, en este caso, en lugar de hacerlo en el nivel de DB, hágalo en el nivel de la aplicación.