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


я реализовал 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 82

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

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

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

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 возникают некоторые серьезные вопросы:
1. Что произойдет, если приложение использует курсор, полученный из ContentProvider.query() и агент резервного копирования пытается переопределить весь файл?
2. Элемент ссылке хороший пример совершенного (низкий 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 чтобы получить данные - > сериализовать (без _id s) для резервного копирования и десериализации -> вставить данные для восстановления.

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

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

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

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

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

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

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

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

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

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

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