sabato 27 luglio 2013

Android: backup e restore di un database sqlite

Per l'applicazione Mie Ricette mi sono trovato a risolvere un problema: dare la possibilità all'utente di eseguire un backup del database delle ricette.
Il database usato nello sviluppo Android è Sqlite, quindi è essenzialmente un file che può essere copiato in una posizione "sicura" come la scheda SD, o comunque un percorso accessibile collegando il telefono al computer.
Nello script quindi sono presenti due funzioni, backupDatabase e restoreDatabase. Il primo l'ho trovato in qualche blog (perdonatemi, non mi ricordo quale). Entrambi utilizzano i FileChannel, ma mentre il cuore del funzionamento del primo si ha nella funzione nativa FileChannel.transferFrom, nel secondo specularmente si ha in FileChannel.transferTo.

package net.stefanobianchini.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import net.stefanobianchini.ricette.R;

import android.app.Activity;
import android.content.res.Resources;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

public class backupdb {

 public static void backupDatabase(String dbPath, String nomeFileBackup,
   Activity act, Resources res) {

  try {
    File currentDB = new File(dbPath);// path del db su telefono
    File backupDB = new File(sd, nomeFileBackup);// file di destinazione
    @SuppressWarnings("resource")
    FileChannel src = new FileInputStream(currentDB)
       .getChannel();// apriamo un filechannel sul db e sul
           // file di destinazione
    @SuppressWarnings("resource")
    FileChannel dst = new FileOutputStream(backupDB)
       .getChannel();
    dst.transferFrom(src, 0, src.size());// trasferiamo il contenuto
    src.close();
     dst.close();
     Toast.makeText(
       act.getBaseContext(),

         res.getString(R.string.msg_db_location) + "\n " +
         backupDB.getAbsolutePath(),
       Toast.LENGTH_LONG).show();

  } catch (IOException e) {
   Toast.makeText(act.getBaseContext(), e.toString(),
     Toast.LENGTH_LONG).show();
  }
 }

 @SuppressWarnings("resource")
 private static void copyFile(File src, File dst) throws IOException {
  FileChannel inChannel = new FileInputStream(src).getChannel();
  FileChannel outChannel = new FileOutputStream(dst).getChannel();
  try {
   inChannel.transferTo(0, inChannel.size(), outChannel);
  } finally {
   if (inChannel != null)
    inChannel.close();
   if (outChannel != null)
    outChannel.close();
  }
 }

 public static void restoreDatabase(String dbPath, String percorsoFileBackup,
   Activity act, Resources res) {

  try {
   File currentDB = new File(dbPath);// path del db su telefono
   File backupDB = new File(percorsoFileBackup);// file di backup sorgente

   copyFile(backupDB, currentDB);

   Toast.makeText(
     act.getBaseContext(),
     String.format(res.getString(R.string.restore_done),
       backupDB.toString()), Toast.LENGTH_LONG).show();

  } catch (IOException e) {
   Toast.makeText(act.getBaseContext(), e.toString(),
     Toast.LENGTH_LONG).show();
  }
 }

}
Ora siamo pronti per eseguire il backup. Da notare che prima di eseguire il backup viene chiuso per sicurezza il db mediante il SQLiteOpenHelper (nel mio caso chiamato db_help);
//Backup del DB sqlite
db_help.close();
backupdb.backupDatabase(db_help.getReadableDatabase().getPath(), "backup_mie_ricette.db", HomeActivity.this, res);

Nessun commento: