Служба резервного копирования Android обеспечивает резервное копирование и восстановление данных «ключ-значение» в облачном хранилище вашего приложения Android. Во время операции резервного копирования «ключ-значение» данные резервной копии приложения передаются в резервный транспорт устройства. Если устройство использует транспорт резервного копирования Google по умолчанию, данные передаются в службу резервного копирования Android для архивирования.
Объем данных ограничен 5 МБ на каждого пользователя вашего приложения. За хранение резервных данных плата не взимается.
Обзор возможностей резервного копирования Android и рекомендации о том, какие данные следует выполнять резервное копирование и восстановление, см. в разделе Обзор резервного копирования данных .
Реализация резервного копирования «ключ-значение»
Чтобы создать резервную копию данных вашего приложения, вам необходимо внедрить агент резервного копирования. Ваш агент резервного копирования вызывается диспетчером резервного копирования как во время резервного копирования, так и во время восстановления.
Для реализации агента резервного копирования необходимо:
Объявите свой агент резервного копирования в файле манифеста с атрибутом
android:backupAgent
.Определите агент резервного копирования, выполнив одно из следующих действий:
Класс
BackupAgent
предоставляет центральный интерфейс, который ваше приложение использует для связи с диспетчером резервного копирования. Если вы расширяете этот класс напрямую, вам необходимо переопределитьonBackup()
иonRestore()
для выполнения операций резервного копирования и восстановления ваших данных.Класс
BackupAgentHelper
представляет собой удобную оболочку для классаBackupAgent
, сводя к минимуму объем кода, который необходимо написать. В вашемBackupAgentHelper
вы должны использовать один или несколько вспомогательных объектов, которые автоматически создают резервные копии и восстанавливают определенные типы данных, поэтому вам не нужно реализовыватьonBackup()
иonRestore()
. Если вам не нужен полный контроль над резервными копиями вашего приложения, мы рекомендуем использоватьBackupAgentHelper
для обработки резервных копий вашего приложения.В настоящее время Android предоставляет помощники по резервному копированию, которые позволяют создавать резервные копии и восстанавливать полные файлы из
SharedPreferences
и внутреннего хранилища .
Объявите агент резервного копирования в своем манифесте.
После того, как вы определились с именем класса для своего агента резервного копирования, объявите его в своем манифесте, используя атрибут android:backupAgent
в теге <application>
.
Например:
<manifest ... > ... <application android:label="MyApplication" android:backupAgent="MyBackupAgent"> <meta-data android:name="com.google.android.backup.api_key" android:value="unused" /> <activity ... > ... </activity> </application> </manifest>
Для поддержки старых устройств мы рекомендуем добавить ключ API <meta-data>
в файл манифеста Android. Службе резервного копирования Android больше не требуется служебный ключ, но некоторые старые устройства все равно могут проверять наличие ключа при резервном копировании. Установите android:name
значение com.google.android.backup.api_key
и android:value
unused
.
Атрибут android:restoreAnyVersion
принимает логическое значение, указывающее, хотите ли вы восстановить данные приложения независимо от текущей версии приложения по сравнению с версией, в которой были созданы данные резервной копии. Значение по умолчанию — false
. Дополнительную информацию см. в разделе Проверка версии данных восстановления .
Расширение BackupAgentHelper
Вам следует создать свой агент резервного копирования с помощью BackupAgentHelper
если вы хотите выполнять резервное копирование полных файлов из SharedPreferences
или внутреннего хранилища. Для создания агента резервного копирования с помощью BackupAgentHelper
требуется гораздо меньше кода, чем для расширения BackupAgent
, поскольку вам не нужно реализовывать onBackup()
и onRestore()
.
Ваша реализация BackupAgentHelper
должна использовать один или несколько помощников резервного копирования. Помощник резервного копирования — это специализированный компонент, который вызывает BackupAgentHelper
для выполнения операций резервного копирования и восстановления для определенного типа данных. Платформа Android в настоящее время предоставляет два разных помощника:
-
SharedPreferencesBackupHelper
для резервного копирования файловSharedPreferences
. -
FileBackupHelper
для резервного копирования файлов из внутреннего хранилища.
Вы можете включить несколько помощников в свой BackupAgentHelper
, но для каждого типа данных необходим только один помощник. То есть, если у вас есть несколько файлов SharedPreferences
, вам понадобится только один SharedPreferencesBackupHelper
.
Для каждого помощника, который вы хотите добавить в свой BackupAgentHelper
, вы должны сделать следующее во время метода onCreate()
:
- Создайте экземпляр желаемого вспомогательного класса. В конструкторе класса вы должны указать файлы, резервную копию которых хотите создать.
- Вызовите
addHelper()
чтобы добавить помощника вBackupAgentHelper
.
В следующих разделах описывается, как создать агент резервного копирования, используя каждый из доступных помощников.
Резервное копирование общих настроек
При создании экземпляра SharedPreferencesBackupHelper
необходимо указать имя одного или нескольких файлов SharedPreferences
.
Например, для резервного копирования файла SharedPreferences
с именем user_preferences
полный агент резервного копирования с использованием BackupAgentHelper
выглядит следующим образом:
Котлин
// The name of the SharedPreferences file const val PREFS = "user_preferences" // A key to uniquely identify the set of backup data const val PREFS_BACKUP_KEY = "prefs" class MyPrefsBackupAgent : BackupAgentHelper() { override fun onCreate() { // Allocate a helper and add it to the backup agent SharedPreferencesBackupHelper(this, PREFS).also { addHelper(PREFS_BACKUP_KEY, it) } } }
Ява
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
SharedPreferencesBackupHelper
включает в себя весь код, необходимый для резервного копирования и восстановления файла SharedPreferences
.
Когда диспетчер резервного копирования вызывает onBackup()
и onRestore()
, BackupAgentHelper
вызывает ваши помощники по резервному копированию для резервного копирования и восстановления указанных файлов.
Резервное копирование других файлов
Когда вы создаете экземпляр FileBackupHelper
, вы должны включить имя одного или нескольких файлов, которые сохраняются во внутреннем хранилище вашего приложения, как указано в getFilesDir()
, которое является тем же местом, где openFileOutput()
записывает файлы.
Например, для резервного копирования двух файлов с именами scores
и stats
агент резервного копирования, использующий BackupAgentHelper
выглядит следующим образом:
Котлин
// The name of the file const val TOP_SCORES = "scores" const val PLAYER_STATS = "stats" // A key to uniquely identify the set of backup data const val FILES_BACKUP_KEY = "myfiles" class MyFileBackupAgent : BackupAgentHelper() { override fun onCreate() { // Allocate a helper and add it to the backup agent FileBackupHelper(this, TOP_SCORES, PLAYER_STATS).also { addHelper(FILES_BACKUP_KEY, it) } } }
Ява
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
FileBackupHelper
включает в себя весь код, необходимый для резервного копирования и восстановления файлов, сохраненных во внутренней памяти вашего приложения.
Однако чтение и запись файлов во внутренней памяти не является потокобезопасным . Чтобы гарантировать, что ваш агент резервного копирования не читает и не записывает ваши файлы одновременно с вашими действиями, вы должны использовать синхронизированные операторы каждый раз, когда вы выполняете чтение или запись. Например, в любом действии, где вы читаете и записываете файл, вам нужен объект, который будет использоваться в качестве внутренней блокировки для синхронизированных операторов:
Котлин
// Object for intrinsic lock companion object { val sDataLock = Any() }
Ява
// Object for intrinsic lock static final Object sDataLock = new Object();
Затем создайте синхронизированный оператор с этой блокировкой каждый раз, когда вы читаете или записываете файлы. Например, вот синхронизированный оператор для записи последнего результата в игре в файл:
Котлин
try { synchronized(MyActivity.sDataLock) { val dataFile = File(filesDir, TOP_SCORES) RandomAccessFile(dataFile, "rw").apply { writeInt(score) } } } catch (e: IOException) { Log.e(TAG, "Unable to write to file") }
Ява
try { synchronized (MyActivity.sDataLock) { File dataFile = new File(getFilesDir(), TOP_SCORES); RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw"); raFile.writeInt(score); } } catch (IOException e) { Log.e(TAG, "Unable to write to file"); }
Вам следует синхронизировать операторы чтения с одной и той же блокировкой.
Затем в BackupAgentHelper
вы должны переопределить onBackup()
и onRestore()
чтобы синхронизировать операции резервного копирования и восстановления с одной и той же внутренней блокировкой. Например, приведенному выше примеру MyFileBackupAgent
необходимы следующие методы:
Котлин
@Throws(IOException::class) override fun onBackup( oldState: ParcelFileDescriptor, data: BackupDataOutput, newState: ParcelFileDescriptor ) { // Hold the lock while the FileBackupHelper performs back up synchronized(MyActivity.sDataLock) { super.onBackup(oldState, data, newState) } } @Throws(IOException::class) override fun onRestore( data: BackupDataInput, appVersionCode: Int, newState: ParcelFileDescriptor ) { // Hold the lock while the FileBackupHelper restores the file synchronized(MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState) } }
Ява
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs back up synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
Расширение резервного агента
Большинству приложений не требуется напрямую расширять класс BackupAgent
, вместо этого им следует расширять BackupAgentHelper
чтобы воспользоваться преимуществами встроенных вспомогательных классов, которые автоматически создают резервные копии и восстанавливают ваши файлы. Однако вы можете напрямую расширить BackupAgent
, чтобы выполнять следующие действия:
- Версия вашего формата данных. Например, если вы ожидаете необходимости пересмотреть формат, в котором вы записываете данные своего приложения, вы можете создать агент резервного копирования для перекрестной проверки версии вашего приложения во время операции восстановления и выполнения любых необходимых работ по совместимости, если версия на устройстве отличается от данных резервной копии. Дополнительную информацию см. в разделе Проверка версии данных восстановления .
- Укажите части данных для резервного копирования. Вместо резервного копирования всего файла вы можете указать части данных для резервного копирования и способ последующего восстановления каждой части на устройство. Это также может помочь вам управлять разными версиями, поскольку вы читаете и записываете данные как уникальные объекты, а не как целые файлы.
- Резервное копирование данных в базе данных. Если у вас есть база данных SQLite, которую вы хотите восстановить, когда пользователь переустанавливает ваше приложение, вам необходимо создать собственный
BackupAgent
, который считывает соответствующие данные во время операции резервного копирования, а затем создает таблицу и вставляет данные во время операции восстановления.
Если вам не нужно выполнять какие-либо из вышеперечисленных задач и вы хотите выполнить резервное копирование полных файлов из SharedPreferences
или внутреннего хранилища, см. Расширение BackupAgentHelper
.
Требуемые методы
При создании BackupAgent
необходимо реализовать следующие методы обратного вызова:
-
onBackup()
- Менеджер резервного копирования вызывает этот метод после запроса резервной копии . В этом методе вы считываете данные своего приложения с устройства и передаете данные, резервную копию которых хотите создать, диспетчеру резервного копирования, как описано в разделе «Выполнение резервного копирования» .
-
onRestore()
Диспетчер резервного копирования вызывает этот метод во время операции восстановления. Этот метод предоставляет данные резервной копии, которые ваше приложение может использовать для восстановления прежнего состояния, как описано в разделе «Выполнение восстановления» .
Система вызывает этот метод для восстановления любых данных резервной копии, когда пользователь переустанавливает ваше приложение, но ваше приложение также может запросить восстановление .
Выполните резервное копирование
Запрос на резервное копирование не приводит к немедленному вызову метода onBackup()
. Вместо этого диспетчер резервного копирования ждет подходящего времени, а затем выполняет резервное копирование для всех приложений, которые запросили резервное копирование с момента выполнения последнего резервного копирования. На этом этапе вы должны предоставить данные вашего приложения диспетчеру резервного копирования, чтобы их можно было сохранить в облачном хранилище.
Только диспетчер резервного копирования может вызывать метод onBackup()
вашего агента резервного копирования. Каждый раз, когда данные вашего приложения изменяются и вы хотите выполнить резервное копирование, вы должны запросить операцию резервного копирования, вызвав dataChanged()
. Дополнительную информацию см. в разделе Запрос резервной копии .
Совет . При разработке приложения вы можете инициировать немедленную операцию резервного копирования из диспетчера резервного копирования с помощью инструмента bmgr
.
Когда диспетчер резервного копирования вызывает ваш метод onBackup()
, он передает три параметра:
-
oldState
- Открытый
ParcelFileDescriptor
только для чтения, указывающий на последнее состояние резервной копии, предоставленное вашим приложением. Это не данные резервной копии из облачного хранилища, а локальное представление данных, резервная копия которых была создана при последнем вызовеonBackup()
, как определеноnewState
илиonRestore()
.onRestore()
рассматривается в следующем разделе. ПосколькуonBackup()
не позволяет вам читать существующие данные резервной копии в облачном хранилище, вы можете использовать это локальное представление, чтобы определить, изменились ли ваши данные с момента последнего резервного копирования. -
data
- Объект
BackupDataOutput
, который вы используете для доставки данных резервной копии в диспетчер резервного копирования. -
newState
- Открытый
ParcelFileDescriptor
для чтения и записи, указывающий на файл, в который вы должны записать представление данных, которые вы доставили вdata
. Представление может быть таким же простым, как временная метка последнего изменения вашего файла. Этот объект возвращается какoldState
в следующий раз, когда диспетчер резервного копирования вызывает ваш методonBackup()
. Если вы не записываете данные резервной копии вnewState
, тогдаoldState
будет указывать на пустой файл в следующий раз, когда Backup Manager вызоветonBackup()
.
Используя эти параметры, реализуйте метод onBackup()
чтобы сделать следующее:
Проверьте, изменились ли ваши данные с момента последнего резервного копирования, сравнив
oldState
с текущими данными. То, как вы читаете данные вoldState
зависит от того, как вы изначально записали их вnewState
(см. шаг 3). Самый простой способ записать состояние файла — использовать временную метку последнего изменения. Например, вот как вы можете прочитать и сравнить метку времени изoldState
:Котлин
val instream = FileInputStream(oldState.fileDescriptor) val dataInputStream = DataInputStream(instream) try { // Get the last modified timestamp from the state file and data file val stateModified = dataInputStream.readLong() val fileModified: Long = dataFile.lastModified() if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return } } catch (e: IOException) { // Unable to read state file... be safe and do a backup }
Ява
// Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = dataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
Если ничего не изменилось и резервное копирование не требуется, перейдите к шагу 3.
Если ваши данные изменились по сравнению с
oldState
, запишите текущие данные вdata
чтобы создать их резервную копию в облачном хранилище.Вы должны записать каждый фрагмент данных как объект в
BackupDataOutput
. Сущность — это плоская запись двоичных данных, идентифицируемая уникальной ключевой строкой. Таким образом, набор данных, для которого выполняется резервное копирование, концептуально представляет собой набор пар ключ-значение.Чтобы добавить объект в набор резервных данных, необходимо:
Вызовите
writeEntityHeader()
, передав уникальный строковый ключ для данных, которые вы собираетесь записать, и размер данных.Вызовите
writeEntityData()
, передав буфер байтов, содержащий ваши данные, и количество байтов для записи из буфера, которое должно соответствовать размеру, переданному вwriteEntityHeader()
.
Например, следующий код объединяет некоторые данные в поток байтов и записывает их в один объект:
Котлин
val buffer: ByteArray = ByteArrayOutputStream().run { DataOutputStream(this).apply { writeInt(playerName) writeInt(playerScore) } toByteArray() } val len: Int = buffer.size data.apply { writeEntityHeader(TOPSCORE_BACKUP_KEY, len) writeEntityData(buffer, len) }
Ява
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(playerName); outWriter.writeInt(playerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Выполните это для каждого фрагмента данных, резервную копию которого вы хотите создать. То, как вы разделите свои данные на сущности, зависит от вас. Вы можете даже использовать только одну сущность.
Независимо от того, выполняете ли вы резервное копирование (на шаге 2), запишите представление текущих данных в
newState
ParcelFileDescriptor
. Диспетчер резервного копирования сохраняет этот объект локально как представление данных, для которых в данный момент выполняется резервное копирование. Он передает это вам обратно какoldState
при следующем вызовеonBackup()
, чтобы вы могли определить, необходима ли еще одна резервная копия, как это было сделано на шаге 1. Если вы не запишете текущее состояние данных в этот файл, тогдаoldState
будет пусто во время следующего обратного вызова.В следующем примере представление текущих данных сохраняется в
newState
с использованием временной метки последнего изменения файла:Котлин
val modified = dataFile.lastModified() FileOutputStream(newState.fileDescriptor).also { DataOutputStream(it).apply { writeLong(modified) } }
Ява
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = dataFile.lastModified(); out.writeLong(modified);
Выполнить восстановление
Когда приходит время восстановить данные вашего приложения, диспетчер резервного копирования вызывает метод onRestore()
вашего агента резервного копирования. При вызове этого метода диспетчер резервного копирования доставляет данные резервной копии, чтобы вы могли восстановить их на устройстве.
Только диспетчер резервного копирования может вызывать onRestore()
, что происходит автоматически, когда система устанавливает ваше приложение и находит существующие данные резервной копии.
Когда диспетчер резервного копирования вызывает ваш метод onRestore()
, он передает три параметра:
-
data
- Объект
BackupDataInput
, который позволяет вам читать данные резервной копии. -
appVersionCode
- Целое число, представляющее значение атрибута манифеста
android:versionCode
вашего приложения, каким оно было при резервном копировании этих данных. Вы можете использовать это, чтобы перепроверить текущую версию приложения и определить, совместим ли формат данных. Дополнительные сведения об использовании этого параметра для обработки различных версий данных восстановления см. в разделе Проверка версии данных восстановления . -
newState
- Открытый
ParcelFileDescriptor
для чтения и записи, указывающий на файл, в который вы должны записать окончательное состояние резервной копии, предоставленное сdata
. Этот объект возвращается какoldState
при следующем вызовеonBackup()
. Напомним, что вы также должны написать тот же объектnewState
в обратном вызовеonBackup()
— это также гарантирует, что объектoldState
, переданный вonBackup()
, действителен даже при первом вызовеonBackup()
после восстановления устройства.
В вашей реализации onRestore()
вы должны вызвать readNextHeader()
для data
, чтобы перебрать все объекты в наборе данных. Для каждой найденной сущности выполните следующие действия:
- Получите ключ объекта с помощью
getKey()
. Сравните ключ сущности со списком известных значений ключей, которые вы должны были объявить как статические конечные строки внутри класса
BackupAgent
. Когда ключ соответствует одной из известных строк ключа, введите оператор, чтобы извлечь данные объекта и сохранить их на устройстве:- Получите размер данных объекта с помощью
getDataSize()
и создайте массив байтов этого размера. - Вызовите
readEntityData()
и передайте ему массив байтов, куда будут помещены данные, а также укажите начальное смещение и размер для чтения. - Ваш массив байтов теперь заполнен. Считайте данные и запишите их на устройство так, как вам нравится.
- Получите размер данных объекта с помощью
После того, как вы прочитаете и запишете свои данные обратно на устройство, запишите состояние ваших данных в параметр
newState
так же, как вы это делаете во времяonBackup()
.
Например, вот как можно восстановить данные из резервной копии, приведенной в примере из предыдущего раздела:
Котлин
@Throws(IOException::class) override fun onRestore(data: BackupDataInput, appVersionCode: Int, newState: ParcelFileDescriptor) { with(data) { // There should be only one entity, but the safest // way to consume it is using a while loop while (readNextHeader()) { when(key) { TOPSCORE_BACKUP_KEY -> { val dataBuf = ByteArray(dataSize).also { readEntityData(it, 0, dataSize) } ByteArrayInputStream(dataBuf).also { DataInputStream(it).apply { // Read the player name and score from the backup data playerName = readUTF() playerScore = readInt() } // Record the score on the device (to a file or something) recordScore(playerName, playerScore) } } else -> skipEntityData() } } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream(newState.fileDescriptor).also { DataOutputStream(it).apply { writeUTF(playerName) writeInt(mPlayerScore) } } }
Ява
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data playerName = in.readUTF(); playerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(playerName, playerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(playerName); out.writeInt(mPlayerScore); }
В этом примере параметр appVersionCode
, переданный в onRestore()
не используется. Однако вы можете использовать его, если вы решили выполнить резервное копирование, когда версия приложения пользователя фактически изменилась (например, пользователь перешел с версии 1.5 вашего приложения на 1.0). Дополнительную информацию см. в следующем разделе.
Проверьте версию данных восстановления
Когда диспетчер резервного копирования сохраняет ваши данные в облачное хранилище, он автоматически включает версию вашего приложения, определенную атрибутом android:versionCode
вашего файла манифеста. Прежде чем диспетчер резервного копирования вызовет ваш агент резервного копирования для восстановления данных, он просматривает android:versionCode
установленного приложения и сравнивает его со значением, записанным в наборе данных восстановления. Если версия, записанная в наборе данных для восстановления, новее, чем версия приложения на устройстве, значит, пользователь понизил версию своего приложения. В этом случае диспетчер резервного копирования прервет операцию восстановления вашего приложения и не вызовет метод onRestore()
, поскольку набор восстановления считается бессмысленным для более старой версии.
Вы можете переопределить это поведение с помощью атрибута android:restoreAnyVersion
. Установите для этого атрибута значение true
, чтобы указать, что вы хотите восстановить приложение независимо от версии набора восстановления. Значение по умолчанию — false
. Если вы установите для этого параметра значение true
, диспетчер резервного копирования будет игнорировать android:versionCode
и во всех случаях вызывать ваш метод onRestore()
. При этом вы можете вручную проверить разницу версий в методе onRestore()
и предпринять любые шаги, необходимые для обеспечения совместимости данных, если версии не совпадают.
Чтобы помочь вам обрабатывать различные версии во время операции восстановления, метод onRestore()
передает вам код версии, включенный в набор данных восстановления, в качестве параметра appVersionCode
. Затем вы можете запросить код версии текущего приложения с помощью поля PackageInfo.versionCode
. Например:
Котлин
val info: PackageInfo? = try { packageManager.getPackageInfo(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { null } val version: Int = info?.versionCode ?: 0
Ява
PackageInfo info; try { String name = getPackageName(); info = getPackageManager().getPackageInfo(name, 0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Затем сравните version
, полученную из PackageInfo
с appVersionCode
переданным в onRestore()
.
Запросить резервную копию
Вы можете запросить операцию резервного копирования в любое время, вызвав dataChanged()
. Этот метод уведомляет диспетчер резервного копирования о том, что вы хотите выполнить резервное копирование данных с помощью агента резервного копирования. Затем диспетчер резервного копирования вызывает метод onBackup()
вашего агента резервного копирования в будущем. Как правило, вам следует запрашивать резервное копирование каждый раз, когда ваши данные изменяются (например, когда пользователь меняет настройки приложения, резервную копию которого вы хотите создать). Если вы вызываете dataChanged()
несколько раз, прежде чем диспетчер резервного копирования запросит резервную копию у вашего агента, ваш агент все равно получит только один вызов onBackup()
.
Запросить восстановление
В ходе нормальной работы вашего приложения вам не нужно запрашивать операцию восстановления. Система автоматически проверяет наличие резервных данных и выполняет восстановление после установки вашего приложения.
Перейти на автоматическое резервное копирование
Вы можете перевести свое приложение на полное резервное копирование данных, установив android:fullBackupOnly
значение true
в элементе <application>
файла манифеста. При работе на устройстве с Android 5.1 (уровень API 22) или более ранней версии ваше приложение игнорирует это значение в манифесте и продолжает выполнять резервное копирование значений ключа. При работе на устройстве с Android 6.0 (уровень API 23) или выше ваше приложение выполняет автоматическое резервное копирование вместо резервного копирования ключей и значений.
Конфиденциальность пользователя
Мы в Google прекрасно осознаем доверие, которое оказывают нам пользователи, и нашу ответственность за защиту конфиденциальности пользователей. Google безопасно передает данные резервного копирования на серверы Google и обратно, чтобы обеспечить функции резервного копирования и восстановления. Google рассматривает эти данные как личную информацию в соответствии с Политикой конфиденциальности Google.
Кроме того, пользователи могут отключить функцию резервного копирования данных через настройки резервного копирования системы Android. Когда пользователь отключает резервное копирование, служба резервного копирования Android удаляет все сохраненные данные резервного копирования. Пользователь может повторно включить резервное копирование на устройстве, но служба резервного копирования Android не будет восстанавливать ранее удаленные данные.