สำรองข้อมูลคู่คีย์-ค่าด้วยบริการสำรองข้อมูล Android

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

ข้อมูลจะจำกัดอยู่ที่ 5 MB ต่อผู้ใช้ 1 คนของแอป โดยไม่มีค่าใช้จ่ายในการจัดเก็บข้อมูลสำรอง

หากต้องการดูภาพรวมของตัวเลือกการสำรองข้อมูลของ Android และคำแนะนำเกี่ยวกับข้อมูลที่คุณ ควรสำรองข้อมูลและคืนค่า โปรดดูการสำรองข้อมูล ภาพรวม

ใช้การสำรองข้อมูลคีย์-ค่า

หากต้องการสำรองข้อมูลแอป คุณต้องใช้ Agent สำรอง ข้อมูลสำรองของคุณ เครื่องมือจัดการการสำรองข้อมูลจะเรียก Agent ทั้งในระหว่างการสำรองข้อมูลและการกู้คืน

หากต้องการใช้ Agent สำรอง คุณต้องดำเนินการดังนี้

  1. ประกาศ Agent ข้อมูลสำรองในไฟล์ Manifest ด้วยเมธอด android:backupAgent

  2. กำหนด Agent สำรองโดยทำอย่างใดอย่างหนึ่งต่อไปนี้

    • ขยายเวลา BackupAgent

      ชั้นเรียน BackupAgent มีอินเทอร์เฟซส่วนกลางที่แอปของคุณใช้เพื่อสื่อสารกับ เครื่องมือจัดการการสำรองข้อมูล หากขยายคลาสนี้โดยตรง คุณต้องลบล้าง onBackup() และ onRestore() เพื่อจัดการการดำเนินการสำรองและกู้คืนข้อมูล

    • ขยายเวลา BackupAgentHelper

      คลาส BackupAgentHelper เป็นตัวช่วยที่สะดวกสำหรับคลาส BackupAgent ซึ่งจะช่วยลดปริมาณโค้ดที่คุณต้องเขียน ใน BackupAgentHelper คุณต้องใช้ออบเจ็กต์ตัวช่วยอย่างน้อย 1 รายการ สำรองข้อมูลและคืนค่าข้อมูลบางประเภทโดยอัตโนมัติ เพื่อที่คุณไม่ต้อง ต้องติดตั้งใช้งาน onBackup() และ onRestore() เราขอแนะนำให้ใช้ BackupAgentHelper เพื่อจัดการข้อมูลสำรองของแอป เว้นแต่ว่าคุณต้องการควบคุมข้อมูลสำรองของแอปอย่างเต็มรูปแบบ

      ปัจจุบัน Android มีเครื่องมือช่วยสํารองข้อมูลที่จะสํารองและกู้คืนไฟล์ที่สมบูรณ์จาก SharedPreferences และพื้นที่เก็บข้อมูลภายใน

ประกาศตัวแทนการสำรองข้อมูลในไฟล์ Manifest

เมื่อเลือกชื่อคลาสสำหรับตัวแทนการสำรองข้อมูลแล้ว ให้ประกาศชื่อนั้นในไฟล์ Manifest โดยใช้แอตทริบิวต์ android:backupAgent ในแท็ก <application>

เช่น

<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>

หากต้องการรองรับอุปกรณ์รุ่นเก่า เราขอแนะนำให้เพิ่มคีย์ API <meta-data> ลงในไฟล์ Manifest ของ Android Android Backup Service ไม่จำเป็นต้องใช้คีย์บริการอีกต่อไป แต่อุปกรณ์รุ่นเก่าบางรุ่นอาจยังตรวจสอบคีย์เมื่อสำรองข้อมูล ตั้งค่า android:name เป็น com.google.android.backup.api_key และ android:value ถึง unused

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

ขยาย BackupAgentHelper

คุณควรสร้าง Agent สำรองโดยใช้ BackupAgentHelper หากต้องการ สำรองไฟล์ที่สมบูรณ์จาก SharedPreferences หรือที่จัดเก็บข้อมูลภายใน การสร้าง Agent สำรองด้วย BackupAgentHelper ต้องใช้โค้ดน้อยกว่ามาก ขยายเวลาของ BackupAgent เพราะคุณไม่ต้องติดตั้งใช้งาน onBackup() และ onRestore()

การใช้งาน BackupAgentHelper ต้องใช้ผู้ช่วยเหลือในการสำรองข้อมูลอย่างน้อย 1 ราย ผู้ช่วยสำรองคือคอมโพเนนต์พิเศษที่ BackupAgentHelper เรียกใช้ ทำการสำรองและกู้คืนข้อมูลสำหรับข้อมูลประเภทใดประเภทหนึ่ง ปัจจุบันเฟรมเวิร์ก Android มีตัวช่วย 2 รายการ ได้แก่

  • SharedPreferencesBackupHelper เพื่อสำรองข้อมูลไฟล์ SharedPreferences
  • FileBackupHelper ถึงด้านหลัง ไฟล์จากที่จัดเก็บข้อมูลภายใน

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

สำหรับผู้ช่วยแต่ละคนที่คุณต้องการเพิ่มลงใน BackupAgentHelper คุณจะต้องทำ การติดตามระหว่าง เมธอด onCreate():

  1. สร้างอินสแตนซ์ของคลาสตัวช่วยที่ต้องการ ในคอนสตรัคเตอร์ของคลาส คุณต้องระบุไฟล์ที่ต้องการสํารองข้อมูล
  2. โทรไปที่ addHelper() เพื่อเพิ่มผู้ช่วยลงใน BackupAgentHelper

ส่วนต่อไปนี้จะอธิบายวิธีสร้าง Agent สำรองโดยใช้ ผู้ช่วยเหลือที่พร้อมให้บริการ

สำรองข้อมูล SharedPreferences

เมื่อสร้างอินสแตนซ์ SharedPreferencesBackupHelper คุณต้องระบุชื่อไฟล์ SharedPreferences อย่างน้อย 1 ไฟล์

ตัวอย่างเช่น หากต้องการสำรองข้อมูลไฟล์ SharedPreferences ที่ชื่อ user_preferences ตัวแทนการสำรองข้อมูลแบบสมบูรณ์ที่ใช้ BackupAgentHelper จะมีลักษณะดังนี้

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

SharedPreferencesBackupHelper มีโค้ดทั้งหมดที่จำเป็นในการสำรองและกู้คืนไฟล์ SharedPreferences

เมื่อผู้จัดการสำรองโทรหา onBackup() และ onRestore() BackupAgentHelper จะโทรหาผู้ช่วยสำรองข้อมูลเพื่อสำรองข้อมูลและกู้คืน ไฟล์ที่ระบุ

สำรองไฟล์อื่นๆ

เมื่อสร้างอินสแตนซ์ FileBackupHelper คุณต้องระบุชื่อของ บันทึกไฟล์ลงในที่จัดเก็บข้อมูลภายในของแอป ตามที่ระบุโดย getFilesDir(), ซึ่งเป็นตำแหน่งเดียวกับที่ openFileOutput() เขียนไฟล์

เช่น หากต้องการสำรองข้อมูล 2 ไฟล์ชื่อ scores และ stats ซึ่งเป็น Agent ข้อมูลสำรอง ที่ใช้ BackupAgentHelper จะมีลักษณะดังนี้

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

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

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

Kotlin

// Object for intrinsic lock
companion object {
    val sDataLock = Any()
}

Java

// Object for intrinsic lock
static final Object sDataLock = new Object();

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

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

คุณควรซิงค์ข้อความการอ่านด้วยล็อกเดียวกัน

จากนั้นใน BackupAgentHelper คุณต้องลบล้าง onBackup() และ onRestore() เพื่อซิงค์การดำเนินการสำรองและกู้คืนข้อมูล ล็อกภายใน ตัวอย่างเช่น ตัวอย่าง MyFileBackupAgent จากด้านบนต้องใช้วิธีการต่อไปนี้

Kotlin

@Throws(IOException::class)
override fun onBackup(
        oldState: ParcelFileDescriptor,
        data: BackupDataOutput,
        newState: ParcelFileDescriptor
) {
    // Hold the lock while the FileBackupHelper performs back up
    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 back up
    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);
    }
}

ขยาย BackupAgent

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

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

หากไม่จําเป็นต้องทํางานใดๆ ข้างต้นและต้องการสํารองข้อมูลไฟล์ทั้งหมดจาก SharedPreferences หรือพื้นที่เก็บข้อมูลภายใน โปรดดูการขยายBackupAgentHelper

เมธอดที่จำเป็น

เมื่อสร้าง BackupAgent คุณต้องติดตั้งใช้งานเมธอดการเรียกกลับต่อไปนี้

onBackup()
เครื่องมือจัดการข้อมูลสํารองจะเรียกใช้เมธอดนี้หลังจากที่คุณขอสํารองข้อมูล วิธีนี้ให้คุณอ่านข้อมูลแอปจาก อุปกรณ์ของคุณและส่งข้อมูลที่คุณต้องการสำรองไปยังเครื่องมือจัดการการสำรองข้อมูล ตามที่อธิบายไว้ในหัวข้อดำเนินการสำรองข้อมูล
onRestore()

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

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

สำรองข้อมูล

คำขอสำรองจะไม่ส่งผลให้คุณโทรหา onBackup() ทันที แต่เครื่องมือจัดการข้อมูลสํารองจะรอเวลาที่เหมาะสม จากนั้นจะสํารองข้อมูลสําหรับแอปทั้งหมดที่ขอสํารองข้อมูลนับตั้งแต่ที่สํารองข้อมูลครั้งล่าสุด ขั้นตอนนี้เป็นเวลาที่คุณต้องให้ข้อมูลแอปแก่เครื่องมือจัดการข้อมูลสํารองเพื่อให้บันทึกข้อมูลลงในพื้นที่เก็บข้อมูลระบบคลาวด์ได้

มีเพียงเครื่องมือจัดการการสำรองข้อมูลเท่านั้นที่จะเรียกเมธอด onBackup() ของ Agent สำรองได้ ชิ้น ทุกครั้งที่ข้อมูลแอปเปลี่ยนไป และคุณต้องการสำรองข้อมูล คุณต้อง ขอการดำเนินการสำรองข้อมูลโดยการเรียกใช้ dataChanged() โปรดดูข้อมูลเพิ่มเติมที่ขอข้อมูลสำรอง

เคล็ดลับ: ขณะพัฒนาแอป คุณจะเริ่มสำรองข้อมูลได้ทันที จากเครื่องมือจัดการการสำรองข้อมูลที่มี bmgr เครื่องมือ

เมื่อเครื่องมือจัดการข้อมูลสํารองเรียกใช้เมธอด onBackup() ระบบจะส่งพารามิเตอร์ 3 รายการต่อไปนี้

oldState
ไฟล์ ParcelFileDescriptor แบบเปิดที่อ่านอย่างเดียวซึ่งชี้ไปที่สถานะการสำรองข้อมูลล่าสุดที่แอปของคุณระบุ ไม่ใช่ข้อมูลสำรองจากพื้นที่เก็บข้อมูลระบบคลาวด์ แต่เป็นการแสดงข้อมูลสำรองในพื้นที่เก็บข้อมูลในเครื่องเมื่อมีการเรียกใช้ onBackup() ครั้งล่าสุดตามที่ระบุโดย newState หรือจาก onRestore() เราจะกล่าวถึง onRestore() ในส่วนถัดไป เพราะ onBackup() ไม่อนุญาตให้คุณอ่านข้อมูลสำรองที่มีอยู่ในระบบคลาวด์ คุณสามารถใช้การนำเสนอภายในเครื่องเพื่อพิจารณาว่าข้อมูลของคุณ เปลี่ยนแปลงไปจากการสำรองข้อมูลครั้งล่าสุด
data
ออบเจ็กต์ BackupDataOutput ใช้เพื่อส่งข้อมูลสํารองไปยังเครื่องมือจัดการข้อมูลสํารอง
newState
ParcelFileDescriptor แบบอ่าน/เขียนที่เปิดอยู่ซึ่งชี้ไปยังไฟล์ที่คุณต้องใช้เขียนการนำเสนอข้อมูลที่ส่งไปยัง data ต แสดงแทนได้ง่ายขึ้น เช่น การประทับเวลาที่แก้ไขล่าสุดสำหรับไฟล์ ออบเจ็กต์นี้จะแสดงผลเป็น oldState ในครั้งถัดไปที่เรียกใช้ Backup Manager เมธอด onBackup() หากคุณไม่ได้เขียนข้อมูลสำรองลงใน newState จากนั้น oldState จะชี้ไปที่ไฟล์ว่างเมื่อเรียกผู้จัดการสำรองในครั้งถัดไป onBackup()

ใช้พารามิเตอร์เหล่านี้และใช้เมธอด onBackup() เพื่อทำสิ่งต่อไปนี้

  1. ตรวจสอบว่าข้อมูลเปลี่ยนแปลงไปหรือไม่นับจากการสำรองข้อมูลครั้งล่าสุดโดยการเปรียบเทียบ oldState ไปยังข้อมูลปัจจุบันของคุณ วิธีอ่านข้อมูลใน oldState จะขึ้นอยู่กับวิธีเขียนข้อมูลไปยัง newState เดิม (ดูขั้นตอนที่ 3) วิธีที่ง่ายที่สุดในการ บันทึกสถานะของไฟล์เป็นการประทับเวลาที่แก้ไขล่าสุด ตัวอย่างเช่น วิธีอ่านและเปรียบเทียบการประทับเวลาจาก oldState มีดังนี้

    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
    }

    หากไม่มีอะไรเปลี่ยนแปลงและคุณไม่จำเป็นต้องสำรองข้อมูล ให้ข้ามไปยังขั้นตอนที่ 3

  2. หากข้อมูลมีการเปลี่ยนแปลงเมื่อเทียบกับ oldState ให้เขียนข้อมูลปัจจุบันลงใน data เพื่อสำรองข้อมูลไปยังพื้นที่เก็บข้อมูลระบบคลาวด์

    คุณต้องเขียนข้อมูลแต่ละส่วนเป็นเอนทิตีใน BackupDataOutput CANNOT TRANSLATE เอนทิตีคือระเบียนข้อมูลไบนารีที่แยกเป็นหลายรายการซึ่งระบุโดยคีย์ที่ไม่ซ้ำกัน สตริง ดังนั้น ชุดข้อมูลที่คุณสำรองข้อมูลในแนวคิดชุด คู่คีย์-ค่า

    หากต้องการเพิ่มเอนทิตีไปยังชุดข้อมูลสำรอง คุณต้องดำเนินการดังนี้

    1. โทรไปที่ writeEntityHeader() โดยส่งคีย์สตริงที่ไม่ซ้ำกันสำหรับข้อมูลที่คุณกำลังจะเขียนและขนาดข้อมูล

    2. โทรไปที่ writeEntityData() โดยส่งบัฟเฟอร์ไบต์ที่มีข้อมูลและจํานวนไบต์ที่จะเขียนจากบัฟเฟอร์ ซึ่งควรตรงกับขนาดที่ส่งไปยัง writeEntityHeader()

    ตัวอย่างเช่น โค้ดต่อไปนี้จะผสานข้อมูลบางส่วนเป็นสตรีมไบต์และเขียนลงในเอนทิตีเดียว

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

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

  3. ไม่ว่าคุณจะสำรองข้อมูลหรือไม่ (ในขั้นตอนที่ 2) ให้เขียนการนำเสนอข้อมูลปัจจุบันลงใน newState ParcelFileDescriptor Backup Manager จะเก็บออบเจ็กต์นี้ไว้ในเครื่องเพื่อแสดงข้อมูลที่สำรองข้อมูลไว้ในปัจจุบัน ครั้งต่อไปจะส่งข้อมูลนี้กลับมาให้คุณด้วยชื่อ oldState จะเรียก onBackup() เพื่อให้คุณสามารถระบุได้ว่าข้อมูลสำรองอีกรายการหนึ่งคือ ตามขั้นตอนที่ 1 หากคุณไม่ได้เขียนสถานะข้อมูลปัจจุบันลงในไฟล์นี้ oldState จะว่างเปล่าระหว่างการเรียกกลับครั้งถัดไป

    ตัวอย่างต่อไปนี้จะบันทึกการนำเสนอข้อมูลปัจจุบันลงใน newState ใช้การประทับเวลาที่แก้ไขล่าสุดของไฟล์:

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

ดำเนินการคืนค่า

เมื่อถึงเวลากู้คืนข้อมูลแอป ตัวจัดการข้อมูลสำรองจะเรียกใช้ข้อมูลสำรอง เมธอด onRestore() ของตัวแทน เมื่อเรียกใช้เมธอดนี้ เครื่องมือจัดการข้อมูลสำรองจะส่งข้อมูลสำรองให้คุณกู้คืนลงในอุปกรณ์

มีเพียงผู้จัดการสำรองเท่านั้นที่โทรหา onRestore() ได้ ซึ่งจะเกิดขึ้นโดยอัตโนมัติ เมื่อระบบติดตั้งแอปของคุณและพบข้อมูลสำรองที่มีอยู่

ได้ด้วย

เมื่อผู้จัดการสำรองเรียกเมธอด onRestore() เมธอดจะผ่านสาม ได้แก่

data
ออบเจ็กต์ BackupDataInput ซึ่งช่วยให้คุณอ่านข้อมูลสำรองได้
appVersionCode
จำนวนเต็มแสดงค่าของแอตทริบิวต์ไฟล์ Manifest ของแอป android:versionCode ตามที่บันทึกไว้เมื่อสำรองข้อมูลนี้ คุณใช้ เพื่อตรวจสอบเวอร์ชันปัจจุบันของแอปและดูว่ารูปแบบข้อมูล ที่เข้ากันได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ตัวเลือกนี้เพื่อจัดการข้อมูลการกู้คืนเวอร์ชันต่างๆ ได้ที่ตรวจสอบเวอร์ชันข้อมูลการกู้คืน
newState
ParcelFileDescriptor ที่เปิดอยู่ซึ่งอ่าน/เขียนซึ่งชี้ไปยังไฟล์ที่คุณ ต้องเขียนสถานะการสำรองข้อมูลสุดท้ายที่ให้ไว้กับ data ระบบจะแสดงผลออบเจ็กต์นี้เป็น oldState เมื่อเรียกใช้ onBackup() ในครั้งถัดไป โปรดทราบว่าคุณต้องเขียนออบเจ็กต์ newState เดียวกันใน onBackup() callback ด้วย ซึ่งการเขียนที่นี่จะช่วยให้ออบเจ็กต์ oldState ที่ส่งให้กับ onBackup() ใช้งานได้แม้ในครั้งแรกที่เรียกใช้ onBackup() หลังจากกู้คืนอุปกรณ์แล้ว

ในการใช้งาน onRestore() คุณควรเรียกใช้ readNextHeader() ในวันที่ data เพื่อทำซ้ำผ่านเอนทิตีทั้งหมดในชุดข้อมูล สำหรับเอนทิตีแต่ละรายการ ที่พบ ให้ทำตามขั้นตอนต่อไปนี้

  1. รับคีย์เอนทิตีด้วย getKey()
  2. เปรียบเทียบคีย์เอนทิตีกับรายการค่าคีย์ที่รู้จักซึ่งคุณควรมี ประกาศเป็นสตริงสุดท้ายแบบคงที่ภายในคลาส BackupAgent ของคุณ เมื่อคีย์ตรงกับสตริงคีย์ที่คุณรู้จัก ให้ป้อนคำสั่งเพื่อดึงข้อมูลเอนทิตีและบันทึกลงในอุปกรณ์

    1. ดูขนาดข้อมูลเอนทิตีด้วย getDataSize() และสร้างไบต์อาร์เรย์ขนาดดังกล่าว
    2. โทร readEntityData() แล้วส่งไบต์อาร์เรย์ ซึ่งเป็นจุดที่ข้อมูลจะไป และระบุ ออฟเซ็ตเริ่มต้นและขนาดที่จะอ่าน
    3. ไบต์อาร์เรย์ของคุณเต็มแล้ว อ่านข้อมูลและเขียนลงในอุปกรณ์ อย่างไรก็ได้ตามที่คุณต้องการ
  3. หลังจากที่อ่านและเขียนข้อมูลกลับไปยังอุปกรณ์แล้ว ให้เขียนสถานะ ข้อมูลลงในพารามิเตอร์ newState เช่นเดียวกับที่ทำระหว่าง onBackup()

ตัวอย่างวิธีคืนค่าข้อมูลที่สำรองไว้ในตัวอย่าง ส่วนก่อนหน้า:

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

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

ตรวจสอบเวอร์ชันข้อมูลการกู้คืน

เมื่อเครื่องมือจัดการการสำรองข้อมูลบันทึกข้อมูลลงในพื้นที่เก็บข้อมูลระบบคลาวด์ ระบบจะดำเนินการดังกล่าวโดยอัตโนมัติ มีเวอร์ชันของแอปตามที่กำหนดโดยไฟล์ Manifest android:versionCode ก่อนที่ผู้จัดการข้อมูลสำรองจะเรียกใช้ข้อมูลสำรอง Agent ของคุณเพื่อกู้คืนข้อมูล มันจะดูที่ android:versionCode ของ แอปที่ติดตั้งแล้วเปรียบเทียบกับค่าที่บันทึกไว้ในชุดข้อมูลคืนค่า ถ้า เวอร์ชันที่บันทึกไว้ในชุดข้อมูลคืนค่าใหม่กว่าเวอร์ชันแอปใน อุปกรณ์ จากนั้นผู้ใช้ได้ดาวน์เกรดแอปของตน ในกรณีนี้ ข้อมูลสำรอง ผู้จัดการจะล้มเลิกการดำเนินการกู้คืนแอปของคุณและไม่เรียกใช้ onRestore() เนื่องจากชุดการคืนค่าถือว่าไม่มีความหมายสำหรับ เวอร์ชันเก่า

คุณลบล้างลักษณะการทำงานนี้ได้ด้วยแอตทริบิวต์ android:restoreAnyVersion ตั้งค่าแอตทริบิวต์นี้เป็น true เพื่อระบุว่าคุณต้องการกู้คืนแอป โดยไม่คำนึงถึงเวอร์ชันที่คืนค่าเวอร์ชัน ค่าเริ่มต้นคือ false หากคุณ ตั้งค่าเป็น true จากนั้นเครื่องมือจัดการการสำรองข้อมูลจะไม่สนใจ android:versionCodeและเรียกเมธอด onRestore() ในทุกกรณี กำลังทำ คุณจึงตรวจสอบความแตกต่างของเวอร์ชันด้วยตนเองได้ใน onRestore() และดำเนินการตามขั้นตอนที่จำเป็นเพื่อทำให้ข้อมูลใช้งานร่วมกันได้หากเวอร์ชัน ไม่ตรง

เพื่อช่วยคุณจัดการเวอร์ชันต่างๆ ในระหว่างดำเนินการกู้คืน เมธอด onRestore() จะส่งรหัสเวอร์ชันที่มาพร้อมกับข้อมูลการคืนค่าให้คุณ ตั้งค่าเป็นพารามิเตอร์ appVersionCode จากนั้นคุณสามารถค้นหารหัสเวอร์ชันของแอปปัจจุบันได้โดยใช้ช่อง PackageInfo.versionCode เช่น

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

จากนั้นเปรียบเทียบ version ที่ได้จาก PackageInfo กับ appVersionCode ที่ส่งไปยัง onRestore()

ขอข้อมูลสํารอง

คุณขอให้ดำเนินการสำรองข้อมูลได้ทุกเมื่อโดยโทรไปที่หมายเลข dataChanged() วิธีนี้จะแจ้งให้เครื่องมือจัดการการสำรองข้อมูลทราบว่าคุณต้องการสำรองข้อมูลโดยใช้ตัวแทนการสำรองข้อมูล จากนั้นเครื่องมือจัดการข้อมูลสำรองจะเรียกใช้เมธอด onBackup() ของตัวแทนการสำรองข้อมูลในอนาคต โดยปกติแล้ว คุณควรขอการสํารองข้อมูลทุกครั้งที่มีการเปลี่ยนแปลงข้อมูล (เช่น เมื่อผู้ใช้เปลี่ยนค่ากําหนดของแอปที่คุณต้องการสํารองข้อมูล) หากคุณโทรหา dataChanged() หลายหมายเลข จำนวนครั้งก่อนที่เครื่องมือจัดการข้อมูลสำรองจะส่งคำขอข้อมูลสำรองจากตัวแทนของคุณ ยังคงได้รับโทรศัพท์จาก onBackup() เพียงครั้งเดียว

ขอกู้คืน

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

ย้ายข้อมูลไปยังการสำรองข้อมูลอัตโนมัติ

คุณสามารถเปลี่ยนแอปเป็นการสำรองข้อมูลทั้งหมดได้โดยการตั้งค่า android:fullBackupOnly เป็น true ในองค์ประกอบ <application> ในไฟล์ Manifest เมื่อทำงานในอุปกรณ์ที่ใช้ Android 5.1 (API ระดับ 22) หรือต่ำกว่า แอปจะละเว้นค่านี้ในไฟล์ Manifest และทำการสำรองข้อมูลคีย์-ค่าต่อไป เมื่อทำงาน ในอุปกรณ์ Android 6.0 (API ระดับ 23) ขึ้นไป แอปของคุณจะใช้ Auto การสำรองข้อมูลแทนคีย์-ค่าสำรอง

ความเป็นส่วนตัวของผู้ใช้

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

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