پشتیبان‌گیری از جفت‌های کلید-مقدار با Android Backup Service

Android Backup Service (سرویس پشتیبان‌گیری Android) پشتیبان‌گیری و بازیابی فضای ذخیره‌سازی ابری را برای داده‌های کلید-مقدار در برنامه Android شما ارائه می‌دهد. درطول عملیات پشتیبان‌گیری کلید-مقدار، داده‌های پشتیبان‌گیری برنامه به انتقال پشتیبان‌گیری دستگاه منتقل می‌شود. اگر دستگاه از انتقال پشتیبانی Google پیش‌فرض استفاده می‌کند، داده‌ها برای بایگانی شدن به Android Backup Service ارسال می‌شود.

داده‌ها به ۵ مگابایت برای هر کاربر برنامه‌تان محدود است. برای ذخیره کردن داده‌های پشتیبان‌گیری‌شده هزینه‌ای دریافت نمی‌شود.

برای دریافت نمای کلی از گزینه‌های پشتیبان‌گیری Android و راهنمایی درباره اینکه کدام داده‌ها را باید پشتیبان‌گیری و بازیابی کنید، به نمای کلی پشتیبان‌گیری داده مراجعه کنید.

پیاده‌سازی پشتیبان‌گیری کلید-مقدار

برای پشتیبان‌گیری از داده‌های برنامه‌تان، باید عامل پشتیبان‌گیری را پیاده‌سازی کنید. «مدیر پشتیبان‌گیری» هم درطول پشتیبان‌گیری و هم درطول بازیابی، عامل پشتیبان‌گیری شما را فراخوانی می‌کند.

برای پیاده‌سازی عامل پشتیبان‌گیری، باید:

  1. عامل پشتیبان‌گیری را در فایل مانیفست با مشخصه android:backupAgent اعلام کنید.

  2. با انجام یکی از کارهای زیر، عامل پشتیبان‌گیری را تعریف کنید:

    • درحال گسترش دادن BackupAgent

      کلاس BackupAgent میانای مرکزی را ارائه می‌دهد که برنامه‌تان برای برقراری ارتباط با «مدیر پشتیبان‌گیری» از آن استفاده می‌کند. اگر این کلاس را مستقیماً گسترش دهید، باید onBackup() و onRestore() را لغو کنید تا عملیات پشتیبان‌گیری و بازیابی داده‌هایتان را مدیریت کنید.

    • درحال گسترش دادن BackupAgentHelper

      کلاس BackupAgentHelper یک پوشاننده مناسب برای کلاس BackupAgent ارائه می‌دهد، و مقدار کدی را که باید بنویسید به حداقل می‌رساند. در BackupAgentHelper، باید از یک یا چند شیء کمکی استفاده کنید که به‌طور خودکار انواع خاصی از داده‌ها را پشتیبان‌گیری و بازیابی می‌کنند تا نیازی به پیاده‌سازی onBackup() و onRestore() نداشته باشید. توصیه می‌کنیم برای مدیریت پشتیبان‌گیری‌های برنامه‌تان از BackupAgentHelper استفاده کنید، مگر اینکه به کنترل کامل بر پشتیبان‌گیری‌های برنامه‌تان نیاز داشته باشید.

      درحال‌حاضر Android دستیارهای پشتیبان‌گیری‌ای ارائه می‌دهد که فایل‌های کامل را از SharedPreferences و فضای ذخیره‌سازی داخلی پشتیبان‌گیری و بازیابی می‌کنند.

اعلام کردن عامل پشتیبان‌گیری در مانیفست

وقتی درباره نام کلاس برای عامل پشتیبان‌گیری‌تان تصمیم گرفتید، آن را در مانیفست خود بااستفاده از ویژگی 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>

برای پشتیبانی از دستگاه‌های قدیمی‌تر، توصیه می‌کنیم کلید میانای برنامه‌سازی کاربردی <meta-data> را به فایل مانیفست Android خود اضافه کنید. «سرویس پشتیبان‌گیری Android» دیگر به کلید سرویس نیاز ندارد، اما برخی‌از دستگاه‌های قدیمی‌تر ممکن است همچنان هنگام پشتیبان‌گیری کلید را بررسی کنند. android:name را روی com.google.android.backup.api_key و android:value را روی unused تنظیم کنید.

مشخصه android:restoreAnyVersion مقدار بولی می‌گیرد تا نشان دهد آیا می‌خواهید داده‌های برنامه را بدون درنظر گرفتن نسخه فعلی برنامه درمقایسه با نسخه‌ای که داده‌های پشتیبان را تولید کرده است بازیابی کنید یا نه. مقدار پیش‌فرض false است. برای اطلاعات بیشتر، به بررسی نسخه داده‌های بازیابی مراجعه کنید.

گسترش BackupAgentHelper

اگر می‌خواهید فایل‌های کامل را از SharedPreferences یا حافظه داخلی پشتیبان‌گیری کنید، باید عامل پشتیبان‌گیری‌تان را بااستفاده از BackupAgentHelper بسازید. ساختن عامل پشتیبان‌گیری با BackupAgentHelper به کد بسیار کمتری نسبت به گسترش BackupAgent نیاز دارد، زیرا لازم نیست onBackup() و onRestore() را پیاده‌سازی کنید.

پیاده‌سازی BackupAgentHelper شما باید از یک یا چند کمک‌کننده پشتیبان‌گیری استفاده کند. کمک‌کننده پشتیبان‌گیری مؤلفه‌ای تخصصی است که BackupAgentHelper برای انجام عملیات پشتیبان‌گیری و بازیابی برای نوع خاصی از داده‌ها فراخوانی می‌کند. درحال‌حاضر چارچوب Android دو کمک‌کننده مختلف ارائه می‌دهد:

می‌توانید چند کمک‌کننده در BackupAgentHelper خود اضافه کنید، اما فقط یک کمک‌کننده برای هر نوع داده لازم است. یعنی اگر چند فایل SharedPreferences دارید، فقط به یک SharedPreferencesBackupHelper نیاز دارید.

برای هر کمک‌کننده‌ای که می‌خواهید به BackupAgentHelper اضافه کنید، باید درطول روش onCreate() کارهای زیر را انجام دهید:

  1. نمونه‌ای از کلاس کمکی موردنظر را نمونه‌سازی کنید. در سازنده کلاس، باید فایل(های) موردنظرتان را برای پشتیبان‌گیری مشخص کنید.
  2. با addHelper() تماس بگیرید تا کمک‌رسان را به BackupAgentHelper خود اضافه کنید.

بخش‌های زیر نحوه ایجاد عامل پشتیبان‌گیری را بااستفاده از هریک از کمک‌کننده‌های دردسترس شرح می‌دهد.

پشتیبان‌گیری از SharedPreferences

وقتی SharedPreferencesBackupHelper را نمونه‌سازی می‌کنید، باید نام یک یا چند فایل SharedPreferences را اضافه کنید.

برای مثال، برای پشتیبان‌گیری از فایل SharedPreferences با نام user_preferences، یک عامل پشتیبان‌گیری کامل بااستفاده از BackupAgentHelper به‌صورت زیر است:

کاتلین

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

جاوا

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() فایل‌ها را می‌نویسد.

برای مثال، برای پشتیبان‌گیری از دو فایل به‌نام scores و stats، عامل پشتیبان‌گیری بااستفاده از BackupAgentHelper به‌این‌صورت است:

کاتلین

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

جاوا

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 شامل همه کدهای لازم برای پشتیبان‌گیری و بازیابی فایل‌هایی است که در فضای ذخیره‌سازی داخلی برنامه‌تان ذخیره شده‌اند.

بااین‌حال، خواندن و نوشتن در فایل‌های حافظه داخلی ایمن از رشته نیست. برای اطمینان از اینکه عامل پشتیبان‌گیری شما فایل‌هایتان را هم‌زمان با فعالیت‌هایتان نمی‌خواند یا نمی‌نویسد، باید هربار که خواندن یا نوشتن انجام می‌دهید از عبارت‌های همگام‌سازی‌شده استفاده کنید. برای مثال، در هر فعالیتی که فایل را می‌خوانید و می‌نویسید، به شیئی نیاز دارید که به‌عنوان قفل ذاتی برای دستورات همگام‌سازی‌شده استفاده شود:

کاتلین

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

جاوا

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

سپس هربار که فایل‌ها را می‌خوانید یا می‌نویسید، با این قفل یک عبارت همگام‌سازی‌شده ایجاد کنید. برای مثال، در اینجا یک عبارت همگام‌سازی‌شده برای نوشتن آخرین امتیاز در یک بازی در یک فایل آورده شده است:

کاتلین

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

جاوا

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 از بالا به روش‌های زیر نیاز دارد:

کاتلین

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

جاوا

@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() عامل پشتیبان‌گیری شما را فراخوانی کند. هرزمان که داده‌های برنامه‌تان تغییر می‌کند و می‌خواهید پشتیبان‌گیری انجام دهید، باید با فراخوانی dataChanged() درخواست عملیات پشتیبان‌گیری کنید. برای اطلاعات بیشتر، به درخواست پشتیبان‌گیری مراجعه کنید.

نکته: هنگام توسعه برنامه‌تان، می‌توانید بااستفاده از ابزار bmgr، عملیات پشتیبان‌گیری فوری را از «مدیر پشتیبان‌گیری» آغاز کنید.

وقتی «مدیر پشتیبان‌گیری» روش onBackup() شما را فراخوانی می‌کند، سه پارامتر را ارسال می‌کند:

oldState
یک ParcelFileDescriptor باز و فقط خواندنی که به آخرین وضعیت پشتیبان‌گیری ارائه‌شده توسط برنامه شما اشاره می‌کند. این داده‌های پشتیبان‌گیری‌شده از فضای ذخیره‌سازی ابری نیست، بلکه نمایشی محلی از داده‌هایی است که آخرین‌بار هنگام فراخوانی onBackup() پشتیبان‌گیری شده‌اند، همان‌طور که توسط newState یا از onRestore() تعریف شده است. onRestore() در بخش بعدی پوشش داده شده است. Because onBackup() does not allow you to read existing backup data in the cloud storage, you can use this local representation to determine whether your data has changed since the last backup.
data
یک شیء BackupDataOutput که از آن برای ارسال داده‌های پشتیبان‌گیری به «مدیر پشتیبان‌گیری» استفاده می‌کنید.
newState
یک ParcelFileDescriptor باز، خواندن/نوشتن که به فایلی اشاره می‌کند که در آن باید نمایشی از داده‌هایی را که به data تحویل داده‌اید بنویسید. نمایش می‌تواند به‌سادگی مُهر زمان آخرین اصلاح فایل شما باشد. دفعه بعدی که «مدیر پشتیبان‌گیری» روش onBackup() شما را فراخوانی می‌کند، این شیء به‌عنوان oldState برگردانده می‌شود. اگر داده‌های پشتیبان‌گیری را در newState ننویسید، دفعه بعد که «مدیر پشتیبان‌گیری» onBackup() را فراخوانی می‌کند، oldState به فایل خالی اشاره خواهد کرد.

بااستفاده از این پارامترها، روش onBackup() خود را برای انجام کارهای زیر پیاده‌سازی کنید:

  1. با مقایسه کردن oldState با داده‌های فعلی‌تان، بررسی کنید که آیا داده‌هایتان از آخرین پشتیبان‌گیری تغییر کرده است یا نه. نحوه خواندن داده‌ها در oldState به نحوه نوشتن اولیه آن در newState بستگی دارد (مرحله ۳ را ببینید). آسان‌ترین راه برای ثبت وضعیت فایل، استفاده از مُهر زمان آخرین اصلاح آن است. برای مثال، در اینجا نحوه خواندن و مقایسه مُهر زمان از oldState آورده شده است:

    کاتلین

    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
    }

    جاوا

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

    اگر چیزی تغییر نکرده است و نیازی به پشتیبان‌گیری ندارید، به مرحله ۳ بروید.

  2. اگر داده‌هایتان درمقایسه با oldState تغییر کرده است، داده‌های فعلی را در data بنویسید تا در فضای ذخیره‌سازی ابری پشتیبان‌گیری شود.

    باید هر بخش از داده‌ها را به‌عنوان یک نهاد در BackupDataOutput بنویسید. نهاد یک سابقه داده دودویی مسطح است که با رشته کلید یکتایی شناسایی می‌شود. بنابراین، مجموعه داده‌ای که پشتیبان‌گیری می‌کنید ازنظر مفهومی مجموعه‌ای از جفت‌های کلید-مقدار است.

    برای افزودن نهاد به مجموعه داده‌های پشتیبان‌گیری، باید:

    1. writeEntityHeader() را فراخوانی کنید و کلید رشته‌ای یکتایی را برای داده‌هایی که می‌خواهید بنویسید و اندازه داده‌ها ارسال کنید.

    2. writeEntityData() را فراخوانی کنید و میان‌گیر بایتی را که حاوی داده‌های شما است و تعداد بایت‌هایی را که باید از میان‌گیر نوشته شود (که باید با اندازه ارسال‌شده به writeEntityHeader() مطابقت داشته باشد) ارسال کنید.

    برای مثال، کد زیر برخی‌از داده‌ها را به یک جریان بایت تبدیل می‌کند و آن را در یک نهاد واحد می‌نویسد:

    کاتلین

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

    جاوا

    // 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. چه پشتیبان‌گیری را انجام دهید (در مرحله ۲) چه انجام ندهید، نمایشی از داده‌های فعلی را در newState ParcelFileDescriptor بنویسید. «مدیر پشتیبان‌گیری» این شیء را به‌صورت محلی به‌عنوان نمایشی از داده‌هایی که درحال‌حاضر پشتیبان‌گیری شده‌اند حفظ می‌کند. دفعه بعد که onBackup() را فراخوانی می‌کند، این مقدار را به‌عنوان oldState به شما برمی‌گرداند تا بتوانید تعیین کنید که آیا پشتیبان‌گیری دیگری لازم است یا نه، همان‌طور که در مرحله ۱ انجام شد. اگر وضعیت داده‌های فعلی را در این فایل ننویسید، oldState درطول فراخوانی بعدی خالی خواهد بود.

    مثال زیر نمایشی از داده‌های فعلی را بااستفاده از مُهر زمان آخرین تغییر فایل در newState ذخیره می‌کند:

    کاتلین

    val modified = dataFile.lastModified()
    FileOutputStream(newState.fileDescriptor).also {
       DataOutputStream(it).apply {
           writeLong(modified)
       }
    }

    جاوا

    FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
    DataOutputStream out = new DataOutputStream(outstream);
    
    long modified = dataFile.lastModified();
    out.writeLong(modified);

انجام بازیابی

وقتی زمان بازیابی داده‌های برنامه شما فرامی‌رسد، «مدیر پشتیبان‌گیری» روش onRestore() عامل پشتیبان‌گیری شما را فراخوانی می‌کند. وقتی این روش را فراخوانی می‌کند، «مدیر پشتیبان‌گیری» داده‌های پشتیبان‌گیری‌شده را ارائه می‌دهد تا بتوانید آن را در دستگاه بازیابی کنید.

فقط «مدیر پشتیبان‌گیری» می‌تواند onRestore() را فراخوانی کند، که این کار به‌طور خودکار هنگام نصب برنامه شما توسط سیستم و پیدا کردن داده‌های پشتیبان‌گیری موجود انجام می‌شود.

وقتی «مدیر پشتیبان‌گیری» روش onRestore() شما را فراخوانی می‌کند، سه پارامتر را ارسال می‌کند:

data
شیء BackupDataInput، که به شما امکان می‌دهد داده‌های پشتیبان‌گیری‌شده را بخوانید.
appVersionCode
عدد صحیحی که نشان‌دهنده مقدار مشخصه مانیفست android:versionCode برنامه‌تان است، همان‌طور که هنگام پشتیبان‌گیری از این داده‌ها بود. می‌توانید از این برای بررسی نسخه فعلی برنامه و تعیین اینکه آیا قالب داده سازگار است یا نه استفاده کنید. برای اطلاعات بیشتر درباره استفاده از این روش برای مدیریت نسخه‌های مختلف داده‌های بازیابی، به بررسی نسخه داده‌های بازیابی مراجعه کنید.
newState
یک ParcelFileDescriptor باز، خواندن/نوشتن که به فایلی اشاره می‌کند که در آن باید وضعیت پشتیبان‌گیری نهایی را که با data ارائه شده است بنویسید. این شیء دفعه بعد که onBackup() فراخوانی شود، به‌صورت oldState برگردانده می‌شود. به‌یاد داشته باشید که باید همان شیء newState را در callback onBackup() بنویسید—انجام این کار در اینجا همچنین تضمین می‌کند که شیء oldState داده‌شده به onBackup() معتبر است، حتی اولین باری که onBackup() پس‌از بازیابی دستگاه فراخوانی می‌شود.

در پیاده‌سازی onRestore()، باید readNextHeader() را در data فراخوانی کنید تا همه نهادها در مجموعه داده تکرار شوند. برای هر نهاد پیداشده، کارهای زیر را انجام دهید:

  1. دریافت کلید نهاد با getKey().
  2. کلید نهاد را با فهرستی از مقادیر کلید شناخته‌شده که باید به‌عنوان رشته‌های نهایی ثابت در کلاس BackupAgent خود اعلام کرده باشید، مقایسه کنید. وقتی کلید با یکی‌از رشته‌های کلید شناخته‌شده شما مطابقت دارد، وارد عبارتی شوید تا داده‌های نهاد را استخراج کنید و آن را در دستگاه ذخیره کنید:

    1. اندازه داده‌های موجودیت را با getDataSize() دریافت کنید و آرایه بایتی با آن اندازه ایجاد کنید.
    2. readEntityData() را فراخوانی کنید و آرایه بایت را به آن ارسال کنید، که محل قرارگیری داده‌ها است، و انحراف شروع و اندازه خواندن را مشخص کنید.
    3. آرایه بایت شما اکنون پر است. داده‌ها را بخوانید و هرطور که دوست دارید آن‌ها را در دستگاه بنویسید.
  3. پس‌از خواندن و نوشتن داده‌هایتان در دستگاه، وضعیت داده‌هایتان را در پارامتر newState بنویسید، همان‌طور که درطول onBackup() انجام می‌دهید.

برای مثال، در اینجا نحوه بازیابی داده‌های پشتیبان‌گیری‌شده توسط مثال در بخش قبلی آورده شده است:

کاتلین

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

جاوا

@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() ارسال شده است استفاده نمی‌شود. بااین‌حال، اگر انتخاب کرده باشید که وقتی نسخه برنامه کاربر درواقع به عقب برگشته است (برای مثال، کاربر از نسخه ۱.۵ برنامه شما به نسخه ۱.۰ رفته است) پشتیبان‌گیری انجام شود، ممکن است بخواهید از آن استفاده کنید. برای اطلاعات بیشتر، بخش بعدی را ببینید.

بررسی نسخه داده‌های بازیابی

وقتی «مدیر پشتیبان‌گیری» داده‌هایتان را در فضای ذخیره‌سازی ابری ذخیره می‌کند، به‌طور خودکار نسخه برنامه‌تان را که توسط مشخصه android:versionCode فایل مانیفست شما تعریف شده است دربرمی‌گیرد. قبل‌از اینکه «مدیر پشتیبان‌گیری» برای بازیابی داده‌هایتان با عامل پشتیبان‌گیری تماس بگیرد، android:versionCode برنامه نصب‌شده را بررسی می‌کند و آن را با مقدار ثبت‌شده در مجموعه داده‌های بازیابی مقایسه می‌کند. اگر نسخه ثبت‌شده در مجموعه داده‌های بازیابی جدیدتر از نسخه برنامه در دستگاه باشد، کاربر برنامه‌اش را تنزل داده است. در این حالت، «مدیر پشتیبان‌گیری» عملیات بازیابی را برای برنامه‌تان لغو می‌کند و روش onRestore() شما را فراخوانی نمی‌کند، زیرا مجموعه بازیابی برای نسخه قدیمی‌تر بی‌معنی تلقی می‌شود.

می‌توانید این رفتار را با مشخصه android:restoreAnyVersion لغو کنید. این مشخصه را روی true تنظیم کنید تا نشان دهید که می‌خواهید برنامه را صرف‌نظر از نسخه مجموعه بازیابی، بازیابی کنید. مقدار پیش‌فرض false است. اگر این را روی true تنظیم کنید، «مدیر پشتیبان‌گیری» android:versionCode را نادیده می‌گیرد و در همه موارد روش onRestore() شما را فراخوانی می‌کند. با انجام این کار، می‌توانید به‌صورت دستی تفاوت نسخه را در روش onRestore() خود بررسی کنید و درصورت عدم تطابق نسخه‌ها، اقدامات لازم را برای سازگار کردن داده‌ها انجام دهید.

برای کمک به شما در مدیریت نسخه‌های مختلف درطول عملیات بازیابی، روش onRestore() کد نسخه موجود در مجموعه داده‌های بازیابی را به‌عنوان پارامتر appVersionCode به شما منتقل می‌کند. سپس می‌توانید کد نسخه برنامه فعلی را با فیلد PackageInfo.versionCode پرس‌وجو کنید. برای مثال:

کاتلین

val info: PackageInfo? = try {
    packageManager.getPackageInfo(packageName, 0)
} catch (e: PackageManager.NameNotFoundException) {
    null
}

val version: Int = info?.versionCode ?: 0

جاوا

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> در فایل مانیفست، برنامه‌تان را به پشتیبان‌گیری کامل داده‌ها انتقال دهید. هنگام اجرا در دستگاهی با Android نسخه ۵.۱ (سطح میانای برنامه‌سازی کاربردی ۲۲) یا پایین‌تر، برنامه‌تان این مقدار را در مانیفست نادیده می‌گیرد و به پشتیبان‌گیری کلید-مقدار ادامه می‌دهد. وقتی برنامه در دستگاهی با Android نسخه ۶.۰ (سطح میانای برنامه‌سازی کاربردی ۲۳) یا بالاتر اجرا می‌شود، به‌جای پشتیبان‌گیری مقدار کلید، «پشتیبان‌گیری خودکار» انجام می‌دهد.

حریم خصوصی کاربران

ما در Google کاملاً از اعتمادی که کاربران به ما دارند و مسئولیتمان برای محافظت از حریم خصوصی کاربران آگاه هستیم. Google داده‌های پشتیبان‌گیری را به‌طور ایمن به سرورهای Google ارسال و از آن‌ها دریافت می‌کند تا ویژگی‌های پشتیبان‌گیری و بازیابی را ارائه دهد. Google مانند اطلاعات شخصی با این داده‌ها برخورد می‌کند و این کار را مطابق با خط‌مشی رازداری Google انجام می‌دهد.

علاوه‌براین، کاربران می‌توانند عملکرد پشتیبان‌گیری داده را ازطریق تنظیمات پشتیبان‌گیری سیستم Android غیرفعال کنند. وقتی کاربر پشتیبان‌گیری را غیرفعال می‌کند، «سرویس پشتیبان‌گیری Android» همه داده‌های پشتیبان‌گیری ذخیره‌شده را حذف می‌کند. کاربر می‌تواند پشتیبان‌گیری را دوباره در دستگاه فعال کند، اما «سرویس پشتیبان‌گیری Android» هیچ‌کدام از داده‌های قبلاً حذف‌شده را بازیابی نخواهد کرد.