Android Backup Service-এর মাধ্যমে কী-ভ্যালু পেয়ারের ব্যাক-আপ নেওয়া

Android Backup Service আপনার Android অ্যাপে কী-ভ্যালু ডেটার জন্য ক্লাউড স্টোরেজ ব্যাক-আপ ও রিস্টোর করার সুবিধা প্রদান করে। কী-ভ্যালু ব্যাক-আপ অপারেশন চলাকালীন, অ্যাপের ব্যাক-আপ ডেটা ডিভাইসের ব্যাক-আপ ট্রান্সপোর্টে পাস করা হয়। ডিভাইসটি ডিফল্ট Google ব্যাকআপ ট্রান্সপোর্ট ব্যবহার করলে, ডেটা আর্কাইভ করার জন্য Android Backup Service-এ পাঠানো হয়।

আপনার অ্যাপের প্রতি ব্যবহারকারীর জন্য ডেটা ৫ এমবিতে সীমিত। ব্যাক-আপ ডেটা স্টোর করার জন্য কোনও চার্জ নেই।

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 ম্যানিফেস্ট ফাইলে API কী <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 ফ্রেমওয়ার্ক বর্তমানে দুটি আলাদা হেল্পার প্রদান করে:

  • SharedPreferencesটি ফাইলের ব্যাক-আপ নিতে SharedPreferencesBackupHelper।
  • ইন্টার্নাল স্টোরেজ থেকে ফাইলের ব্যাক-আপ নিতে FileBackupHelper।

আপনার BackupAgentHelper-এ একাধিক হেল্পার অন্তর্ভুক্ত করতে পারেন, তবে প্রতিটি ডেটার ধরনের জন্য শুধুমাত্র একটি হেল্পার প্রয়োজন। অর্থাৎ, আপনার কাছে একাধিক SharedPreferences ফাইল থাকলে, আপনার শুধু একটি SharedPreferencesBackupHelper প্রয়োজন।

আপনার BackupAgentHelper-এ যোগ করতে চান এমন প্রতিটি হেল্পারের জন্য, আপনাকে onCreate() পদ্ধতি চলাকালীন নিম্নলিখিত কাজগুলি করতে হবে:

  1. পছন্দসই হেল্পার ক্লাসের ইনস্ট্যান্স ইনস্ট্যান্টিয়েট করুন। ক্লাস কনস্ট্রাক্টরে, আপনি যে ফাইল(গুলি) ব্যাক-আপ নিতে চান তা উল্লেখ করতে হবে।
  2. আপনার BackupAgentHelper-এ সাহায্যকারীকে যোগ করতে addHelper() কল করুন।

নিম্নলিখিত বিভাগে প্রতিটি উপলভ্য হেল্পার ব্যবহার করে কীভাবে ব্যাক-আপ এজেন্ট তৈরি করতে হয় তা বর্ণনা করা হয়েছে।

SharedPreferences ব্যাক-আপ নিন

আপনি যখন SharedPreferencesBackupHelper ইনস্ট্যান্টিয়েট করেন, তখন আপনাকে অবশ্যই এক বা একাধিক SharedPreferences ফাইলের নাম অন্তর্ভুক্ত করতে হবে।

যেমন, user_preferences নামের SharedPreferences ফাইলের ব্যাক-আপ নিতে, 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)
        }
    }
}

জাভা

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

জাভা

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

জাভা

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

জাভা

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

জাভা

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

    জাভা

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

    জাভা

    // 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-এ বর্তমান ডেটার একটি উপস্থাপনা সেভ করে:

    Kotlin

    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 হিসেবে রিটার্ন করা হয়। মনে রাখবেন যে আপনাকে onBackup() কলব্যাকে একই newState অবজেক্টও লিখতে হবে—এছাড়াও, এখানে এটি করলে তা নিশ্চিত করে যে onBackup()-কে দেওয়া oldState অবজেক্টটি বৈধ, এমনকি ডিভাইস রিস্টোর করার পরে প্রথমবার onBackup() কল করার পরেও।

onRestore()-এর প্রয়োগে, ডেটা সেটের সব এন্টিটি ইটারেট করতে, আপনাকে data-এ readNextHeader() কল করতে হবে। প্রতিটি এন্টিটি খুঁজে পাওয়ার জন্য, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  1. getKey()-এর মাধ্যমে এন্টিটি কী পান।
  2. এন্টিটি 'কী'-কে পরিচিত 'কী' ভ্যালুর তালিকার সাথে তুলনা করুন যা আপনার BackupAgent ক্লাসের মধ্যে স্ট্যাটিক ফাইনাল স্ট্রিং হিসেবে ঘোষণা করা উচিত। যখন কী আপনার পরিচিত কী স্ট্রিংয়ের সাথে মিলে যায়, তখন এন্টিটি ডেটা এক্সট্র্যাক্ট করতে এবং ডিভাইসে সেভ করতে স্টেটমেন্টে লিখুন:

    1. getDataSize() ব্যবহার করে এন্টিটি ডেটার সাইজ জানুন এবং সেই সাইজের একটি বাইট অ্যারে তৈরি করুন।
    2. readEntityData() কল করুন এবং এটিকে বাইট অ্যারে পাস করুন, যেখানে ডেটা যাবে এবং শুরুর অফসেট এবং পড়ার সাইজ নির্দিষ্ট করুন।
    3. আপনার বাইট অ্যারে এখন সম্পূর্ণ। ডেটা পড়ুন এবং আপনার পছন্দমতো ডিভাইসে লিখুন।
  3. আপনার ডেটা ডিভাইসে রিড ও রাইট করার পরে, onBackup()-এর সময় যেভাবে করেন ঠিক সেইভাবে newState প্যারামিটারে আপনার ডেটার স্ট্যাটাস রাইট করুন।

যেমন, আগের বিভাগে দেওয়া উদাহরণ অনুযায়ী ব্যাক-আপ নেওয়া ডেটা আপনি কীভাবে ফিরিয়ে আনবেন তা এখানে দেওয়া হল:

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

জাভা

@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 প্যারামিটার ব্যবহার করা হয়নি। তবে, আপনি ব্যাক-আপ নেওয়ার সিদ্ধান্ত নিলে এটি ব্যবহার করতে চাইতে পারেন যখন ব্যবহারকারীর অ্যাপের ভার্সন আসলে আগের ভার্সনে ফিরে গেছে (যেমন, ব্যবহারকারী আপনার অ্যাপের ১.৫ ভার্সন থেকে ১.০ ভার্সনে ফিরে গেছেন)। আরও তথ্যের জন্য, পরবর্তী বিভাগ দেখুন।

রিস্টোর করা ডেটার ভার্সন চেক করুন

ব্যাক-আপ ম্যানেজার আপনার ডেটা ক্লাউড স্টোরেজে সেভ করার সময়, এটি অটোমেটিক আপনার অ্যাপের ভার্সন অন্তর্ভুক্ত করে, যেমন আপনার ম্যানিফেস্ট ফাইলের android:versionCode অ্যাট্রিবিউট দ্বারা সংজ্ঞায়িত করা হয়েছে। ব্যাক-আপ ম্যানেজার আপনার ডেটা রিস্টোর করার জন্য ব্যাক-আপ এজেন্টকে কল করার আগে, এটি ইনস্টল করা অ্যাপের android:versionCode দেখে এবং রিস্টোর ডেটা সেটে রেকর্ড করা ভ্যালুর সাথে তুলনা করে। যদি রিস্টোর ডেটা সেটে রেকর্ড করা ভার্সনটি ডিভাইসে থাকা অ্যাপ ভার্সনের চেয়ে নতুন হয়, তাহলে ব্যবহারকারী তার অ্যাপের পুরনো ভার্সন ব্যবহার করছেন। এই ক্ষেত্রে, ব্যাকআপ ম্যানেজার আপনার অ্যাপের জন্য রিস্টোর অপারেশন বাতিল করবে এবং আপনার onRestore() মেথড কল করবে না, কারণ রিস্টোর সেটটি পুরনো ভার্সনের জন্য অর্থহীন বলে বিবেচিত হয়।

আপনি android:restoreAnyVersion অ্যাট্রিবিউটের মাধ্যমে এই আচরণ ওভাররাইড করতে পারবেন। আপনি যে রিস্টোর সেট ভার্সন নির্বিশেষে অ্যাপটি রিস্টোর করতে চান তা নির্দেশ করতে এই অ্যাট্রিবিউটটি true-এ সেট করুন। ডিফল্ট ভ্যালু হল false। আপনি এটি true-এ সেট করলে, ব্যাক-আপ ম্যানেজার android:versionCode উপেক্ষা করবে এবং সব ক্ষেত্রে আপনার onRestore() পদ্ধতি কল করবে। এটি করার মাধ্যমে, আপনি ম্যানুয়ালি আপনার onRestore() পদ্ধতিতে ভার্সনের পার্থক্য চেক করতে পারবেন এবং ভার্সন ম্যাচ না করলে ডেটা মানানসই করার জন্য প্রয়োজনীয় পদক্ষেপ নিতে পারবেন।

রিস্টোর অপারেশন চলাকালীন বিভিন্ন ভার্সন ম্যানেজ করতে আপনাকে সাহায্য করার জন্য, onRestore() পদ্ধতি আপনাকে রিস্টোর ডেটা সেটের সাথে অন্তর্ভুক্ত ভার্সন কোড পাস করে যা appVersionCode প্যারামিটার হিসেবে সেট করা থাকে। আপনি তারপরে PackageInfo.versionCode ফিল্ডের মাধ্যমে বর্তমান অ্যাপের ভার্সন কোড কোয়েরি করতে পারবেন। যেমন:

Kotlin

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

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

জাভা

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 (API লেভেল 22) বা তার নিচের কোনও ভার্সন আছে এমন ডিভাইসে রান করার সময়, আপনার অ্যাপ ম্যানিফেস্টে এই ভ্যালু উপেক্ষা করে এবং 'কী-ভ্যালু' ব্যাক-আপ নেওয়া চালিয়ে যায়। Android 6.0 (API লেভেল 23) বা তার পরবর্তী যেকোনও ভার্সন থাকা ডিভাইসে রান করার সময়, আপনার অ্যাপ কী-ভ্যালু ব্যাক-আপের পরিবর্তে অটোমেটিক ব্যাক-আপ পারফর্ম করে।

ব্যবহারকারীর গোপনীয়তা

Google-এ, ব্যবহারকারীরা আমাদের উপর যে বিশ্বাস রাখেন এবং ব্যবহারকারীদের গোপনীয়তা রক্ষা করার ব্যাপারে আমাদের যে দায়িত্ব রয়েছে, সেই সম্পর্কে আমরা সচেতন। ব্যাক-আপ ও রিস্টোর ফিচার প্রদান করার জন্য Google নিরাপদে ব্যাক-আপ ডেটা Google সার্ভারে পাঠায় ও সেখান থেকে গ্রহণ করে। Google-এর গোপনীয়তা নীতি অনুযায়ী Google এই ডেটাকে ব্যক্তিগত তথ্য হিসেবে বিবেচনা করে।

এছাড়াও, ব্যবহারকারীরা Android সিস্টেমের ব্যাক-আপ সেটিংসের মাধ্যমে ডেটা ব্যাক-আপ ফাংশনালিটি বন্ধ করতে পারবেন। কোনও ব্যবহারকারী ব্যাক-আপ বন্ধ করে দিলে, Android Backup Service সেভ করা সব ব্যাক-আপ ডেটা মুছে দেয়। ব্যবহারকারী ডিভাইসে ব্যাক-আপ আবার চালু করতে পারেন, কিন্তু Android Backup Service আগে মুছে ফেলা কোনও ডেটা ফিরিয়ে আনবে না।