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

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

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

برای مروری بر گزینه‌های پشتیبان‌گیری 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>

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

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

BackupAgentHelper را گسترش دهید

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

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

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

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

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

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

از 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() در بخش بعدی پوشش داده شده است. از آنجایی که onBackup() به شما اجازه نمی‌دهد داده‌های پشتیبان موجود در فضای ذخیره‌سازی ابری را بخوانید، می‌توانید از این نمایش محلی برای تعیین اینکه آیا داده‌های شما از آخرین نسخه پشتیبان تغییر کرده است یا خیر استفاده کنید.
data
یک شی BackupDataOutput که از آن برای تحویل داده های پشتیبان خود به مدیر پشتیبان استفاده می کنید.
newState
یک ParcelFileDescriptor باز، خواندن/نوشتن که به فایلی اشاره می‌کند که در آن باید نمایشی از داده‌هایی که به data تحویل داده‌اید بنویسید. یک نمایش می تواند به سادگی آخرین مُهر زمانی اصلاح شده برای فایل شما باشد. دفعه بعد که مدیر پشتیبان گیری متد onBackup() شما را فراخوانی کرد، این شی به عنوان oldState برگردانده می شود. اگر اطلاعات پشتیبان خود را در newState ننویسید، دفعه بعد که Backup Manager onBackup() را فراخواند oldState به یک فایل خالی اشاره می کند.

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

  1. با مقایسه oldState با داده های فعلی خود بررسی کنید که آیا داده های شما از آخرین نسخه پشتیبان تغییر کرده است یا خیر. نحوه خواندن داده ها در oldState بستگی به نحوه نوشتن آن در newState دارد (مرحله 3 را ببینید). ساده ترین راه برای ثبت وضعیت یک فایل، آخرین بار اصلاح شده آن است. برای مثال، در اینجا نحوه خواندن و مقایسه مهر زمانی از 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
    }

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

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

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

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

وقتی مدیر پشتیبان‌گیری داده‌های شما را در فضای ذخیره‌سازی ابری ذخیره می‌کند، به‌طور خودکار نسخه برنامه شما را که با ویژگی 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 نسخه 5.1 (سطح API 22) یا پایین‌تر اجرا می‌شود، برنامه شما این مقدار را در مانیفست نادیده می‌گیرد و به انجام پشتیبان‌گیری با مقدار کلید ادامه می‌دهد. وقتی روی دستگاهی با Android نسخه 6.0 (سطح API 23) یا بالاتر اجرا می‌شود، برنامه شما به جای پشتیبان‌گیری با مقدار کلید، پشتیبان‌گیری خودکار انجام می‌دهد.

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

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

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