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.