Observação: para a maior parte dos apps, o
Backup automático é usado para
implementar o backup e a restauração. Seu app só pode implementar o Backup automático
ou o backup de chave-valor, mas não os dois. O Backup automático não exige código e faz backup
de arquivos inteiros, enquanto o backup de chave-valor exige que você escreva o código para definir
o conteúdo do backup explicitamente na forma de pares de chave-valor.
Anteriormente, os desenvolvedores registravam o app para receber uma chave de serviço. Não é
mais necessário registrar seu app para o backup de chave-valor.
O Android Backup Service oferece backup e restauração de armazenamento em nuvem para dados de chave-valor no seu app Android. Durante esse tipo de operação, os dados de backup do app são transmitidos para a transferência de backup do dispositivo. Se o dispositivo estiver usando a Transferência de backup padrão do Google, os dados serão transferidos para o Android Backup Service para arquivamento.
A quantidade de dados é limitada a 5 MB por usuário do app, e nenhuma cobrança é gerada para armazenar os dados de backup.
Para ter uma visão geral das opções de backup do Android e ver uma orientação sobre quais dados você deve armazenar em backup e restaurar, consulte a Visão geral do backup de dados.
Implementar backup de chave-valor
Para fazer backup dos dados do app, é necessário implementar um agente de backup. Seu agente de backup é chamado pelo Backup Manager durante o backup e a restauração.
Para implementar um agente de backup, é necessário:
- declarar o agente de backup no arquivo do manifesto com o atributo
android:backupAgent
; - definir um agente de backup de uma das seguintes maneiras:
- Estender a classe BackupAgent
A classe
BackupAgent
fornece a interface central com que seu app se comunica com o Backup Manager. Se você estender essa classe diretamente, será necessário modificaronBackup()
eonRestore()
para processar as operações de backup e restauração dos seus dados. - Estender a classe BackupAgentHelper
A classe
BackupAgentHelper
fornece um wrapper conveniente em torno da classeBackupAgent
, o que diminui a quantidade de código necessária. No seuBackupAgentHelper
, é necessário usar um ou mais objetos "helper", que fazem o backup e a restauração de certos tipos de dados automaticamente. Assim, você não precisa implementaronBackup()
eonRestore()
. A menos que você precise ter controle total dos backups do seu app, recomendamos o uso doBackupAgentHelper
para processá-los.Atualmente, o Android oferece auxiliares de backup que farão backup e restauração de arquivos completos de
SharedPreferences
e do armazenamento interno.
Ou
- Estender a classe BackupAgent
Declarar o agente de backup no manifesto
Essa é a etapa mais fácil. Depois de decidir o nome da classe do
agente de backup, declare-o no manifesto com o atributo android:backupAgent
na tag <application>
.
Exemplo:
<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>
Para oferecer compatibilidade com dispositivos mais antigos, recomendamos adicionar a chave de API <meta-data>
ao seu
arquivo de manifesto do Android. O Android Backup Service não requer mais uma chave
de serviço, mas alguns dispositivos mais antigos ainda podem verificar uma chave ao fazer backup. Defina
android:name
como "com.google.android.backup.api_key"
e
android:value
como "unused"
.
Outro atributo que você pode usar é android:restoreAnyVersion
. Esse atributo usa um valor booleano para indicar se você
quer restaurar os dados do app, independentemente da versão atual do app em comparação com a
versão que produziu os dados de backup. O valor padrão é: "false
". Consulte Verificar a versão dos dados de restauração para mais informações.
O Android Backup Service
O Google oferece uma transferência de backup com o Android Backup Service para a maioria dos dispositivos com o Android versão 2.2 ou mais recente.
Observação: não há garantia de que a transferência de backup oferecida pelo Android Backup Service esteja disponível em todos os dispositivos Android compatíveis com backup. Alguns dispositivos podem oferecer compatibilidade por meio de outra transferência; outros podem ser incompatíveis, e não há como seu app saber qual é a transferência usada no dispositivo.
Estender o BackupAgentHelper
Crie seu agente de backup usando o BackupAgentHelper
se quiser
fazer backup de arquivos completos (de SharedPreferences
ou do armazenamento interno).
A criação do seu agente de backup com BackupAgentHelper
exige muito menos
código do que a extensão de BackupAgent
, porque não é necessário implementar
onBackup()
e onRestore()
.
Sua implementação de BackupAgentHelper
precisa
usar um ou mais auxiliares de backup. Um auxiliar de backup é um componente
especializado que BackupAgentHelper
invoca para realizar operações de backup e
restauração para um determinado tipo de dado. Atualmente, o framework do Android oferece dois
auxiliares diferentes:
SharedPreferencesBackupHelper
para fazer backup de arquivosSharedPreferences
.FileBackupHelper
para fazer backup de arquivos do armazenamento interno.
É possível incluir vários auxiliares no BackupAgentHelper
, mas apenas
um auxiliar é necessário para cada tipo de dado. Ou seja, se você tem vários
arquivos SharedPreferences
, apenas um
SharedPreferencesBackupHelper
é necessário.
Para cada auxiliar que você quiser adicionar ao BackupAgentHelper
, é preciso fazer
o seguinte durante o método onCreate()
:
- Instanciar um exemplo da classe auxiliar visada. No construtor da classe, é necessário especificar os arquivos adequados que você quer armazenar em backup.
- Chamar
addHelper()
para adicionar o auxiliar aoBackupAgentHelper
.
As seções a seguir descrevem como criar um agente de backup usando cada auxiliar disponível.
Fazer backup de SharedPreferences
Ao instanciar um SharedPreferencesBackupHelper
, é necessário
incluir o nome de um ou mais arquivos SharedPreferences
.
Por exemplo, para fazer backup de um arquivo SharedPreferences
denominado
user_preferences
, um agente de backup completo que usa BackupAgentHelper
é semelhante a:
Kotlin
// 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) } } }
Java
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); } }
É isso. O SharedPreferencesBackupHelper
inclui todo o código necessário para fazer o backup e restauração de um arquivo SharedPreferences
.
Quando o Backup Manager chama onBackup()
eonRestore()
, BackupAgentHelper
chama os auxiliares de backup para fazer o
backup e a restauração dos arquivos especificados.
Observação: os métodos de SharedPreferences
são seguros para linha de execução. Portanto,
é possível ler e gravar com segurança o arquivo de preferências compartilhadas do agente de backup e
de outras atividades.
Fazer backup de outros arquivos
Ao instanciar um FileBackupHelper
, é necessário incluir o nome de
um ou mais arquivos salvos no armazenamento interno
do app, conforme especificado por getFilesDir()
, que é o mesmo
local em que openFileOutput()
grava
arquivos.
Por exemplo, para fazer backup de dois arquivos denominados scores
e stats
, um agente
de backup que usa BackupAgentHelper
é semelhante a:
Kotlin
// 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) } } }
Java
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); } }
O FileBackupHelper
inclui todo o código necessário para fazer o backup e
a restauração de arquivos salvos no armazenamento interno do app.
No entanto, a leitura e a gravação de arquivos no armazenamento interno não são seguras para a linha de execução. Para garantir que o agente de backup não leia ou grave seus arquivos ao mesmo tempo que suas atividades, é necessário usar instruções sincronizadas todas as vezes que você fizer uma leitura ou gravação. Por exemplo, em qualquer atividade em que você lê e grava o arquivo, é necessário usar um objeto como o bloqueio intrínseco das instruções sincronizadas:
Kotlin
// Object for intrinsic lock companion object { val sDataLock = Any() }
Java
// Object for intrinsic lock static final Object sDataLock = new Object();
Em seguida, crie uma instrução sincronizada com esse bloqueio sempre que ler ou gravar os arquivos. Por exemplo, veja uma instrução sincronizada para gravar a pontuação mais recente de um jogo em um arquivo:
Kotlin
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") }
Java
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"); }
Sincronize suas instruções de leitura com o mesmo bloqueio.
Em seguida, no BackupAgentHelper
, é preciso modificar onBackup()
e onRestore()
para sincronizar as operações de backup e restauração com o mesmo
bloqueio intrínseco. Por exemplo, o caso com MyFileBackupAgent
acima precisa dos seguintes
métodos:
Kotlin
@Throws(IOException::class) override fun onBackup( oldState: ParcelFileDescriptor, data: BackupDataOutput, newState: ParcelFileDescriptor ) { // Hold the lock while the FileBackupHelper performs backup 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) } }
Java
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup 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); } }
Isso é tudo o que você precisa para fazer o backup e a restauração de dados com
BackupAgentHelper
.
Estender o BackupAgent
A maior parte dos apps não precisa estender a classe BackupAgent
diretamente, mas deve estender o BackupAgentHelper para aproveitar
as classes auxiliares integradas que fazem o backup e a restauração dos seus arquivos automaticamente. No entanto,
estenda BackupAgent
diretamente se uma das ações a seguir for necessária:
- Controlar as versões do seu formato de dados. Por exemplo, se você previr a necessidade de revisar o formato em que os dados do app são gravados, crie um agente de backup para fazer uma verificação cruzada da versão do app durante uma operação de restauração. Faça todo o trabalho de compatibilidade necessário caso a versão no dispositivo seja diferente daquela dos dados de backup. Para mais informações, consulte Verificar a versão dos dados de restauração.
- Em vez de fazer backup de um arquivo inteiro, é possível especificar as partes dos dados que serão armazenadas em backup e como cada parte será restaurada no dispositivo. Isso também pode ajudar a gerenciar versões diferentes, porque os dados são lidos e gravados como entidades únicas, em vez de arquivos completos.
- Fazer backup de dados em um banco de dados. Se você tiver um banco de dados SQLite que pretende restaurar quando
o usuário reinstalar seu app, será necessário criar um
BackupAgent
personalizado que leia os dados corretos durante uma operação de backup. Em seguida, crie sua tabela e insira os dados durante a operação de restauração.
Se você não precisa realizar nenhuma das tarefas acima e quer fazer backup dos arquivos completos de
SharedPreferences
ou do armazenamento interno, consulte
Estender o BackupAgentHelper.
Métodos obrigatórios
Ao criar um BackupAgent
,
é necessário implementar os seguintes métodos de callback:
onBackup()
- O Backup Manager chama esse método depois que você solicita um backup. Nesse método, os dados do app são lidos no dispositivo, e os dados que serão armazenados em backup são transmitidos para o Backup Manager, conforme descrito abaixo em Fazer um backup.
onRestore()
- O Backup Manager chama esse método durante uma operação de restauração.
Esse método exibe seus dados de backup, que podem ser usados pelo app para restaurar
o estado anterior, conforme descrito abaixo em
Fazer uma restauração.
O sistema chama esse método para restaurar qualquer dado de backup quando o usuário reinstala seu app, mas o app também pode solicitar uma restauração.
Fazer um backup
No momento de fazer backup dos dados do app, o Backup Manager chama seu método onBackup()
. É nessa etapa que você precisa informar os dados do app ao Backup Manager para
salvá-los no armazenamento em nuvem.
Só o Backup Manager pode chamar o método onBackup()
do seu agente de backup. Sempre que os dados do app mudarem e você quiser fazer um backup,
será necessário solicitar uma operação de backup chamando dataChanged()
. Consulte Solicitar um
backup para mais informações. Uma solicitação de backup não resulta em uma chamada imediata para o
método onBackup()
. Em vez disso, o Backup Manager aguarda o momento adequado e faz
o backup de todos os apps que fizeram essa solicitação desde a data do último backup.
Dica: durante o desenvolvimento do app, é possível iniciar uma
operação de backup imediata pelo Backup Manager com a ferramenta bmgr
.
Quando o Backup Manager chama o método onBackup()
, ele passa três parâmetros:
oldState
- Um
ParcelFileDescriptor
aberto, somente leitura, que aponta para o último estado de backup fornecido pelo app. Esses dados de backup não são do armazenamento em nuvem, mas uma representação local dos dados que foram armazenados na última vez queonBackup()
foi chamado, conforme definido pornewState
abaixo ou a partir deonRestore()
. Veja mais informações sobre isso na próxima seção. ComoonBackup()
não permite a leitura de dados de backup existentes no armazenamento em nuvem, é possível usar essa representação local para determinar se os dados mudaram desde o último backup. data
- Um objeto
BackupDataOutput
, usado para entregar os dados de backup para o Backup Manager. newState
- Um
ParcelFileDescriptor
aberto, de leitura/gravação, que aponta para um arquivo em que você precisa gravar uma representação dos dados entregues aodata
(uma representação pode ser apenas o carimbo de data/hora da última modificação do seu arquivo). Esse objeto será retornado comooldState
na próxima vez que o Backup Manager chamar o métodoonBackup()
. Se você não gravar os dados de backup emnewState
,oldState
apontará um arquivo vazio na próxima vez que o Backup Manager chamaronBackup()
.
Usando esses parâmetros, implemente seu método onBackup()
para fazer o seguinte:
- Verifique se os dados mudaram desde o último backup, comparando
oldState
aos dados atuais. A forma como os dados são lidos emoldState
depende de como eles foram gravados originalmente emnewState
(veja a etapa 3). A forma mais fácil de registrar o estado de um arquivo é com o carimbo de data/hora da última modificação. Por exemplo, veja como ler e comparar um carimbo de data/hora deoldState
:Kotlin
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 }
Java
// 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 }
Se não houve nenhuma mudança e você não precisa de backup, pule para a etapa 3.
- Se seus dados mudaram em comparação a
oldState
, grave os dados atuais emdata
para fazer backup no armazenamento em nuvem.É preciso gravar cada bloco de dados como uma “entidade” em
BackupDataOutput
. Uma entidade é um registro de dados binários nivelado, identificado por uma string de chave única. Assim, o conjunto de dados armazenado em backup é, conceitualmente, um conjunto de pares de chave-valor.Para adicionar uma entidade ao seu conjunto de dados de backup, é necessário:
- chamar
writeEntityHeader()
, passando uma chave de string única para os dados que você está prestes a gravar e para o tamanho dos dados; - chamar
writeEntityData()
, passando um buffer de bytes contendo seus dados e o número de bytes a serem gravados do buffer (que deve corresponder ao tamanho passado parawriteEntityHeader()
).
Por exemplo, o código a seguir nivela alguns dados em um fluxo de bytes e o grava em uma única entidade:
Kotlin
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) }
Java
// 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);
Faça isso para cada bloco de dados que você quer armazenar em backup. A forma como você divide os dados em entidades fica a seu critério (e é possível usar somente uma entidade).
- chamar
- Independentemente de ter feito um backup ou não (na etapa 2), grave uma representação dos dados atuais
no
ParcelFileDescriptor
emnewState
. O Backup Manager mantém esse objeto localmente como uma representação dos dados que estão em backup no momento. Ele o passará de volta para você comooldState
na próxima vez que chamaronBackup()
, para que você possa determinar se outro backup é necessário (conforme mostrado na etapa 1). Se você não gravar o estado atual dos dados nesse arquivo,oldState
ficará vazio durante o próximo callback.O exemplo a seguir salva uma representação dos dados atuais em
newState
, por meio do carimbo de data/hora da última modificação do arquivo:Kotlin
val modified = dataFile.lastModified() FileOutputStream(newState.fileDescriptor).also { DataOutputStream(it).apply { writeLong(modified) } }
Java
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = dataFile.lastModified(); out.writeLong(modified);
Atenção: se os dados do app estiverem salvos em um arquivo, use instruções sincronizadas ao acessar o arquivo para que o agente de backup não o leia enquanto uma atividade no app também estiver fazendo a gravação.
Fazer uma restauração
No momento de restaurar os dados do app, o Backup Manager chama o
método onRestore()
do agente de backup. Ao chamar esse método, o Backup Manager exibe seus dados de backup para que
você possa restaurá-los no dispositivo.
Somente o Backup Manager pode chamar onRestore()
, o que acontece automaticamente quando o sistema instala seu app e
encontra dados de backup existentes.
Observação: ao desenvolver seu app, também é possível solicitar uma
operação de restauração com a
ferramenta bmgr
.
Quando o Backup Manager chama o método onRestore()
, ele passa três parâmetros:
data
- Um
BackupDataInput
, que permite ler seus dados de backup. appVersionCode
- Um número inteiro que representa o valor do atributo do manifesto
android:versionCode
do seu app, referente ao momento em que esses dados foram armazenados em backup. Essa opção permite verificar a versão atual do app e determinar se o formato de dados é compatível. Para ver mais informações sobre como usar esse parâmetro para processar diferentes versões de dados de restauração, consulte a seção abaixo sobre Verificar a versão dos dados de restauração. newState
- Um
ParcelFileDescriptor
aberto, de leitura/gravação, que aponta para um arquivo em que você precisa gravar o estado de backup final fornecido comdata
. Esse objeto será retornado comooldState
na próxima vez queonBackup()
for chamado. Lembre-se de que você precisa gravar o mesmo objetonewState
no callbackonBackup()
. Fazer isso também aqui garante que o objetooldState
entregue aonBackup()
seja válido mesmo na primeira vez queonBackup()
é chamado depois da restauração do dispositivo.
Na implementação de onRestore()
, chame readNextHeader()
nos
data
para iterar
por meio de todas as entidades no conjunto de dados. Para cada entidade encontrada, faça o seguinte:
- Para conseguir a chave da entidade, use
getKey()
. - Compare a chave da entidade com uma lista de chaves-valor conhecidas, que devem ter sido declaradas como strings
estáticas finais dentro da classe
BackupAgent
. Quando a chave corresponder a uma das strings de chave conhecidas, insira uma instrução para extrair os dados da entidade e salve-os no dispositivo:- Veja o tamanho dos dados da entidade com
getDataSize()
e crie uma matriz de bytes desse tamanho. - Chame
readEntityData()
e passe a ele a matriz de bytes, que é para onde os dados irão, e especifique o deslocamento inicial e o tamanho a ser lido. - Sua matriz de bytes já está cheia e você pode ler os dados e gravá-los no dispositivo da forma que preferir.
- Veja o tamanho dos dados da entidade com
- Depois de ler e gravar os dados no dispositivo, grave o estado deles no parâmetro
newState
da mesma forma que faria duranteonBackup()
.
Por exemplo, veja como restaurar os dados armazenados em backup de acordo com o exemplo da seção anterior:
Kotlin
@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) } } }
Java
@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); }
Nesse exemplo, o parâmetro appVersionCode
transmitido para onRestore()
não é usado. No entanto, use-o se você optou por fazer backup
quando a versão do app utilizada pelo usuário
regride (por exemplo, o usuário passou da versão 1.5 do seu app para a 1.0). Para mais informações, consulte
a seção sobre Verificar a versão dos dados de restauração.
Verificar a versão dos dados de restauração
Quando o Backup Manager salva seus dados no armazenamento em nuvem, ele inclui automaticamente a versão
do seu app, conforme definido pelo atributo android:versionCode
do arquivo de manifesto. Antes de o Backup Manager chamar seu agente de backup para restaurar seus dados, ele
analisa o android:versionCode
do app instalado e o compara com o valor
registrado no conjunto de dados de restauração. Se a versão registrada no conjunto de dados de restauração for
mais recente do que a versão do app no dispositivo, o usuário terá feito downgrade do
app. Nesse caso, o Backup Manager cancelará a operação de restauração do app
e não chamará o método onRestore()
,
porque o conjunto de restauração será considerado irrelevante para uma versão mais antiga.
É possível substituir esse comportamento com o atributo android:restoreAnyVersion
. Esse atributo é "true
" ou "false
" para indicar se você quer restaurar o app, independentemente da versão do conjunto
de restauração. O valor padrão é "false
". Se você o definir como "true
"
o Backup Manager ignorará o android:versionCode
e chamará o método onRestore()
em todos os casos. Ao fazer isso, você poderá verificar manualmente a diferença de versão no seu método onRestore()
e seguir qualquer etapa necessária para tornar os dados compatíveis se as versões forem conflitantes.
Para processar versões diferentes durante uma operação de restauração, o método onRestore()
passa o código de versão incluído no conjunto de dados de restauração como o
parâmetro appVersionCode
. Em seguida, é possível consultar o código de versão do app atual com o campo PackageInfo.versionCode
. Exemplo:
Kotlin
val info: PackageInfo? = try { packageManager.getPackageInfo(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { null } val version: Int = info?.versionCode ?: 0
Java
PackageInfo info; try { String name = getPackageName(); info = getPackageManager().getPackageInfo(name, 0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Então, basta comparar a version
adquirida de PackageInfo
com o appVersionCode
transmitido para onRestore()
.
Cuidado: é preciso entender as consequências de se configurar
android:restoreAnyVersion
como "true
" para seu app. Se cada versão do app
compatível com backup não considerar corretamente as variações no formato de dados durante
onRestore()
, os dados no dispositivo poderão ser salvos em um formato incompatível com a versão atualmente
instalada no dispositivo.
Solicitar um backup
Você pode solicitar uma operação de backup a qualquer momento chamando dataChanged()
. Esse método notifica o Backup Manager de que você
quer fazer backup dos dados por meio do agente de backup. Em seguida, o Backup Manager chama o método
onBackup()
do agente de backup em um momento oportuno no futuro. Normalmente, é necessário
solicitar um backup sempre que seus dados mudam (por exemplo, quando o usuário muda uma preferência do app
que você quer armazenar em backup). Se você chamar dataChanged()
várias vezes seguidas antes de o Backup
Manager solicitar um backup, seu agente ainda receberá apenas uma chamada para onBackup()
.
Observação: ao desenvolver seu app, você pode solicitar um
backup e iniciar uma operação de backup imediata com a ferramenta
bmgr
.
Solicitar uma restauração
Durante a vida normal do seu app, não deve ser necessário solicitar uma operação de restauração. O sistema verifica automaticamente os dados de backup e realiza uma restauração quando o app é instalado.
Observação: ao desenvolver seu app, você pode solicitar uma
operação de restauração com a ferramenta
bmgr
.
Migrar para o backup automático
Você pode fazer a transição do seu app para backups de dados completos configurando
android:fullBackupOnly
como true
no elemento <application>
do arquivo de manifesto. Quando
executado em um dispositivo com o Android 5.1 (API de nível 22) ou anterior, o app ignora
esse valor no manifesto e continua realizando backups de chave-valor. Quando
executado em um dispositivo com o Android 6.0 (API de nível 23) ou posterior, o app realiza o
backup automático em vez do backup de chave-valor.
Privacidade do usuário
No Google, estamos cientes da confiança que os usuários depositam em nós e da nossa responsabilidade de proteger a privacidade. O Google transmite os dados de backup entre os próprios servidores com segurança para oferecer recursos de backup e restauração. O Google trata esses dados como informações pessoais de acordo com a Política de Privacidade.
Além disso, os usuários podem desativar o recurso de backup de dados nas configurações de backup do sistema Android. Quando o usuário desativa o backup, o Android Backup Service exclui todos os dados de backup salvos. O usuário pode ativar novamente o backup no dispositivo, mas o Android Backup Service não restaurará os dados excluídos anteriormente.