ดึงค่าจาก SharedPreferences หลังจากติดตั้งใหม่และด้วย allowBackup = true โดยใช้การสำรองข้อมูลอัตโนมัติของ Android


9

ฉันมีปัญหาในการเรียกคืนค่า (บนอุปกรณ์ Android 9.0) จากการตั้งค่าที่ใช้ร่วมกันหลังจากที่ฉันติดตั้งแอพอีกครั้งและในขณะที่มี allowBackup = true

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

ตามนี้: https://developer.android.com/guide/topics/data/autobackup

การตั้งค่าที่ใช้ร่วมกันควรคืนค่า?

    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();
  • ฉันลงชื่อเข้าใช้บัญชี Gmail ของฉันในโทรศัพท์ (อีมูเลเตอร์) (API 27) และเมื่อไปที่แอพ Drive, Backup, App-data ฉันเห็นแอพ 18 แอปเช่น Youtube, Gmail เป็นต้นและแอพของฉันเอง
  • เมื่อฉันไปที่ "การตั้งค่า> ระบบ> การสำรองข้อมูล" ฉันเห็นแอพเดียวกันและแอพของฉันเอง ฉันโหลดแอพรหัสที่ใช้ร่วมกันจะถูกดำเนินการ ฉันปิดแอพและลบออกจากหน่วยความจำ จากนั้นฉันกดปุ่ม "สำรองข้อมูลตอนนี้":

ป้อนคำอธิบายรูปภาพที่นี่

ในการตรวจสอบฉันสามารถดูแอพของฉันในรายการและได้รับการสำรองข้อมูล 0 นาทีที่ผ่านมา:

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นฉันจะลบและติดตั้งแอพใหม่อีกครั้งและคาดหวังว่าจะมี 2 ค่า (Elena และ 12) แต่พวกเขาจะถูกลบ ...

ฉันยังลองเหมือนกันผ่านทางบรรทัดคำสั่ง แต่ก็ไม่ทำงาน: 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. ลบแอพ
  2. ติดตั้งแอพอีกครั้งและตรวจสอบว่าค่า SharedPreferences ยังคงอยู่หรือไม่
  3. ค่าจะหายไป ...

พฤติกรรมนั้นดูเหมือนจะแตกต่างกันไปตามอุปกรณ์ Samsung ที่มี Samsung Cloud แต่ตอนนี้เรามามุ่งเน้นที่สต็อก Android

UPDATE:

มันไม่ทำงานอีกต่อไปเพราะฉันปรับลดรุ่นใน build.gradle:

build.gradle:
versionCode 70
versionName "1.6.1"

แม้จะandroid:restoreAnyVersion="true"อยู่ในAndroidManifestนั้นก็จะทำงานได้เฉพาะเมื่อรุ่นไม่ได้ลดระดับ


นี้อาจช่วยให้คุณสำรอง sharedpreferences ในหุ่นยนต์
PraveenSP

สวัสดี @PraveenSP ฉันต้องการใช้การสำรองข้อมูลอัตโนมัติของ Android ไม่ใช่รุ่นที่เก็บคีย์ / ค่ารุ่นเก่า
Jim Clermonts

คำตอบ:


1

ตามค่าเริ่มต้นระบบ Android จะสำรองข้อมูลเริ่มต้นทั้งหมดหากอุปกรณ์เชื่อมต่อกับเครือข่าย Wi-Fi (หากผู้ใช้อุปกรณ์ไม่ได้เลือกที่จะสำรองข้อมูลมือถือ) ให้ลองติดตั้งแอพใหม่อีกครั้งและเชื่อมต่อกับ Wi-Fi โดยทั่วไป ข้อมูลหนึ่งครั้งในทุก ๆ 24 ชั่วโมงหากมีการเปลี่ยนแปลงใด ๆ คุณสามารถลองระบุกฎของคุณเองสำหรับการสำรองข้อมูลเช่นนี้

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

และสร้างไฟล์ xml และวางไว้ในไดเรกทอรี res / xml ภายในไฟล์ให้เพิ่มกฎด้วยและองค์ประกอบ ตัวอย่างต่อไปนี้สำรองข้อมูลการตั้งค่าที่แชร์ทั้งหมดยกเว้น 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>`

ฉันหวังว่ามันจะแก้ปัญหาของคุณ


ฉันแค่ต้องการบันทึก sharedprefs ไม่ใช่ xml
Jim Clermonts

รหัสข้างต้นเป็นกฎสำหรับระบบ Android ที่จะบันทึกข้อมูลในไดรฟ์ผู้ใช้มันจะไม่ถูกบันทึกเป็น xml, รวม & ยกเว้นแท็กแจ้งเตือนไปยังระบบว่าข้อมูลใดที่จะต้องซิงค์และบันทึกและข้อมูลใดที่จะต้องแยกออกจากการดำเนินการซิงค์ .
Ashish srivastava

ฉันเข้าใจ แต่มันไม่จำเป็นฉันคิดว่า?
Jim Clermonts

1
ฉันไม่คิดว่ามันเป็นเพราะมันไม่จำเป็นเมื่อบูตระบบ Android รวบรวมมันตรวจสอบกฎแอพทั้งหมดที่กำหนดค่าโดยชัดแจ้งโดยค่าเริ่มต้นมันยังซิงค์เมื่ออนุญาตให้แบ็คอัปเป็นจริง แต่เมื่อเราเขียนกฎระบบ Android เก็บไว้ในกฎท้องถิ่น
Ashish srivastava

ฉันได้เพิ่มโค้ด XML ด้านบน แต่ไม่ได้สร้างความแตกต่าง
Jim Clermonts

1

ตามเอกสารแบ็กอัพข้อมูลคุณต้องสร้างSharedPreferencesBackupHelperและทำตามขั้นตอนเหล่านี้เพื่อให้การแบ็คอัพของ SharedPreferences จำเป็นต้องลงทะเบียนบริการสำรองข้อมูลสำหรับกรณีของคุณ


สวัสดีฉันกำลังพูดถึงการสำรองข้อมูลอัตโนมัติของ Android ไม่ใช่บริการสำรองข้อมูลของ Android
Jim Clermonts

มันเกี่ยวข้องกับการสำรองข้อมูลอัตโนมัติเท่านั้น สำหรับตัวอย่างคุณสามารถทำตามขั้นตอนเหล่านี้codelabs.developers.google.com/codelabs/android-backup-codelab/…
Damodhar

1

ขอให้ขั้นตอนนี้ใช้ได้สำหรับคุณ ลองสำรองและกู้คืน sharedPreferences ของผู้ใช้ นี่จะเป็นการสำรองการตั้งค่าของคุณไปยังอุปกรณ์จัดเก็บข้อมูลภายนอกหรืออุปกรณ์ใด ๆ

เมื่อผู้ใช้ถอนการติดตั้งแอพผู้ใช้ควรทำการสำรอง เพิ่มตรรกะบางอย่างที่หากไฟล์มีอยู่ในที่จัดเก็บข้อมูลภายนอกหรืออุปกรณ์ให้เรียกคืนกลับมา

ใช้รหัสด้านล่างเพื่อสำรองการตั้งค่าของคุณ

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();
    }

ใช้รหัสด้านล่างเพื่อเรียกคืนการตั้งค่า

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();

}

จากนั้นรีสตาร์ทแอปของคุณก่อนที่สิ่งเหล่านี้จะเริ่มต้น

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);
}

สวัสดีปัญหาคือว่ามันไม่ได้สำรองเมื่อ versionCode ของฉันใน build.gradle ถูกปรับลดรุ่น นั่นคือเหตุผลที่มันไม่ทำงาน รหัสจำนวนมากนี้ไม่จำเป็นสำหรับการจัดเก็บสตริงแบบธรรมดาเท่านั้น
Jim Clermonts

ทำงานให้ฉัน ... บันทึกวันของฉัน ... ขอบคุณ
unownsp

0

คุณไม่ได้เปิดการตั้งค่ากู้คืนอัตโนมัติตามที่แสดงในข้อความคอนโซล คุณควรไปที่การตั้งค่าระบบและเปิดใช้งาน หลังจากนั้นการฟื้นฟูก็สามารถใช้ได้

การตั้งค่า -> สำรอง -> กู้คืนอัตโนมัติ

BTW โทเค็นที่คุณใช้ไม่ถูกต้อง 1573806051616 เป็นเวลาประทับแทนโทเค็น โทเค็นควรปรากฏดังต่อไปนี้:

Ancestral packages: none
Ever backed up: XXXXXXXXXXXXXXXX

ที่นี่XXXXXXXXXXXXXXXXควรเป็นโทเค็น


-1

ในการทดสอบการกู้คืนคุณต้องเรียกใช้คำสั่งด้านล่างโดยไม่ต้องลบแอปของคุณ คำสั่งกู้คืนจะบังคับให้หยุดแอปพลิเคชั่นล้างข้อมูลและทำการกู้คืนดังนั้นจึงจำลองการทำงานของ Android AutoBackup:

bmgr restore <TOKEN> <PACKAGE>

TOKENถูกดึงจากlogcatคำสั่งเมื่อรันคำสั่ง backup หรือจากdumpsys backupคำสั่ง นอกจากนี้คุณสามารถตรวจสอบdumpsys backupเพื่อตรวจสอบว่าการสำรองข้อมูลถูกสร้างขึ้น

ข้อมูลเพิ่มเติมที่นี่: https://developer.android.com/guide/topics/data/testingbackup.html#TestingRestore


ฉันลองและแก้ไขคำตอบ แต่ค่า sharedpref ยังคงหายไป
Jim Clermonts

มีเงื่อนไขบางประการที่จะต้องทำตามการสำรองข้อมูล กรุณาตรวจสอบลิงค์ด้านล่าง :: blog.devknox.io/ …
Susheel Tickoo

เป็นไปตามเงื่อนไขที่กำหนดและไม่สามารถใช้งานได้
Jim Clermonts

-1

วิธีหนึ่งในการทำเช่นนี้มีดังนี้ (โปรดทราบว่านี่อาจไม่ใช่วิธีที่ดีที่สุด แต่ใช้งานได้!)

ก่อนที่จะถอนการติดตั้งแอปให้สำรองข้อมูล เพิ่มตรรกะบางอย่างที่ถ้าไฟล์มีอยู่ในที่จัดเก็บข้อมูลภายนอกให้เรียกคืนกลับมา

ค่ากำหนดที่เก็บไว้ในคลาส SharedPreferences จะถูกบันทึกลงใน /data/data//shared_prefs/_preferences.xml - การสำรองข้อมูลเป็นเรื่องง่ายคุณเพียงแค่คัดลอกเนื้อหาของไฟล์นี้ไปยังหน่วยเก็บข้อมูลภายนอก:

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();
}

ใช้รหัสต่อไปนี้เพื่อกู้คืนการตั้งค่า

แต่การกู้คืนนั้นมีความท้าทายมากกว่าเนื่องจากไฟล์ shared_prefs ไม่สามารถเขียนได้โดยตรงโดยแอพและคลาส SharedPrefs ไม่เปิดเผยการทำงานโดยตรงกับซีเรียลไลซ์จาก xml แต่คุณต้องแยกวิเคราะห์ XML ด้วยตัวเองและดันกลับเข้าไปใหม่โชคดีที่ไฟล์ XML มีโครงสร้างที่ตรงไปตรงมาดังนั้นจึงง่ายต่อการวนรอบองค์ประกอบและเปลี่ยนกลับเป็นค่ากำหนด

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);
}

หมายเหตุ : ต้องใช้สิทธิ์ในการเขียนและอ่านจากที่เก็บข้อมูลภายนอก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.