Fazer backup de pares de chave-valor com o Android Backup Service
O Android Backup Service oferece backup e restauração de armazenamento em nuvem para dados de chave-valor no seu app para Android. Durante esse tipo de operação, os dados de backup do app são passados 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 armazenar em backup e restaurar, consulte a Visão geral do backup de dados.
Observação: para a maior parte dos apps, o backup automático é usado para backup e restauração. Seu app só pode implementar o backup automático ou o backup chave-valor, não ambos. O backup automático não exige código e faz backup de arquivos inteiros, enquanto o backup de chave-valor exige código para definir o conteúdo do backup explicitamente na forma de pares de chave-valor.
declarar o agente de backup no arquivo do manifesto com o atributo android:backupAgent;
registrar seu app no Android Backup Service;
definir um agente de backup de uma das seguintes maneiras:
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 modificar onBackup() e onRestore() para processar as operações de backup e restauração dos seus dados.
A classe BackupAgentHelper fornece um wrapper conveniente em torno da classe BackupAgent, o que diminui a quantidade de código necessária. No seu BackupAgentHelper, é 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 implementar onBackup() e onRestore(). A menos que você precise ter controle total dos backups do seu app, recomendamos o uso do BackupAgentHelper para processar os backups do seu app.
Atualmente, o Android oferece auxiliares de backup que farão backup e restauração de arquivos completos a partir do SharedPreferences e do armazenamento interno.
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>.
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.
Registrar-se no 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 posterior.
Para que o app faça backup usando o Android Backup Service, é necessário registrá-lo no serviço para receber uma chave do Backup Service e declará-la no manifesto do Android.
Para receber a chave do Backup Service, registre-se no Android Backup Service . Ao se registrar, você receberá uma chave do Backup Service e o código XML <meta-data> adequado para o arquivo de manifesto do Android, que precisa ser incluído como filho do elemento <application>. Por exemplo:
O android:name precisa ser "com.google.android.backup.api_key", e o android:value precisa ser a chave do Backup Service recebida ao se registrar no Android Backup Service.
Caso você tenha vários apps, é necessário registrar cada um deles usando o respectivo nome de pacote.
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. No entanto, se você implementar o backup do app, sempre inclua uma chave para o Android Backup Service de forma que o app possa fazer backup quando o dispositivo usar a transferência oferecida por esse serviço. Se o dispositivo não usar o Android Backup Service, o elemento <meta-data> com a chave do Backup Service será ignorado.
Crie seu agente de backup usando 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:
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 ao BackupAgentHelper.
Por exemplo, para fazer backup de um arquivo SharedPreferences denominado user_preferences, um agente de backup completo que usa BackupAgentHelper é semelhante a:
Pronto. 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.
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:
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:
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:
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:
Isso é tudo o que você precisa para fazer o backup e a restauração de dados com BackupAgentHelper.
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.
O Backup Manager chama esse método depois que você solicita um backup. Nesse método, os dados do app são lidos a partir do dispositivo, e os dados que serão armazenados em backup são passados para o Backup Manager, conforme descrito abaixo em Fazer um backup.
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.
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 a partir do Backup Manager com a ferramenta bmgr.
Quando o Backup Manager chama o método onBackup(), ele passa três parâmetros:
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 que onBackup() foi chamado, conforme definido por newState abaixo ou a partir de onRestore(). Veja mais informações sobre isso na próxima seção. Como onBackup() 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.
Um objeto BackupDataOutput, usado para entregar os dados de backup para o Backup Manager.
Um ParcelFileDescriptor aberto, de leitura/gravação, que aponta para um arquivo em que você precisa gravar uma representação dos dados entregues ao data (uma representação pode ser apenas o carimbo de data/hora da última modificação do seu arquivo). Esse objeto será retornado como oldState na próxima vez que o Backup Manager chamar o método onBackup(). Se você não gravar os dados de backup em newState, oldState apontará um arquivo vazio na próxima vez que o Backup Manager chamar onBackup().
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 em oldState depende de como eles foram gravados originalmente em newState (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 de oldState:
Se seus dados mudaram em comparação a oldState, grave os dados atuais em data 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.
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 para writeEntityHeader()).
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).
Independentemente de ter feito um backup ou não (na etapa 2), grave uma representação dos dados atuais no ParcelFileDescriptor em newState. 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ê como oldState na próxima vez que chamar onBackup(), 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:
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.
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:
Um BackupDataInput, que permite ler seus dados de backup.
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.
Um ParcelFileDescriptor aberto, de leitura/gravação, que aponta para um arquivo em que você precisa gravar o estado de backup final fornecido com data. Esse objeto será retornado como oldState na próxima vez que onBackup() for chamado. Lembre-se de que você precisa gravar o mesmo objeto newState no callback onBackup(). Fazer isso também aqui garante que o objeto oldState entregue a onBackup() seja válido mesmo na primeira vez que onBackup() é 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.
Depois de ler e gravar os dados no dispositivo, grave o estado deles no parâmetro newState da mesma forma que faria durante onBackup().
Nesse exemplo, o parâmetro appVersionCode passado 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.
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 modificar 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ê defini-lo 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. Por exemplo:
Então, basta comparar a version adquirida de PackageInfo com o appVersionCode passado 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.
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.
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.
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 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 nível 23) ou posterior, o app realiza o backup automático em vez do backup de chave-valor.
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 privacidade 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.