Debe de haber muchas formas distintas de exportar e importar una base de datos de una aplicación android, pero yo voy a ir a la opción fácil: copiar el fichero de la base de datos en una tarjeta SD (exportación) y volver a copiarlo desde esa tarjeta a la aplicación (importación). Ojo, cuando se realice este proceso, se sobreescriben los ficheros que tengamos, así que al importar, se perderá la BD que tengamos en nuestro móvil.
Este proceso está pensado para cuando cambiamos de móvil y queremos tener en nuestra aplicación del móvil nuevo, los datos que ya teníamos en el antiguo. Lo que hacemos es un backup de la BD en la tarjeta SD. Luego llevamos esa tarjeta al nuevo móvil e importamos la BD a la aplicación.
Supongamos que tenemos una aplicación que tiene una BD llamada Personas.db. De esta es de la que vamos a hacer el backup.
Pasos previos: puesto que lo que vamos a hacer es copiar ficheros en la tarjeta SD, y luego leerlos de ahí, necesitamos que nuestra aplicación tenga los permisos necesarios para ello. Así que, en el Manifest, hay que añadir los dos permisos siguientes (lectura y escritura en la tarjeta externa):
<manifest...>
...
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
Con esto, cuando vayamos a escribir a la tarjeta SD o a leer de ella por primera vez, el móvil le pedirá permiso al usuario para acceder a la tarjeta. Si el usuario se lo da (se supone que sí), podremos acceder a la SD.
Necesitaremos, en nuestra aplicación, un botón que llame a la función de exportar la BD. Esta función hará lo siguiente:
// Botón de exportar la BD
public void onExportar(View view) {
try {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
/* Comprobamos el estado de la tarjeta SD. Esto es solo a nivel
* desarrollo. Si hay algún problema, como que la tarjeta sea solo de
* lectura, o no esté, habrá que tomar las medidas oportunas. En este
* ejemplo se supone que la tarjeta está y permite leer y escribir, de
* manera que esto se pone solo como información al desarrollador que
* le aparecerá en el logcat.
*/
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
Log.d("Test", "sdcard mounted and writable");
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.d("Test", "sdcard mounted readonly");
} else {
Log.d("Test", "sdcard state: " + state);
}
// Se comprueba si tenemos permisos para leer/escribir en la tarjeta
boolean permission = isStoragePermissionGranted();
if(permission) {
if (sd.canWrite()) {
String currentDBPath = "//data//" + "com.example.myPackage"
+ "//databases//" + "PersonasDB.db";
String backupDBPath = "PersonasDBBckp.db";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, backupDBPath);
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getBaseContext(), backupDB.toString(),
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(),
"No se puede escribir en SD Card",
Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(),
"No hay permisos para escribir",
Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
.show();
}
}
El núcleo de todo esto es la función transferFrom:
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
Esta función, copia datos desde el fichero fuente (src) hacia el fichero destino (dst), empezando en la posición 0, y copiando tantos bytes como indica el tercer parámetro, por eso se le pasa el tamaño del fichero «src«.
Las sentencias previas, simplemente son para definir estos dos ficheros, el fuente (src) y el destino(dst), indicando su ruta.
String currentDBPath = "//data//" + "com.example.myPackage" +
"//databases//" + "PersonasDB.db";
String backupDBPath = "PersonasDBBckp.db";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, backupDBPath);
Cuando vamos a exportar, el fichero fuente será nuestra BD de la aplicación (currentDB) y el destino, la BD de backup que queremos crear (backupDB). Es decir, vamos a copiar de currentDB –> backupDB.
En la función que hagamos para importar, será al revés, ya que querremos copiar el backup a la BD actual. backupDB –> currentDB
Para la parte de importación también necesitaremos un botón de importar la BD, que llamará a una función que hará lo siguiente:
// Botón de importar la BD
public void onImportar(View view) {
try {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
String currentDBPath= "//data//" + "com.example.myPackage" +
"//databases//" + "PersonasDB.db";
String backupDBPath = "PersonasDBBckp.db";
File backupDB= new File(sd, backupDBPath);
File currentDB = new File(data, currentDBPath);
FileChannel src = new FileInputStream(backupDB).getChannel();
FileChannel dst = new FileOutputStream(currentDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getBaseContext(), backupDB.toString(),
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
.show();
}
}
El procedimiento es el mismo que en la función de exportar. La única diferencia es que en este caso, nuestro fichero fuente (src) es el fichero de backup que habíamos creado (backupDB) y el destino es el fichero actual de nuestra app (currentDB).
FileChannel src = new FileInputStream(backupDB).getChannel();
FileChannel dst = new FileOutputStream(currentDB).getChannel();
En la función de Exportar, estamos llamando a la función isStoragePermissionGranted. En la función de importar no lo he hecho por simplificarla, pero hay que hacerlo, porque si no tiene los permisos, la función fallará. Además, lo ideal es que a la hora de importar solo pregunte por el permiso de LECTURA en la SD, no por el de ESCRITURA, ya que no va a escribir en la SD.
Como decía, esta función comprueba si tenemos permiso para escribir en la tarjeta SD. De no ser así, solicita dichos permisos. IMPORTANTE: cuando los solicite, como la función devolverá un «false«, hay que volver a darle a la opción de exportar, ya que la primera vez no habrá exportado la BD.
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if
(checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v("permisos", "Permission is granted");
return true;
} else {
Log.v("permisos","Permission is revoked");
ActivityCompat.requestPermissions(this, new String[]
{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
} else {
//en versiones sdk<23 ya están los permisos. Se dan al instalarse.
Log.v("permisos","Permission is granted");
return true;
}
}
Una vez que la aplicación ya tiene los permisos, no volverá a preguntar y hará la exportación y la importación sin interrupciones.
Con esto estaría todo hecho. En este ejemplo, tras exportar, en la tarjeta SD habría un fichero Personas.db. Y ese fichero lo podríamos luego importar en nuestra aplicación, sobreescribiendo la bd que ya tengamos (cuidado con esto no vayamos a perder datos sin querer).