Android Backup Service की मदद से, की-वैल्यू पेयर का बैक अप लेना

Android Backup Service, आपके Android ऐप्लिकेशन में मौजूद की-वैल्यू डेटा के लिए, क्लाउड स्टोरेज में बैकअप लेने और उसे वापस पाने की सुविधा देती है. की-वैल्यू डेटा का बैकअप लेने के दौरान, ऐप्लिकेशन का बैकअप डेटा, डिवाइस के बैकअप ट्रांसपोर्ट को पास किया जाता है. अगर डिवाइस, Google Backup Transport की डिफ़ॉल्ट सेटिंग का इस्तेमाल कर रहा है, तो डेटा को संग्रहित करने के लिए Android Backup Service को भेजा जाता है.

आपके ऐप्लिकेशन के हर उपयोगकर्ता के लिए, डेटा की सीमा 5 एमबी है. बैकअप डेटा को सेव करने के लिए कोई शुल्क नहीं लिया जाता.

Android के बैकअप विकल्पों की खास जानकारी पाने और यह जानने के लिए कि आपको कौनसा डेटा बैक अप और वापस लाना चाहिए, डेटा बैकअप की खास जानकारी देखें.

की-वैल्यू बैकअप लागू करना

अपने ऐप्लिकेशन के डेटा का बैकअप लेने के लिए, आपको बैकअप एजेंट लागू करना होगा. बैकअप और रीस्टोर, दोनों के दौरान बैकअप मैनेजर आपके बैकअप एजेंट को कॉल करता है.

बैकअप एजेंट को लागू करने के लिए, आपको ये काम करने होंगे:

  1. अपनी मेनिफ़ेस्ट फ़ाइल में, android:backupAgent एट्रिब्यूट की मदद से बैकअप एजेंट का एलान करें.

  2. बैकअप एजेंट तय करने के लिए, इनमें से कोई एक तरीका अपनाएं:

    • BackupAgent को लंबा किया जा रहा है

      BackupAgent क्लास, मुख्य इंटरफ़ेस उपलब्ध कराती है. इसका इस्तेमाल करके, आपका ऐप्लिकेशन बैकअप मैनेजर के साथ कम्यूनिकेट करता है. अगर इस क्लास को सीधे तौर पर एक्सटेंड किया जाता है, तो आपको अपने डेटा के बैकअप और उसे वापस लाने की प्रोसेस को मैनेज करने के लिए, onBackup() और onRestore() को ओवरराइड करना होगा.

    • BackupAgentHelper की अवधि बढ़ाई जा रही है

      BackupAgentHelper क्लास, BackupAgent क्लास के आस-पास एक सुविधाजनक रैपर उपलब्ध कराती है. इससे आपको कम कोड लिखना पड़ता है. अपने BackupAgentHelper में, आपको एक या उससे ज़्यादा हेल्पर ऑब्जेक्ट का इस्तेमाल करना होगा. ये ऑब्जेक्ट, कुछ खास तरह के डेटा का बैकअप अपने-आप लेते हैं और उसे वापस लाते हैं. इससे आपको onBackup() और onRestore() को लागू करने की ज़रूरत नहीं पड़ती. जब तक आपको अपने ऐप्लिकेशन के बैकअप पर पूरा कंट्रोल न चाहिए हो, तब तक हमारा सुझाव है कि आप अपने ऐप्लिकेशन के बैकअप को मैनेज करने के लिए, BackupAgentHelper का इस्तेमाल करें.

      फ़िलहाल, Android में बैकअप हेल्पर की सुविधा उपलब्ध है. इससे SharedPreferences और इंटरनल स्टोरेज से पूरी फ़ाइलों का बैकअप लिया जा सकता है और उन्हें वापस लाया जा सकता है.

अपने मेनिफ़ेस्ट में बैकअप एजेंट के बारे में जानकारी देना

बैकअप एजेंट के लिए क्लास का नाम तय करने के बाद, <application> टैग में android:backupAgent एट्रिब्यूट का इस्तेमाल करके, अपने मेनिफ़ेस्ट में इसका एलान करें.

उदाहरण के लिए:

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

पुराने डिवाइसों पर काम करने के लिए, हमारा सुझाव है कि आप अपनी Android मेनिफ़ेस्ट फ़ाइल में एपीआई पासकोड <meta-data> जोड़ें. Android Backup Service को अब सेवा कुंजी की ज़रूरत नहीं होती. हालांकि, कुछ पुराने डिवाइसों पर बैकअप लेते समय, अब भी कुंजी की जांच की जा सकती है. 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 का इस्तेमाल करके बनाया गया पूरा बैकअप एजेंट इस तरह दिखता है:

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() फ़ाइलें सेव करता है.

उदाहरण के लिए, scores और stats नाम की दो फ़ाइलों का बैक अप लेने के लिए, 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() तरीके को कॉल कर सकता है. जब भी आपके ऐप्लिकेशन का डेटा बदलता है और आपको बैकअप लेना होता है, तो आपको dataChanged() को कॉल करके बैकअप लेने का अनुरोध करना होगा. ज़्यादा जानकारी के लिए, बैकअप का अनुरोध करना लेख पढ़ें.

अहम जानकारी: अपना ऐप्लिकेशन डेवलप करते समय, bmgr टूल की मदद से, बैकअप मैनेजर से तुरंत बैकअप लेने की प्रोसेस शुरू की जा सकती है.

जब बैकअप मैनेजर आपके onBackup() तरीके को कॉल करता है, तो वह तीन पैरामीटर पास करता है:

oldState
एक खुला, रीड-ओनली ParcelFileDescriptor, जो आपके ऐप्लिकेशन से उपलब्ध कराए गए आखिरी बैकअप की स्थिति को दिखाता है. यह क्लाउड स्टोरेज से लिया गया बैकअप डेटा नहीं है, बल्कि उस डेटा का लोकल वर्शन है जिसका बैकअप पिछली बार onBackup() को कॉल करने पर लिया गया था. जैसा कि newState या onRestore() से तय किया गया है. onRestore() के बारे में अगले सेक्शन में बताया गया है. onBackup() आपको क्लाउड स्टोरेज में मौजूद बैकअप डेटा को पढ़ने की अनुमति नहीं देता है. इसलिए, इस लोकल डेटा का इस्तेमाल करके यह तय किया जा सकता है कि पिछले बैकअप के बाद से आपके डेटा में कोई बदलाव हुआ है या नहीं.
data
BackupDataOutput ऑब्जेक्ट, जिसका इस्तेमाल बैकअप मैनेजर को बैकअप डेटा डिलीवर करने के लिए किया जाता है.
newState
एक खुली हुई, रीड-राइट ParcelFileDescriptor जो किसी ऐसी फ़ाइल पर ले जाती है जिसमें आपको उस डेटा को लिखना होगा जो आपने data को डिलीवर किया है. आपकी फ़ाइल में आखिरी बार किए गए बदलाव का टाइमस्टैंप, एक तरह का ई-टैग हो सकता है. अगली बार जब बैकअप मैनेजर आपके onBackup() तरीके को कॉल करेगा, तो यह ऑब्जेक्ट oldState के तौर पर दिखाया जाएगा. अगर आपने बैकअप डेटा को newState में नहीं लिखा, तो अगली बार जब बैकअप मैनेजर onBackup() को कॉल करेगा, तब oldState एक खाली फ़ाइल पर ले जाएगा.

इन पैरामीटर का इस्तेमाल करके, onBackup() तरीके को लागू करें. इससे ये काम किए जा सकते हैं:

  1. देखें कि पिछले बैकअप के बाद से आपके डेटा में कोई बदलाव हुआ है या नहीं. इसके लिए, oldState की तुलना अपने मौजूदा डेटा से करें. oldState में डेटा को पढ़ने का तरीका इस बात पर निर्भर करता है कि आपने उसे newState में कैसे लिखा था (तीसरा चरण देखें). किसी फ़ाइल की स्थिति को रिकॉर्ड करने का सबसे आसान तरीका है, पिछली बार किए गए बदलाव का टाइमस्टैंप. उदाहरण के लिए, 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
    }
    

    अगर कोई बदलाव नहीं हुआ है और आपको बैक अप लेने की ज़रूरत नहीं है, तो तीसरे चरण पर जाएं.

  2. अगर आपका डेटा oldState की तुलना में बदल गया है, तो मौजूदा डेटा को data में सेव करें, ताकि क्लाउड स्टोरेज में उसका बैकअप लिया जा सके.

    आपको डेटा के हर हिस्से को BackupDataOutput में एक इकाई के तौर पर लिखना होगा. एंटिटी, फ़्लैट किया गया बाइनरी डेटा रिकॉर्ड होती है. इसकी पहचान, यूनीक कुंजी स्ट्रिंग से की जाती है. इसलिए, जिस डेटासेट का बैक अप लिया जाता है वह सैद्धांतिक तौर पर की-वैल्यू पेयर का एक सेट होता है.

    अपने बैकअप डेटासेट में कोई इकाई जोड़ने के लिए, आपको:

    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 using 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 में सेव किया गया है. इसके लिए, फ़ाइल में पिछली बार किए गए बदलाव के टाइमस्टैंप का इस्तेमाल किया गया है:

    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
यह एक पूर्णांक है, जो आपके ऐप्लिकेशन के android:versionCode मेनिफ़ेस्ट एट्रिब्यूट की वैल्यू दिखाता है. यह वैल्यू, डेटा का बैकअप लेते समय मौजूद वैल्यू होती है. इसकी मदद से, ऐप्लिकेशन के मौजूदा वर्शन की दोबारा जांच की जा सकती है. साथ ही, यह पता लगाया जा सकता है कि डेटा फ़ॉर्मैट, ऐप्लिकेशन के साथ काम करता है या नहीं. रीस्टोर किए गए डेटा के अलग-अलग वर्शन को मैनेज करने के लिए, इस सुविधा का इस्तेमाल करने के बारे में ज़्यादा जानकारी पाने के लिए, रीस्टोर किए गए डेटा का वर्शन देखें पर जाएं.
newState
एक खुला हुआ, रीड-राइट ParcelFileDescriptor जो उस फ़ाइल पर ले जाता है जिसमें आपको बैकअप की आखिरी स्थिति लिखनी होगी. यह स्थिति data के साथ दी गई थी. अगली बार onBackup() को कॉल किए जाने पर, यह ऑब्जेक्ट oldState के तौर पर दिखाया जाता है. याद रखें कि आपको onBackup() कॉलबैक में भी वही newState ऑब्जेक्ट लिखना होगा. ऐसा करने से यह पक्का होता है कि onBackup() को दिया गया oldState ऑब्जेक्ट मान्य है. भले ही, डिवाइस को पहले जैसा करने के बाद, onBackup() को पहली बार कॉल किया गया हो.

onRestore() को लागू करते समय, आपको data पर readNextHeader() को कॉल करना चाहिए, ताकि डेटासेट में मौजूद सभी इकाइयों को दोहराया जा सके. हर इकाई के लिए, यह तरीका अपनाएं:

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

इस उदाहरण में, onRestore() को पास किए गए appVersionCode पैरामीटर का इस्तेमाल नहीं किया गया है. हालांकि, अगर आपने बैकअप लेने का विकल्प चुना है, तो हो सकता है कि आप इसका इस्तेमाल करना चाहें. ऐसा तब होता है, जब उपयोगकर्ता के ऐप्लिकेशन का वर्शन असल में पीछे चला गया हो. उदाहरण के लिए, उपयोगकर्ता आपके ऐप्लिकेशन के वर्शन 1.5 से 1.0 पर चला गया हो. ज़्यादा जानकारी के लिए, अगला सेक्शन देखें.

रीस्टोर किए गए डेटा का वर्शन देखना

जब बैकअप मैनेजर आपके डेटा को क्लाउड स्टोरेज में सेव करता है, तो वह आपके ऐप्लिकेशन के वर्शन को अपने-आप शामिल कर लेता है. यह वर्शन, आपकी मेनिफ़ेस्ट फ़ाइल के android:versionCode एट्रिब्यूट से तय होता है. बैकअप मैनेजर, डेटा वापस लाने के लिए आपके बैकअप एजेंट को कॉल करने से पहले, इंस्टॉल किए गए ऐप्लिकेशन का android:versionCode देखता है. साथ ही, इसकी तुलना डेटा वापस लाने के लिए रिकॉर्ड किए गए डेटासेट में मौजूद वैल्यू से करता है. अगर रीस्टोर डेटासेट में रिकॉर्ड किया गया वर्शन, डिवाइस पर मौजूद ऐप्लिकेशन के वर्शन से नया है, तो इसका मतलब है कि उपयोगकर्ता ने अपने ऐप्लिकेशन को डाउनग्रेड किया है. इस मामले में, बैकअप मैनेजर आपके ऐप्लिकेशन के लिए, रीस्टोर करने की प्रोसेस को रद्द कर देगा और आपके onRestore() तरीके को कॉल नहीं करेगा. ऐसा इसलिए, क्योंकि रीस्टोर सेट को पुराने वर्शन के लिए काम का नहीं माना जाता है.

android:restoreAnyVersion एट्रिब्यूट का इस्तेमाल करके, यह व्यवहार बदला जा सकता है:

  • false (डिफ़ॉल्ट): अगर डेटा वापस पाने के लिए इस्तेमाल किए गए डेटासेट में रिकॉर्ड किया गया वर्शन, डिवाइस पर मौजूद ऐप्लिकेशन के वर्शन से नया है, तो बैकअप मैनेजर डेटा वापस पाने की प्रोसेस को रोक देता है.
  • true: बैकअप मैनेजर, android:versionCode को अनदेखा करता है और सभी मामलों में आपके 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;
}

इसके बाद, PackageInfo से मिले version की तुलना, onRestore() में पास किए गए appVersionCode से करें.

बैकअप का अनुरोध करना

dataChanged() को कॉल करके, किसी भी समय बैकअप लेने का अनुरोध किया जा सकता है. इस तरीके से, बैकअप मैनेजर को सूचना मिलती है कि आपको अपने बैकअप एजेंट का इस्तेमाल करके, डेटा का बैकअप लेना है. इसके बाद, बैकअप मैनेजर आपके बैकअप एजेंट के onBackup() तरीके को कॉल करता है. आम तौर पर, आपको हर बार डेटा में बदलाव होने पर बैकअप का अनुरोध करना चाहिए. जैसे, जब उपयोगकर्ता ऐप्लिकेशन की किसी ऐसी सेटिंग में बदलाव करता है जिसका आपको बैकअप लेना है. अगर बैकअप मैनेजर आपके एजेंट से बैकअप का अनुरोध करने से पहले, dataChanged() को कई बार कॉल करता है, तो भी आपके एजेंट को onBackup() पर सिर्फ़ एक कॉल मिलेगा.

डेटा वापस पाने का अनुरोध करना

ऐप्लिकेशन के सामान्य इस्तेमाल के दौरान, आपको डेटा वापस पाने का अनुरोध करने की ज़रूरत नहीं पड़नी चाहिए. सिस्टम, बैकअप डेटा की अपने-आप जांच करता है. साथ ही, ऐप्लिकेशन इंस्टॉल होने पर डेटा को वापस लाता है.

ऑटो बैकअप की सुविधा पर माइग्रेट करना

अपने ऐप्लिकेशन को पूरे डेटा के बैकअप पर स्विच किया जा सकता है. इसके लिए, मेनिफ़ेस्ट फ़ाइल में <application> एलिमेंट में android:fullBackupOnly को true पर सेट करें. Android 5.1 (एपीआई लेवल 22) या इससे पहले के वर्शन वाले डिवाइस पर, आपका ऐप्लिकेशन मेनिफ़ेस्ट में मौजूद इस वैल्यू को अनदेखा कर देता है. साथ ही, मुख्य वैल्यू के बैकअप लेना जारी रखता है. Android 6.0 (एपीआई लेवल 23) या इसके बाद के वर्शन वाले डिवाइस पर, आपका ऐप्लिकेशन की-वैल्यू बैकअप के बजाय ऑटो बैकअप की सुविधा का इस्तेमाल करता है.

उपयोगकर्ता की निजता

हम अच्छी तरह जानते हैं कि उपयोगकर्ताओं को हम पर भरोसा है और उनकी निजता को बरकरार रखना हमारी ज़िम्मेदारी है. Google, बैकअप और डेटा वापस पाने की सुविधाएं देने के लिए, बैकअप डेटा को Google के सर्वर पर सुरक्षित तरीके से भेजता है और वहां से वापस लाता है. Google, इस डेटा को अपनी निजता नीति के मुताबिक निजी जानकारी मानता है.

इसके अलावा, उपयोगकर्ता Android सिस्टम की बैकअप सेटिंग में जाकर, डेटा बैकअप की सुविधा को बंद कर सकते हैं. जब कोई उपयोगकर्ता बैकअप की सुविधा बंद करता है, तो Android Backup Service, सेव किए गए बैकअप का पूरा डेटा मिटा देती है. उपयोगकर्ता अपने डिवाइस पर बैकअप की सुविधा को फिर से चालू कर सकता है. हालांकि, Android Backup Service, मिटाए गए किसी भी डेटा को वापस नहीं लाएगी.