Android backup / restore: как создать резервную копию внутренней базы данных?

я реализовал BackupAgentHelper используя предоставленные FileBackupHelper для резервного копирования и восстановления собственной базы данных у меня. Это база данных, которую вы обычно используете вместе с ContentProviders и который проживает в /data/data/yourpackage/databases/.

можно подумать, что это обычное дело. Однако в документах не ясно, что делать:http://developer.android.com/guide/topics/data/backup.html. нет BackupHelper специально для этих типовых баз данных. Поэтому я использовал FileBackupHelper, указал на мой .файл БД в "/databases/", введены блокировки вокруг любой операции БД (например,db.insert) в мой ContentProviders и даже пытался создать "/databases/ каталог" перед onRestore() потому что он не существует после установки.

я реализовал аналогичное решение для SharedPreferences успешно в другом приложении в прошлом. Однако, когда я тестирую свою новую реализацию в эмуляторе-2.2, я вижу, что резервное копирование выполняется в LocalTransport из журналов, а также выполняется восстановление (и onRestore() называется). тем не менее, сам файл БД никогда не создается.

обратите внимание, что это все после установки и до первого запуска приложения, после восстановления была выполнена. Кроме того, моя тестовая стратегия была основана на http://developer.android.com/guide/topics/data/backup.html#Testing.

обратите внимание, что я не говорю о какой-то базе данных sqlite, которой я управляю сам, ни о резервном копировании SDcard, собственный сервер или в другом месте.

я видел упоминание в документах о базах данных, рекомендующих использовать пользовательский BackupAgent но это, кажется, не связаны:

однако, вы, возможно, захотите продлить BackupAgent напрямую, если вам нужно: * Резервное копирование данных в базе данных. Если у вас есть база данных SQLite, которую вы хотите восстановить, когда пользователь переустановите приложение, вам нужно чтобы создать пользовательский BackupAgent, который считывает соответствующие данные во время операция резервного копирования, затем создайте таблица и вставить данные во время операция восстановления.

некоторую ясность, пожалуйста.

если мне действительно нужно сделать это самостоятельно до уровня SQL, то меня беспокоят следующие темы:

  • открыть базы данных и транзакции. Я понятия не имею, как закрыть их от такого одноэлементного класса вне рабочего процесса моего приложения.

  • как сообщает пользователь, для которого выполняется резервное копирование и база данных заблокирована. Это может занять много времени, поэтому мне может понадобиться показать индикатор выполнения.

  • как сделать то же самое на восстановление. Как я понимаю, восстановление может произойти только тогда, когда пользователь уже начал использовать приложение (и ввод данных в базу данных). Таким образом, вы не можете предположить, чтобы просто восстановить резервные данные на месте (удаление пустых или старых данных). Вам придется как-то присоединиться к нему, что для любого нетривиального база данных невозможна из-за идентификаторов.

  • Как обновить приложение после восстановления делается без получения пользователь застрял в какой-то-теперь-недостижимой точке.

  • могу ли я быть уверен, что база данных уже обновлена при резервном копировании или восстановлении? В противном случае ожидаемая схема может не совпадать.

6 ответов


более чистым подходом было бы создать пользовательский BackupHelper:

public class DbBackupHelper extends FileBackupHelper {

    public DbBackupHelper(Context ctx, String dbName) {
        super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath());
    }
}

а затем добавьте его в BackupAgentHelper:

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

после пересмотра моего вопроса я смог заставить его работать после просмотра как ConnectBot делает это. Спасибо Кенни и Джеффри!

это на самом деле так же просто, как добавить:

FileBackupHelper hosts = new FileBackupHelper(this,
    "../databases/" + HostDatabase.DB_NAME);
addHelper(HostDatabase.DB_NAME, hosts);

на BackupAgentHelper.

мне не хватало того факта, что вам придется использовать относительный путь с "../databases/".

тем не менее, это отнюдь не идеальное решение. Документы для FileBackupHelper упоминания, например: "FileBackupHelper следует использовать только с небольшими файлами конфигурации, не большими двоичными файлами.", последнее относится к базам данных SQLite.

Я хотел бы получить больше предложений, понимание того, что ожидается от нас (что такое правильное решение), и советы о том, как это может сломаться.


вот еще более чистый способ резервного копирования баз данных в виде файлов. Нет жестко закодированных путей.

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();
   }
}

Примечание: он переопределяет getFilesDir так что FileBackupHelper работает в базах данных dir, а не в файлах dir.

еще один совет: вы также можете использовать databaseList чтобы получить все ваши БД и имена каналов из этого списка (без родительского пути) в FileBackupHelper. Тогда все БД приложения будут сохранены в резервной копии.


используя FileBackupHelper для резервного копирования / восстановления SQLite db вызывает некоторые серьезные вопросы:
1. Что произойдет, если приложение использует курсор, полученный из ContentProvider.query() и backup agent пытается переопределить весь файл?
2. The ссылке хороший пример совершенного (низкий entrophy ;) тестирования. Вы удалите приложение, установите его снова и восстановлении. Однако жизнь может быть жестокой. Взгляните на ссылке. Представим себе сценарий, когда пользователь покупает новое устройство. Поскольку не имеет собственного набора, агент резервного копирования использует набор другого устройства. Приложение установлено, и ваш backupHelper извлекает старый файл со схемой версии db ниже текущей. SQLiteOpenHelper звонки onDowngrade С реализацией по умолчанию:

public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    throw new SQLiteException("Can't downgrade database from version " +
            oldVersion + " to " + newVersion);
}

независимо от того, что делает пользователь, он не может использовать ваше приложение на новом устройстве.

Я бы предложил использовать ContentResolver для получения данных - > сериализация (без _ids) для резервного копирования и десериализации -> вставить данные для восстановления.

Примечание: получение / вставка данных осуществляется через ContentResolver, что позволяет избежать проблем cuncurrency. Сериализация выполняется в вашем backupAgent. Если вы делаете свой собственный курсорсопоставление объектов сериализация элемента может быть так же просто, как реализация Serializable С transient field _id в классе, представляющем вашу сущность.

Я бы также использовал bulk insert т. е. ContentProviderOperation пример и CursorLoader.setUpdateThrottle чтобы приложение не застряло с перезапуском загрузчика при изменении данных во время резервного копирования процесс восстановления.

если вы окажетесь в ситуации понижения, вы можете выбрать либо прервать восстановление данных, либо восстановить и обновить ContentResolver с полями, относящимися к пониженной версии.

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

надеюсь, что это помогает.


начиная с Android M, теперь для приложений доступен API резервного копирования/восстановления полных данных. Этот новый API включает в себя XML-спецификацию в манифесте приложения, которая позволяет разработчику описывать, какие файлы создавать резервные копии прямым семантическим способом: "резервное копирование базы данных под названием" mydata.децибел.'" Этот новый API намного проще в использовании разработчиками - вам не нужно отслеживать различия или явно запрашивать резервный проход, а XML-описание файлов для резервного копирования означает, что вам часто не нужно писать любой код вообще.

(вы can примите участие даже в операции резервного копирования/восстановления полных данных, чтобы получить обратный вызов, когда восстановление происходит, например. Это гибкий путь.)

посмотреть Настройка автоматического резервного копирования для приложений раздел на developer.android.com для описания того, как использовать новый API.


одним из вариантов будет построить его в логике приложения над базой данных. Это на самом деле кричит для такого уровня, я думаю. Не уверен, что вы уже делаете это, но большинство людей (несмотря на Android content manager cursor approach) представят некоторое отображение ORM - либо пользовательский, либо некоторый подход orm-lite. И что я предпочел бы сделать в этом случае:

  1. чтобы убедиться, что ваше приложение работает хорошо, когда приложение / данные добавляются в фон с новыми данными добавлен/удален, а приложение уже началось
  2. чтобы сделать Java - >protobuf или даже просто java сопоставление сериализации и запись собственный BackupHelper для чтения данных из потока и просто добавьте его в база данных....

поэтому в этом случае вместо того, чтобы делать это на уровне БД, сделайте это на уровне приложения.