Mencadangkan key-value pair dengan Android Backup Service

Android Backup Service menyediakan pencadangan dan pemulihan penyimpanan cloud untuk data nilai kunci di aplikasi Android Anda. Selama operasi pencadangan nilai kunci berlangsung, data pencadangan aplikasi diteruskan ke backup transport perangkat. Jika perangkat menggunakan Google Backup Transport default, data akan diteruskan ke Android Backup Service untuk pengarsipan.

Data dibatasi hingga 5 MB per pengguna aplikasi. Tidak ada biaya untuk penyimpanan data cadangan.

Untuk ringkasan opsi pencadangan Android dan panduan terkait data apa saja yang harus dicadangkan dan dipulihkan, baca Ringkasan pencadangan data.

Mengimplementasikan pencadangan nilai kunci

Untuk mencadangkan data aplikasi, Anda perlu mengimplementasikan agen pencadangan. Agen pencadangan dipanggil oleh Pengelola Pencadangan selama proses pencadangan dan pemulihan.

Untuk mengimplementasikan agen pencadangan, Anda harus:

  1. Mendeklarasikan agen pencadangan dalam file manifes dengan atribut android:backupAgent.

  2. Tentukan agen pencadangan dengan melakukan salah satu tindakan berikut:

    • Memperluas BackupAgent

      Class BackupAgent menyediakan antarmuka pusat yang digunakan aplikasi untuk berkomunikasi dengan Pengelola Pencadangan. Jika langsung memperluas class ini, Anda harus mengganti onBackup() dan onRestore() untuk menangani operasi pencadangan dan pemulihan data Anda.

    • Memperluas BackupAgentHelper

      Class BackupAgentHelper menyediakan wrapper yang tepat di sekitar class BackupAgent, sehingga meminimalkan jumlah kode yang perlu Anda tulis. Dalam BackupAgentHelper, Anda harus menggunakan satu atau beberapa objek helper, yang otomatis mencadangkan dan memulihkan jenis data tertentu, sehingga Anda tidak perlu mengimplementasikan onBackup() dan onRestore(). Kecuali jika Anda memerlukan kontrol penuh atas cadangan aplikasi, sebaiknya gunakan BackupAgentHelper untuk menangani cadangan aplikasi Anda.

      Saat ini, Android menyediakan helper pencadangan yang akan mencadangkan dan memulihkan seluruh file dari SharedPreferences and penyimpanan internal.

Mendeklarasikan agen pencadangan dalam manifes

Setelah Anda menentukan nama class untuk agen pencadangan, deklarasikan agen pencadangan tersebut dalam manifes menggunakan atribut android:backupAgent dalam tag <application>.

Contoh:

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

Untuk mendukung perangkat yang lebih lama, sebaiknya tambahkan kunci API <meta-data> ke file manifes Android Anda. Android Backup Service tidak lagi memerlukan kunci layanan, tetapi beberapa perangkat yang lebih lama mungkin masih memeriksa kunci saat melakukan pencadangan. Tetapkan android:name ke com.google.android.backup.api_key, dan android:value ke unused.

Atribut android:restoreAnyVersion memerlukan nilai boolean untuk menunjukkan apakah Anda ingin memulihkan data aplikasi, terlepas dari versi aplikasi saat ini dibandingkan dengan versi yang menghasilkan data cadangan. Nilai defaultnya adalah false. Lihat Memeriksa versi data pemulihan untuk informasi selengkapnya.

Memperluas BackupAgentHelper

Anda harus membuat agen pencadangan menggunakan BackupAgentHelper jika ingin mencadangkan seluruh file dari SharedPreferences atau penyimpanan internal. Membuat agen pencadangan dengan BackupAgentHelper memerlukan kode yang jauh lebih sedikit daripada memperluas BackupAgent, karena Anda tidak perlu mengimplementasikan onBackup() dan onRestore().

Implementasi BackupAgentHelper harus menggunakan satu atau beberapa helper pencadangan. Helper pencadangan adalah komponen khusus yang digunakan BackupAgentHelper untuk melakukan operasi pencadangan dan pemulihan jenis data tertentu. Framework Android saat ini menyediakan dua helper yang berbeda:

Anda dapat menyertakan beberapa helper di BackupAgentHelper, tetapi hanya satu helper yang diperlukan untuk setiap jenis data. Artinya, jika memiliki beberapa file SharedPreferences, Anda hanya memerlukan satu SharedPreferencesBackupHelper.

Untuk setiap helper yang ingin ditambahkan ke BackupAgentHelper, Anda harus melakukan tindakan berikut selama metode onCreate():

  1. Membuat instance class helper yang diinginkan. Di konstruktor class, Anda harus menentukan file yang ingin dicadangkan.
  2. Panggil addHelper() untuk menambahkan helper ke BackupAgentHelper.

Bagian berikut menjelaskan cara membuat agen pencadangan menggunakan masing-masing helper yang tersedia.

Mencadangkan SharedPreferences

Saat membuat instance SharedPreferencesBackupHelper, Anda harus menyertakan nama satu atau beberapa file SharedPreferences.

Misalnya, untuk mencadangkan file SharedPreferences bernama user_preferences, agen pencadangan lengkap menggunakan BackupAgentHelper yang terlihat seperti ini:

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 menyertakan semua kode yang diperlukan untuk mencadangkan dan memulihkan file SharedPreferences.

Saat Pengelola Pencadangan memanggil onBackup() dan onRestore(), BackupAgentHelper akan memanggil helper pencadangan Anda untuk mencadangkan dan memulihkan file yang ditentukan.

Mencadangkan file lainnya

Saat membuat instance FileBackupHelper, Anda harus menyertakan nama satu atau beberapa file yang disimpan ke penyimpanan internal aplikasi, seperti yang ditentukan oleh getFilesDir(), yakni lokasi yang sama tempat openFileOutput() menulis file.

Misalnya, untuk mencadangkan dua file bernama scores dan stats, agen pencadangan menggunakan BackupAgentHelper yang terlihat seperti ini:

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 menyertakan semua kode yang diperlukan untuk mencadangkan dan memulihkan file yang disimpan ke penyimpanan internal aplikasi Anda.

Namun, membaca dan menulis file di penyimpanan internal tidak aman bagi thread. Untuk memastikan agen pencadangan tidak membaca atau menulis file bersamaan dengan aktivitas Anda, gunakan pernyataan tersinkron setiap kali Anda melakukan proses baca atau tulis. Misalnya, dalam aktivitas apa pun saat Anda membaca dan menulis file, Anda memerlukan objek untuk digunakan sebagai kunci intrinsik untuk pernyataan tersinkron:

Kotlin

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

Java

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

Lalu, buat pernyataan tersinkron dengan kunci ini setiap kali Anda membaca atau menulis file. Misalnya, berikut adalah pernyataan tersinkron untuk menulis skor terbaru dalam game ke file:

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

Anda harus menyinkronkan pernyataan baca dengan kunci yang sama.

Lalu, di BackupAgentHelper, Anda harus mengganti onBackup() dan onRestore() untuk menyinkronkan operasi pencadangan dan pemulihan dengan kunci intrinsik yang sama. Misalnya, contoh MyFileBackupAgent di atas memerlukan metode berikut:

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

Memperluas BackupAgent

Sebagian besar aplikasi tidak perlu memperluas class BackupAgent secara langsung, tetapi perlu memperluas BackupAgentHelper untuk memanfaatkan class helper bawaan yang otomatis mencadangkan dan memulihkan file Anda. Namun, Anda dapat memperluas BackupAgent secara langsung untuk melakukan tindakan berikut:

  • Menetapkan versi format data Anda. Misalnya, jika mengantisipasi kebutuhan untuk memperbaiki format yang digunakan untuk menulis data aplikasi, Anda dapat membuat agen pencadangan untuk memeriksa versi aplikasi selama operasi pemulihan dan melakukan pekerjaan kompatibilitas apa pun yang diperlukan jika versi di perangkat berbeda dengan data cadangan. Untuk informasi selengkapnya, lihat Memeriksa versi data pemulihan.
  • Menentukan bagian data yang akan dicadangkan. Daripada mencadangkan seluruh file, Anda dapat menentukan bagian data yang akan dicadangkan dan cara setiap bagian tersebut dipulihkan ke perangkat. Tindakan ini juga dapat membantu mengelola versi yang berbeda karena Anda membaca dan menulis data sebagai entitas unik, bukan seluruh file.
  • Mencadangkan data di database. Jika memiliki database SQLite yang ingin dipulihkan saat pengguna menginstal ulang aplikasi, Anda perlu membuat BackupAgent kustom yang membaca data yang sesuai selama operasi pencadangan, lalu membuat tabel dan memasukkan data selama operasi pemulihan.

Jika Anda tidak perlu melakukan salah satu tugas di atas dan ingin mencadangkan seluruh file dari SharedPreferences atau penyimpanan internal, lihat Memperluas BackupAgentHelper.

Metode yang diperlukan

Saat membuat BackupAgent, Anda harus mengimplementasikan metode callback berikut:

onBackup()
Pengelola Pencadangan memanggil metode ini setelah Anda meminta pencadangan. Dalam metode ini, Anda membaca data aplikasi dari perangkat dan meneruskan data yang ingin dicadangkan ke Pengelola Pencadangan, seperti yang dijelaskan dalam Melakukan pencadangan.
onRestore()

Pengelola Pencadangan memanggil metode ini selama operasi pemulihan. Metode ini mengirimkan data cadangan, yang dapat digunakan aplikasi untuk memulihkan status sebelumnya, seperti yang dijelaskan dalam Melakukan pemulihan.

Sistem memanggil metode ini untuk memulihkan data cadangan apa pun saat pengguna menginstal ulang aplikasi Anda, tetapi aplikasi Anda juga dapat meminta pemulihan.

Melakukan pencadangan

Permintaan pencadangan tidak menghasilkan panggilan langsung ke metode onBackup() Anda. Sebaliknya, Pengelola Pencadangan menunggu waktu yang tepat, lalu melakukan pencadangan untuk semua aplikasi yang telah meminta pencadangan sejak pencadangan terakhir dilakukan. Ini adalah titik saat Anda harus memberikan data aplikasi ke Pengelola Pencadangan agar dapat disimpan ke penyimpanan cloud.

Hanya Pengelola Pencadangan yang dapat memanggil metode onBackup() agen pencadangan Anda. Setiap kali data aplikasi berubah dan Anda ingin melakukan pencadangan, Anda harus meminta operasi pencadangan dengan memanggil dataChanged(). Lihat Meminta pencadangan untuk informasi selengkapnya.

Tips: Saat mengembangkan aplikasi, Anda dapat memulai operasi pencadangan langsung dari Pengelola Pencadangan dengan alat bmgr.

Jika Pengelola Pencadangan memanggil metode onBackup() Anda, metode ini akan meneruskan tiga parameter:

oldState
ParcelFileDescriptor hanya baca terbuka yang mengarah ke status pencadangan terakhir yang diberikan oleh aplikasi Anda. Ini bukan data cadangan dari penyimpanan cloud, tetapi representasi lokal dari data yang dicadangkan saat terakhir kali onBackup() dipanggil, seperti yang didefinisikan oleh newState atau dari onRestore(). onRestore() dibahas di bagian berikutnya. Karena onBackup() tidak mengizinkan Anda membaca data cadangan yang ada di penyimpanan cloud, Anda dapat menggunakan representasi lokal ini untuk menentukan apakah data Anda telah berubah sejak pencadangan terakhir.
data
Objek BackupDataOutput, yang Anda gunakan untuk mengirim data cadangan ke Pengelola Pencadangan.
newState
ParcelFileDescriptor baca/tulis terbuka yang mengarah ke file tempat Anda harus menulis representasi dari data yang dikirimkan ke data. Representasi dapat semudah seperti stempel waktu yang terakhir diubah untuk file Anda. Objek ini ditampilkan sebagai oldState saat Pengelola Pencadangan memanggil metode onBackup() Anda lagi. Jika Anda tidak menulis data cadangan ke newState, oldState akan mengarah ke file kosong saat Pengelola Pencadangan memanggil onBackup() lagi.

Dengan menggunakan parameter ini, implementasikan metode onBackup() Anda untuk melakukan hal berikut:

  1. Memeriksa apakah data telah berubah sejak pencadangan terakhir dengan membandingkan oldState terhadap data saat ini. Cara membaca data dalam oldState bergantung pada cara Anda awalnya menulisnya ke newState (lihat langkah 3). Cara termudah untuk mencatat status file adalah dengan stempel waktu terakhir diubahnya. Misalnya, berikut contoh cara Anda dapat membaca dan membandingkan stempel waktu dari 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
    }
    

    Jika tidak ada yang berubah dan Anda tidak perlu melakukan pencadangan, lanjutkan ke langkah 3.

  2. Jika data Anda berubah, dibandingkan dengan oldState, tulis data saat ini ke data untuk mencadangkannya ke penyimpanan cloud.

    Anda harus menulis setiap bagian data sebagai entitas dalam BackupDataOutput. Entitas adalah rekaman data biner yang disederhanakan yang diidentifikasi dengan string kunci unik. Jadi, set data yang Anda cadangkan secara konseptual adalah set key-value pair.

    Untuk menambahkan entitas ke set data cadangan, Anda harus:

    1. Memanggil writeEntityHeader(), yang meneruskan kunci string unik untuk data yang akan Anda tulis beserta ukuran datanya.

    2. Memanggil writeEntityData(), yang meneruskan buffer byte berisi data dan jumlah byte yang akan ditulis dari buffer, yang seharusnya sesuai dengan ukuran yang diteruskan ke writeEntityHeader().

    Misalnya, kode berikut menyederhanakan data tertentu menjadi aliran byte dan menulisnya menjadi satu entitas:

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

    Lakukan ini untuk setiap bagian data yang ingin Anda cadangkan. Cara Anda membagi data menjadi beberapa entitas bergantung pada Anda. Anda bahkan dapat menggunakan satu entitas saja.

  3. Baik Anda melakukan pencadangan maupun tidak (dalam langkah 2), tulis representasi data saat ini ke newState ParcelFileDescriptor. Pengelola Pencadangan mempertahankan objek ini secara lokal sebagai representasi data yang saat ini sedang dicadangkan. Pengelola Pencadangan mengembalikan objek tersebut kepada Anda sebagai oldState saat onBackup() dipanggil lagi, sehingga Anda dapat menentukan apakah pencadangan lainnya diperlukan sebagaimana dijelaskan di langkah 1. Jika Anda tidak menulis status data saat ini ke file ini, oldState akan kosong selama callback berikutnya.

    Contoh berikut menyimpan representasi data saat ini ke newState menggunakan stempel waktu terakhir diubah file tersebut:

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

Melakukan pemulihan

Jika waktu untuk memulihkan data aplikasi Anda sudah tiba, Pengelola Pencadangan memanggil metode onRestore() agen pencadangan Anda. Saat memanggil metode ini, Pengelola Pencadangan akan mengirim data cadangan Anda, sehingga Anda dapat memulihkannya ke perangkat.

Hanya Pengelola Pencadangan yang dapat memanggil onRestore(), yang terjadi otomatis saat sistem menginstal aplikasi Anda dan menemukan data cadangan yang ada.

Saat Pengelola Pencadangan memanggil metode onRestore() Anda, metode ini akan meneruskan tiga parameter:

data
Objek BackupDataInput, yang memungkinkan Anda membaca data cadangan.
appVersionCode
Bilangan bulat yang merepresentasikan nilai atribut manifes android:versionCode aplikasi Anda, seperti saat data ini dicadangkan. Anda dapat menggunakannya untuk memeriksa versi aplikasi saat ini dan menentukan apakah format data tersebut kompatibel. Untuk informasi selengkapnya tentang cara menggunakan ini guna menangani versi data pemulihan yang berbeda, lihat Memeriksa versi data pemulihan.
newState
ParcelFileDescriptor baca/tulis terbuka yang mengarah ke file tempat Anda harus menulis status pencadangan terakhir yang diberikan dengan data. Objek ini ditampilkan sebagai oldState saat onBackup() dipanggil lagi. Pastikan Anda juga harus menulis objek newState yang sama di callback onBackup()—menuliskannya di sini akan memastikan objek oldState yang diberikan ke onBackup() valid bahkan saat onBackup() pertama kali dipanggil setelah perangkat dipulihkan.

Saat mengimplementasikan onRestore(), Anda harus memanggil readNextHeader() di data untuk mengiterasi semua entitas dalam set data. Untuk setiap entitas yang ditemukan, lakukan hal berikut:

  1. Mendapatkan kunci entitas dengan getKey().
  2. Bandingkan kunci entitas dengan daftar nilai kunci yang diketahui yang harus Anda deklarasikan sebagai string final statis dalam class BackupAgent. Bila kunci cocok dengan salah satu string kunci yang diketahui, masukkan ke dalam pernyataan untuk mengekstrak data entitasnya, lalu simpan ke perangkat:

    1. Dapatkan ukuran data entitas dengan getDataSize() dan buat array byte dari ukuran tersebut.
    2. Panggil readEntityData() dan teruskan array byte, yakni tempat tujuan data. Lalu, tentukan offset awal dan ukuran untuk dibaca.
    3. Array byte Anda kini penuh. Baca data dan tulis ke perangkat sesuai keinginan Anda.
  3. Setelah membaca dan menulis data kembali ke perangkat, tulis status data Anda ke parameter newState, sama seperti yang Anda lakukan selama onBackup().

Misalnya, berikut cara Anda dapat memulihkan data yang dicadangkan dengan contoh di bagian sebelumnya:

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

Dalam contoh ini, parameter appVersionCode yang diteruskan ke onRestore() tidak digunakan. Namun, Anda mungkin ingin menggunakannya jika telah memilih untuk melakukan pencadangan saat versi aplikasi pengguna benar-benar telah di-downgrade (misalnya, pengguna beralih dari aplikasi versi 1.5 ke 1.0). Untuk informasi selengkapnya, lihat bagian berikutnya.

Memeriksa versi data pemulihan

Saat Pengelola pencadangan menyimpan data Anda ke penyimpanan cloud, versi aplikasi akan otomatis disertakan, seperti yang ditentukan oleh atribut android:versionCode file manifes Anda. Sebelum memanggil agen pencadangan Anda untuk memulihkan data, Pengelola Pencadangan akan melihat android:versionCode aplikasi terinstal dan membandingkannya dengan nilai yang dicatat dalam set data pemulihan. Jika versi yang dicatat dalam set data pemulihan lebih baru daripada versi aplikasi di perangkat, maka pengguna telah men-downgrade versi aplikasinya. Dalam kasus ini, Pengelola Pencadangan akan membatalkan operasi pemulihan untuk aplikasi Anda dan tidak akan memanggil metode onRestore(), karena set pemulihan dianggap tidak dapat digunakan untuk versi yang lebih lama.

Anda dapat mengganti perilaku ini dengan atribut android:restoreAnyVersion. Setel atribut ini ke true untuk menunjukkan bahwa Anda ingin memulihkan aplikasi, apa pun versi set pemulihannya. Nilai defaultnya adalah false. Jika Anda menyetelnya ke true, Pengelola Pencadangan akan mengabaikan android:versionCode dan memanggil metode onRestore() Anda dalam semua kasus. Dengan begitu, Anda dapat memeriksa perbedaan versi secara manual di metode onRestore() dan melakukan langkah yang diperlukan untuk membuat data kompatibel jika versi tidak cocok.

Untuk membantu menangani versi yang berbeda selama operasi pemulihan, metode onRestore() meneruskan kode versi yang disertakan dengan set data pemulihan sebagai parameter appVersionCode. Lalu, Anda dapat mengkueri kode versi aplikasi saat ini dengan kolom PackageInfo.versionCode. Contoh:

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

Lalu, bandingkan version yang diperoleh dari PackageInfo ke appVersionCode yang diteruskan ke onRestore().

Meminta pencadangan

Anda dapat meminta operasi pencadangan kapan saja dengan memanggil dataChanged(). Metode ini memberi tahu Pengelola Pencadangan bahwa Anda ingin mencadangkan data menggunakan agen pencadangan. Selanjutnya, Pengelola Pencadangan akan memanggil metode onBackup() agen pencadangan Anda di waktu mendatang. Biasanya, Anda harus meminta pencadangan setiap kali data Anda berubah (misalnya saat pengguna mengubah preferensi aplikasi yang ingin Anda cadangkan). Jika Anda memanggil dataChanged() beberapa kali sebelum Pengelola Pencadangan meminta pencadangan dari agen Anda, agen Anda akan tetap menerima satu panggilan saja untuk onBackup().

Meminta pemulihan

Selama aplikasi Anda berfungsi dengan normal, Anda tidak perlu meminta operasi pemulihan. Sistem akan otomatis memeriksa data pencadangan dan melakukan pemulihan saat aplikasi Anda diinstal.

Bermigrasi ke Pencadangan Otomatis

Anda dapat melakukan transisi aplikasi ke pencadangan data menyeluruh dengan menyetel android:fullBackupOnly ke true dalam elemen <application> di file manifes. Saat berjalan di perangkat yang menggunakan Android 5.1 (API level 22) atau yang lebih lama, aplikasi Anda mengabaikan nilai ini dalam manifes, dan tetap melakukan pencadangan nilai kunci. Saat berjalan di perangkat yang menggunakan Android 6.0 (API level 23) atau yang lebih baru, aplikasi Anda melakukan Pencadangan Otomatis, bukan pencadangan nilai kunci.

Privasi pengguna

Di Google, kami sangat menyadari kepercayaan pengguna yang telah diberikan kepada kami dan tanggung jawab kami dalam melindungi privasi pengguna. Google secara aman mengirim data cadangan ke dan dari server Google untuk memberikan fitur pencadangan dan pemulihan. Google memperlakukan data ini sebagai informasi pribadi sesuai dengan Kebijakan Privasi Google.

Selain itu, pengguna dapat menonaktifkan fungsi pencadangan data melalui setelan pencadangan sistem Android. Saat pengguna menonaktifkan pencadangan, Android Backup Service akan menghapus semua data cadangan yang disimpan. Pengguna dapat mengaktifkan kembali pencadangan di perangkat, tetapi Android Backup Service tidak akan memulihkan data yang telah dihapus sebelumnya.