恢复 SQLite 数据库文件。

6
我正在我的Android应用中实现备份/恢复系统。
自动备份每隔几分钟进行一次。我试图在我的SD卡上从db备份文件中恢复,之后再次卸载和安装我的应用程序。
备份工作正常,但是问题来了:
每当用户重新安装我的应用程序时,都会出现文件未找到异常。但是,如果用户关闭应用程序,然后再次打开它,恢复就可以正常运行。不知何故,在应用程序首次启动时恢复会遇到问题。
恢复必须在第一次启动时发生。
注意:backupExists函数返回true。
@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(backUpExists()){

        restoreDB();
      }
}
     private boolean backUpExists()
  {
    try{
        File sd = Environment.getExternalStorageDirectory();
        if (sd.canRead()){
            String backupDBPath = "myDB";
             File backupedDB = new File(sd, backupDBPath);
             if(backupedDB.exists()){
                 return true;
             }
        }
    } catch(Exception ex) {
         Toast.makeText(getBaseContext(), ex.toString(), Toast.LENGTH_LONG).show();
    }
        return false;
  }
  private void restoreDB()
  {
    try{
        File sd = Environment.getExternalStorageDirectory();
        File data = Environment.getDataDirectory();

        if (sd.canWrite()) {
            String restroredDBPath = "//data//myPackage//databases//myDB";
            String backupDBPath = "myDB";
            File restoredDB = new File(data, restroredDBPath);
            File backupedDB = new File(sd, backupDBPath);
                FileChannel src = new FileInputStream(backupedDB).getChannel();
                FileChannel dst = new FileOutputStream(restoredDB).getChannel();
                dst.transferFrom(src, 0, src.size());
                src.close();
                dst.close();
                Toast.makeText(getBaseContext(), restoredDB.toString(), Toast.LENGTH_LONG).show();

        }
    } catch (Exception e) {

        Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG).show();
    }
  }

回溯

09-09 22:49:50.931: I/Database(23206): sqlite returned: error code = 26, msg = statement aborts at 14: [SELECT COUNT(*) FROM Photos WHERE AlbumId=0] file is encrypted or is not a database
09-09 22:49:50.931: D/AndroidRuntime(23206): Shutting down VM
09-09 22:49:50.931: W/dalvikvm(23206): threadid=1: thread exiting with uncaught exception (group=0x4151c700)
09-09 22:49:50.931: E/AndroidRuntime(23206): FATAL EXCEPTION: main
09-09 22:49:50.931: E/AndroidRuntime(23206): net.sqlcipher.database.SQLiteException: file is encrypted or is not a database
09-09 22:49:50.931: E/AndroidRuntime(23206):    at     net.sqlcipher.database.SQLiteQuery.native_fill_window(Native Method)
09-09 22:49:50.931: E/AndroidRuntime(23206):    at net.sqlcipher.database.SQLiteQuery.fillWindow(SQLiteQuery.java:73)
09-09 22:49:50.931: E/AndroidRuntime(23206):    at net.sqlcipher.database.SQLiteCursor.fillWindow(SQLiteCursor.java:290)
[snipped]

异常具体发生在哪里?移除 try/catch 以获取正确的堆栈跟踪。 - CL.
@CL。是的,这是可能的。我正在使用一个单例DatabaseHelper类,在恢复操作之前已经实例化了。但是,我尝试重新加载数据库库,并将我的数据库实例设置为null,以便重新创建一个新的DatabaseHelper对象。虽然如此,我仍然遇到了相同的异常。 - idish
这是我的DatabaseHelper类的相关代码:http://pastebin.com/rLHV74hi 在恢复之前,我调用getInstance(...)函数,并在恢复后调用setDatabaseHelperNull()函数。 - idish
你不能使用硬编码路径。 - Trikaldarshiii
@Hi-TechKitKatAndroid 我该如何使这条路径独立于设备? - idish
显示剩余3条评论
3个回答

20
请使用这段代码,它可能会对您有所帮助......我已经用这种方式做过同样的事情。 备份
try {
    File sd = Environment.getExternalStorageDirectory();
    File data = Environment.getDataDirectory();

    if (sd.canWrite()) {
        String currentDBPath = "//data/package name/databases/database_name";
        String backupDBPath = "database_name";
        File currentDB = new File(data, currentDBPath);
        File backupDB = new File(sd, backupDBPath);

        if (currentDB.exists()) {
            FileChannel src = new FileInputStream(currentDB).getChannel();
            FileChannel dst = new FileOutputStream(backupDB).getChannel();
            dst.transferFrom(src, 0, src.size());
            src.close();
            dst.close();
            Toast.makeText(getApplicationContext(), "Backup is successful to SD card", Toast.LENGTH_SHORT).show();
        }
    }
} catch (Exception e) {
}

恢复

try {
    File sd = Environment.getExternalStorageDirectory();
    File data = Environment.getDataDirectory();

    if (sd.canWrite()) {
    String currentDBPath = "//data/package name/databases/database_name";
        String backupDBPath = "database_name";
        File currentDB = new File(data, currentDBPath);
        File backupDB = new File(sd, backupDBPath);

        if (currentDB.exists()) {
            FileChannel src = new FileInputStream(backupDB).getChannel();
            FileChannel dst = new FileOutputStream(currentDB).getChannel();
            dst.transferFrom(src, 0, src.size());
            src.close();
            dst.close();
            Toast.makeText(getApplicationContext(), "Database Restored successfully", Toast.LENGTH_SHORT).show();
        }
    }
} catch (Exception e) {
}

在if条件语句中,您可以在filechannel中看到一点不同。


你的回答没有解决我的问题,但它让我再次查看了恢复操作之前实际发生的情况。我发现我正在使用新的DB函数,所以这导致了所有的问题。谢谢。 - idish
1
有时候它无法复制内部存储中的数据库文件内容。如果我从设备中提取数据库,它会显示为空白。有时候又可以。无法识别问题所在。 - Patriotic
@爱国者 可能是因为我在将近5年前发布了这篇帖子。之后,谷歌为数据安全做出了许多更改。这可能是导致此类行为问题的原因。 - Balvinder Singh

3
try
{
    String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/CALC/Backup";
    //final 
    String inFileName = path+"/Calc_backup :"+date;
    File dbFile = new File(inFileName);
    FileInputStream fis = new FileInputStream(dbFile);


    /*File dir = new File(path);
    if(!dir.exists())
        dir.mkdirs();*/
    //Toast.makeText(getActivity(), "directory created @"+dir.getPath(), 2).show();

    // String outFileName = Environment.getExternalStorageDirectory()+"Calac/hems.txt";

    //String outFileName = path+"/"+date;
    String outFileName = "/data/data/com.special.ResideMenuDemo/databases/Calq";

    // Open the empty db as the output stream
    OutputStream output = new FileOutputStream(outFileName);

    // Transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length;

    while ((length = fis.read(buffer))>0)
    {
        output.write(buffer, 0, length);
    }

    Toast.makeText(getActivity(), "Restore Successfully", 2).show();
    // Close the streams
    output.flush();
    output.close();
    fis.close();

}
catch(Exception e)
{
    e.printStackTrace();
}

0

我感觉问题在于读取已经存在的数据库文件。实际上你并没有在读取,每次应用程序安装时代码都会创建一个新的。你必须提供要读取的数据库文件的确切名称才能在你的getExternalEvironment函数中读取它。


嗯,问题已经解决了,解决方案与指定的代码无关,而是存在另一个问题。请查看上面答案中的评论,谢谢 :) - idish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接