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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Solutions Collecting From Web of "Резервное копирование / восстановление Android: как создать резервную копию внутренней базы данных?"

Более чистым подходом было бы создание пользовательского 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. Тогда все DB приложения будут сохранены в резервной копии.

Использование FileBackupHelper для резервного копирования / восстановления sqlite db вызывает ряд серьезных вопросов:
1. Что произойдет, если приложение использует курсор, полученный из ContentProvider.query() и агент резервного копирования пытается переопределить весь файл?
2. Ссылка является хорошим примером идеального тестирования (низкая энтрофия;). Вы удаляете приложение, устанавливаете его снова и восстанавливаете резервную копию. Однако жизнь может быть жестокой. Взгляните на ссылку . Давайте представим сценарий, когда пользователь покупает новое устройство. Поскольку он не имеет собственного набора, агент резервного копирования использует другой набор устройств. Приложение установлено, и ваш 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 для получения данных -> serialize (без _id s) для резервного копирования и десериализации -> вставить данные для восстановления.

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

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

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

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

Надеюсь это поможет.

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

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

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

Один из вариантов – построить его в логике приложения над базой данных. Думаю, на самом деле он кричит о таком левелле. Не уверен, что вы делаете это уже, но большинство людей (несмотря на подход курсора управления контентом android) представит некоторое ORM-отображение – как пользовательский, так и какой-либо подход orm-lite. И в этом случае я бы предпочел:

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

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