Восстановление файла базы данных SQLite

Я внедряю систему резервного копирования/восстановления в своем приложении для Android.

Автоматическое резервное копирование происходит каждые пару минут. Я пытаюсь восстановить файл резервной копии базы данных с SD-карты после того, как мое приложение было удалено, а затем снова установлено.

Резервное копирование работает, но вот проблема:
Всякий раз, когда пользователь снова устанавливает мое приложение, возникает исключение "Файл не найден", но если пользователь закрывает приложение, а затем открывает его снова, восстановление проходит нормально. Каким-то образом восстановление сталкивается с проблемой при первом запуске приложения.

Восстановление должно произойти при первом запуске.

Примечание. функция 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]

person idish    schedule 05.09.2013    source источник
comment
И где именно происходит исключение? Удалите try/catch, чтобы получить правильную трассировку стека.   -  person CL.    schedule 09.09.2013
comment
Я получаю, что файл зашифрован или не является исключением базы данных, когда я пытаюсь выполнить запрос с использованием восстановленной БД. Вот полная трассировка стека: pastebin.com/j4tQU26K   -  person idish    schedule 09.09.2013
comment
@КЛ. Забыл отметить :)   -  person idish    schedule 09.09.2013
comment
В каком классе этот код? Возможно ли, что какой-то код уже пытался открыть файл базы данных?   -  person CL.    schedule 10.09.2013
comment
@КЛ. Да, это возможно. Я использую однотонный класс DatabaseHelper, экземпляр которого уже создан перед операцией восстановления. Но я попытался загрузить библиотеки базы данных еще раз, а также установить для экземпляра моей базы данных значение null, чтобы он повторно создал новый объект DatabaseHelper. Хотя я получаю то же исключение.   -  person idish    schedule 10.09.2013
comment
Вот соответствующий код моего класса DatabaseHelper: pastebin.com/rLHV74hi Я вызываю getInstance(...) функционировать до восстановления. и функцию setDatabaseHelperNull() после восстановления.   -  person idish    schedule 10.09.2013
comment
Вы не должны использовать жестко заданный путь   -  person Trikaldarshiii    schedule 14.09.2013
comment
@Hi-TechKitKatAndroid Как сделать этот путь независимым от устройства?   -  person idish    schedule 14.09.2013


Ответы (2)


Пожалуйста, используйте этот код, он может вам помочь... Я сделал то же самое с этим способом

Для резервного копирования

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.

person Balvinder Singh    schedule 10.09.2013
comment
Ваш ответ не решил мою проблему, но заставил меня еще раз посмотреть, что на самом деле происходит перед операцией восстановления. Я узнал, что использую новые функции БД, и это вызвало все проблемы. Спасибо. - person idish; 10.09.2013
comment
Иногда он не копирует содержимое файла базы данных во внутреннюю память. Если я вытаскиваю базу данных с устройства, она показывает пустую. Иногда это так. Невозможно распознать проблему. - person Patriotic; 21.07.2018
comment
@Патриотично, может быть, потому что я делаю этот пост почти 5 лет назад. Так что после этого так много изменений, сделанных Google для безопасности данных. Это может быть проблемой для такого поведения. - person Balvinder Singh; 19.12.2018

Проблема, которую я чувствую, связана с чтением уже существующего файла базы данных. На самом деле вы его не читаете, код просто создает новый каждый раз при установке приложения. Вам нужно будет указать точное имя файла базы данных, на который вы ссылаетесь, чтобы прочитать его в вашей функции getExternalEvironment.

person Glenford Fernandes    schedule 14.09.2013
comment
Эм, проблема уже решена, решение было не по указанному коду, а была другая проблема. Взгляните на комментарий в ответе выше вас, спасибо :) - person idish; 14.09.2013