recuperando valores de SharedPreferences após a reinstalação e com allowBackup = true usando o backup automático do Android


9

Estou tendo problemas para recuperar valores (em um dispositivo Android 9.0) de Preferências compartilhadas após reinstalar o aplicativo e ao permitir allowBackup = true.

<manifest ... >
    ...
    <application android:allowBackup="true" ... >
        ...
    </application>
</manifest>

De acordo com isso: https://developer.android.com/guide/topics/data/autobackup

As preferências compartilhadas devem ser restauradas?

    SharedPreferences prefs = getSharedPreferences("TEST", MODE_PRIVATE);
    String name = prefs.getString("name", "No name defined");//"No name defined" is the default value.
    int idName = prefs.getInt("idName", 0); //0 is the default value.

    SharedPreferences.Editor editor = getSharedPreferences("TEST", MODE_PRIVATE).edit();
    editor.putString("name", "Elena");
    editor.putInt("idName", 12);
    editor.apply();
  • Eu entrei na minha conta do Gmail no telefone (emulador) (API 27) e, ao acessar o aplicativo Drive, backups, dados do aplicativo, vejo 18 aplicativos como Youtube, Gmail etc. e também meu próprio aplicativo.
  • Quando vou a "Configurações> Sistema> Backup", vejo os mesmos aplicativos e também meu próprio aplicativo. Eu carrego o aplicativo, o código sharedprefs é executado. Fecho o aplicativo e o removo da memória. E então eu pressiono o botão "Backup now":

insira a descrição da imagem aqui

Para verificar, posso ver meu aplicativo na lista e ele foi salvo há 0 minutos:

insira a descrição da imagem aqui

Depois, removo e reinstale o aplicativo e espero que os 2 valores (Elena e 12) estejam lá. Mas eles são apagados ...

Também tentei o mesmo via linha de comando, mas também não estava funcionando: https://developer.android.com/guide/topics/data/testingbackup.html#Preparing

adb shell
dreamlte:/ $ bmgr enabled
Backup Manager currently enabled
dreamlte:/ $ bmgr list transports
    android/com.android.internal.backup.LocalTransport
    com.google.android.gms/.backup.migrate.service.D2dTransport
* com.google.android.gms/.backup.BackupTransportService
dreamlte:/ $ bmgr backupnow my.package.name
Running incremental backup for 1 requested packages.
Package @pm@ with result: Success
Package nl.dagelijkswoord.android with progress: 1536/309248
Package nl.dagelijkswoord.android with result: Success
Backup finished with result: Success

dreamlte:/ $ dumpsys backup
Backup Manager is enabled / provisioned / not pending init
Auto-restore is disabled
No backups running
Last backup pass started: 1573804513565 (now = 1573806675410)
  next scheduled: 1573819001046
...
1573806051616 : my.package.name

dreamlte:/ $ bmgr restore 1573806051616 my.package.name                                                                                                            
No matching restore set token.  Available sets:
  35e84268c94b7d9c : SM-G950F
  3a8e32898034f8af : SM-G950F
  3e2610c4ea45fb96 : Nexus 5X
done

dreamlte:/ $ bmgr restore 35e84268c94b7d9c my.package.name                                                                                                         
Scheduling restore: SM-G950F
restoreStarting: 1 packages
onUpdate: 0 = my.package.name 
restoreFinished: 0
done
  1. Remover aplicativo
  2. Reinstale o aplicativo e verifique se os valores de SharedPreferences ainda estão lá.
  3. Os valores se foram ...

O comportamento também parece diferir nos dispositivos Samsung com Samsung Cloud, mas por enquanto vamos nos concentrar no estoque do Android.

ATUALIZAR:

Não estava mais funcionando porque eu fiz o downgrade da versão em build.gradle:

build.gradle:
versionCode 70
versionName "1.6.1"

Mesmo com android:restoreAnyVersion="true"no AndroidManifestsó funcionaria quando a versão não foi rebaixada.



Olá @PraveenSP Eu quero usar o backup automático do Android e não a versão mais antiga de armazenamento de chave / valor.
— Jim Clermonts

Respostas:


1

Por padrão, o sistema Android faz backup de todos os dados padrão se o dispositivo estiver conectado a uma rede Wi-Fi (se o usuário do dispositivo não tiver ativado os backups de dados móveis), tente reinstalar o aplicativo e conectar-se ao WI-FI, geralmente a sincronização do sistema Android dados uma vez a cada 24 horas, se houver alguma alteração. Você também pode tentar especificar suas próprias regras para backup como este.

`<application 
 android:fullBackupContent="@xml/backup_rules">
</application>`

e crie um arquivo xml e coloque-o no diretório res / xml. Dentro do arquivo, adicione regras com os elementos e. O exemplo a seguir faz backup de todas as preferências compartilhadas, exceto device.xml.

`<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
</full-backup-content>`

Espero que isso resolva seu problema.


Eu só quero salvar os compartilhados, sem xml.
— perfil completo de Jim Clermonts

O código acima é regras para o sistema Android salvar dados na unidade do usuário, não será salvo como xml. A tag Include & Exclude notifica o sistema quais dados devem ser sincronizados e salvos e quais dados devem ser excluídos na operação de sincronização .
— Ashish srivastava 19/11/19

Eu entendo, mas é desnecessário, eu acho?
— perfil completo de Jim Clermonts

11
Eu não acho, porque é desnecessário porque quando a inicialização do sistema android é concluída, verifica todas as regras de aplicativos configuradas no manifesto por padrão e também sincroniza quando allowBackup é verdadeiro, mas quando escrevemos regras, o sistema android mantém as regras locais e o associamos ao nome do pacote.
— Ashish srivastava 19/11/19

Eu adicionei o código XML acima, mas isso não faz diferença.
— Jim Clermonts

1

De acordo com o documento de backup de dados, você precisa criar o SharedPreferencesBackupHelper e siga estas etapas para obter o backup do SharedPreferences, além de registrar os serviços de backup do seu caso


Olá, estou falando do Android Auto Backup, não do serviço de backup do Android.
— Jim Clermonts

está relacionado apenas ao backup automático. para o exemplo, você pode seguir estas etapas codelabs.developers.google.com/codelabs/android-backup-codelab/…
— Damodhar

1

Que este procedimento funcione para você. Tente fazer backup e restaurar as Preferências compartilhadas de um usuário. Isso fará o backup da sua preferência para qualquer armazenamento ou dispositivo externo.

Quando o usuário desinstala o aplicativo, ele deve fazer o backup. Adicione alguma lógica de que, se o arquivo estiver presente no armazenamento externo ou no dispositivo, restaure-o novamente.

use o código abaixo para fazer backup de sua preferência.

public static void backupUserPrefs(Context context) {
    final File prefsFile = new File(context.getFilesDir(), "../shared_prefs/" + context.getPackageName() + "_preferences.xml");
    final File backupFile = new File(context.getExternalFilesDir(null),
        "preferenceBackup.xml");
    String error = "";

    try {
        FileChannel src = new FileInputStream(prefsFile).getChannel();
        FileChannel dst = new FileOutputStream(backupFile).getChannel();

        dst.transferFrom(src, 0, src.size());

        src.close();
        dst.close();

        Toast toast = Toast.makeText(context, "Backed up user prefs to " + backupFile.getAbsolutePath(), Toast.LENGTH_SHORT);
        toast.show();
        return;
    } catch (FileNotFoundException e) {
        error = e.getMessage();
        e.printStackTrace();
    } catch (IOException e) {
        error = e.getMessage();
        e.printStackTrace();
    }

use o código abaixo para restaurar a preferência

public static boolean restoreUserPrefs(Context context) {
    final File backupFile = new File(context.getExternalFilesDir(null),
        "preferenceBackup.xml");
    String error = "";

    try {

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

        Editor editor = sharedPreferences.edit();

        InputStream inputStream = new FileInputStream(backupFile);

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

        Document doc = docBuilder.parse(inputStream);
        Element root = doc.getDocumentElement();

        Node child = root.getFirstChild();
        while (child != null) {
            if (child.getNodeType() == Node.ELEMENT_NODE) {

                Element element = (Element) child;

                String type = element.getNodeName();
                String name = element.getAttribute("name");

                // In my app, all prefs seem to get serialized as either "string" or
                // "boolean" - this will need expanding if yours uses any other types!    
                if (type.equals("string")) {
                    String value = element.getTextContent();
                    editor.putString(name, value);
                } else if (type.equals("boolean")) {
                    String value = element.getAttribute("value");
                    editor.putBoolean(name, value.equals("true"));
                }
            }

            child = child.getNextSibling();

        }

        editor.commit();

        Toast toast = Toast.makeText(context, "Restored user prefs from " + backupFile.getAbsolutePath(), Toast.LENGTH_SHORT);
        toast.show();

        return true;

    } catch (FileNotFoundException e) {
        error = e.getMessage();
        e.printStackTrace();
    } catch (ParserConfigurationException e) {
        error = e.getMessage();
        e.printStackTrace();
    } catch (SAXException e) {
        error = e.getMessage();
        e.printStackTrace();
    } catch (IOException e) {
        error = e.getMessage();
        e.printStackTrace();
    }

    Toast toast = Toast.makeText(context, "Failed to restore user prefs from " + backupFile.getAbsolutePath() + " - " + error, Toast.LENGTH_SHORT);
    toast.show();

    return false;
}


    Toast toast = Toast.makeText(context, "Failed to Back up user prefs to " + backupFile.getAbsolutePath() + " - " + error, Toast.LENGTH_SHORT);
    toast.show();

}

em seguida, reinicie o aplicativo antes que essa preferência ocorra

if (restoreUserPrefs(context)) {
    // Restart              
    AlarmManager alm = (AlarmManager) context.getSystemService(
    Context.ALARM_SERVICE);
    alm.set(AlarmManager.RTC,
    System.currentTimeMillis() + 1000, PendingIntent.getActivity(context, 0,
    new Intent(context, MainActivityName.class), 0));
    android.os.Process.sendSignal(android.os.Process.myPid(),
    android.os.Process.SIGNAL_KILL);
}

Olá, o problema é que ele não faz backup quando meu versionCode no build.gradle é rebaixado. Por isso não estava funcionando. Esse código não é necessário para armazenar apenas uma sequência simples.
— Jim Clermonts

trabalhou para mim ... salvou o meu dia ... obrigado
— unownsp 4/04/19

0

Você não ativou as configurações de restauração automática, como mostrado na mensagem do console. Você deve ir para as configurações do sistema e ativá-lo. Depois disso, a restauração poderia funcionar.

Configurações -> Backup -> Restauração automática.

BTW, o token que você usou não está correto. 1573806051616 é um carimbo de data e hora em vez do token. O token deve aparecer como abaixo:

Ancestral packages: none
Ever backed up: XXXXXXXXXXXXXXXX

Aqui, o XXXXXXXXXXXXXXXXdeve ser o token.


-1

Para testar a restauração, você precisa executar o comando abaixo sem remover o aplicativo. O comando restore forçará a parada do aplicativo, limpe seus dados e executará a restauração, simulando a funcionalidade do Android AutoBackup:

bmgr restore <TOKEN> <PACKAGE>

TOKENé recuperado do logcatcomando ao executar o comando backup ou do diretóriodumpsys backup comando Além disso, você pode verificar dumpsys backupse o backup foi criado.

Mais informações aqui: https://developer.android.com/guide/topics/data/testingbackup.html#TestingRestore


Eu tentei isso e editei a resposta, mas os valores sharedpref ainda se foram.
— Jim Clermonts

Existem algumas pré-condições a serem cumpridas para backup. Por favor, verifique o link abaixo :: blog.devknox.io/…
— Susheel Tickoo

As pré-condições foram atendidas e não está funcionando.
— Jim Clermonts

-1

Uma maneira de fazer isso é a seguinte, (Observe que essa pode não ser a melhor, mas funciona!)

Antes de desinstalar o aplicativo, faça o backup dele. Adicione alguma lógica de que, se o arquivo estiver presente no armazenamento externo , restaure-o novamente.

As preferências armazenadas na classe SharedPreferences são salvas em /data/data//shared_prefs/_preferences.xml - é fácil fazer backup, basta copiar o conteúdo desse arquivo no armazenamento externo:

public static void backupUserPrefs(Context context) {
final File prefsFile = new File(context.getFilesDir(), "../shared_prefs/" + context.getPackageName() + "_preferences.xml");
final File backupFile = new File(context.getExternalFilesDir(null),
    "preferenceBackup.xml");
String error = "";

try {
    FileChannel src = new FileInputStream(prefsFile).getChannel();
    FileChannel dst = new FileOutputStream(backupFile).getChannel();

    dst.transferFrom(src, 0, src.size());

    src.close();
    dst.close();

    Toast toast = Toast.makeText(context, "Backed up user prefs to " + backupFile.getAbsolutePath(), Toast.LENGTH_SHORT);
    toast.show();
    return;
} catch (FileNotFoundException e) {
    error = e.getMessage();
    e.printStackTrace();
} catch (IOException e) {
    error = e.getMessage();
    e.printStackTrace();
}

Use o código a seguir para restaurar a preferência

Porém, a restauração oferece um desafio maior, pois o arquivo shared_prefs não é gravável diretamente pelo aplicativo e a classe SharedPrefs não expõe diretamente sua funcionalidade para serializar a partir do xml. Em vez disso, você deve analisar o XML e inserir os valores novamente. Felizmente, o arquivo XML tem uma estrutura simples, portanto é fácil fazer um loop sobre os elementos e transformá-los novamente em preferências.

if (restoreUserPrefs(context)) {
// Restart              
AlarmManager alm = (AlarmManager) context.getSystemService(
Context.ALARM_SERVICE);
alm.set(AlarmManager.RTC,
System.currentTimeMillis() + 1000, PendingIntent.getActivity(context, 0,
new Intent(context, MainActivityName.class), 0));
android.os.Process.sendSignal(android.os.Process.myPid(),
android.os.Process.SIGNAL_KILL);
}

NOTA : É necessária permissão de gravação e leitura de armazenamento externo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.