Basics of Data Backup in Android: Part 2

Introduction

 
We already know how to do a data backup on the cloud using an Android device. Let us analyze a case where the user resets his phone using a factory reset. All the third-party applications must be uninstalled and the user data might be lost and the settings as well. Although when the user reinstalls that application all the settings and saved content will be successfully retrieved. This process is completely transparent to the user and does not affect the functionality or user experience in an application.
  • During a backup operation, the Android's Backup Manager queries your application for backup data, then hands it to a backup transport that then delivers the data to the cloud storage.
     
  • During a restore and during the retrieval operation, the Backup Manager retrieves the backup data from the backup transport and returns it to your application so your application can restore the data to the device.
Note: The retrieval of data doesn't mean that it is an automatic process when the user reinstalls applications, he would get all the saved settings. However, in the present scenario, this doesn't make any sense.
 

Client-Side Component

 
The backup transport procedure purely is a client-side component of Android's backup framework, but the functionality lies in the fact that it could be customizable by the device manufacturer and service provider. The backup transport might differ from a wide range of devices and which backup transport is available on any given device is transparent to your application. 
 
The Backup Manager APIs isolate the application from the actual backup transport available on a given device. An Android application communicates with the Backup Manager using a fixed set of APIs, regardless of the underlying transport as specified above.
 

Backup Agent

 
To enable the backup in the application we must implement a backup agent. The backup agent is solely responsible for the data backup on the cloud using the backup transport. The calling of the backup transport by the agent is to call the restore point.
 
The following points must be taken care of:
  • Declare your backup agent in your manifest file with the Android: backupAgent attribute.
     
  • Register your application with a backup service. Google offers Android Backup Service as a backup service for most Android-powered devices that require registration of an application for it to work. Any other backup services available might also require you to register to store your data on their servers.

Define a Backup Agent by Either

  • Extending BackupAgent

     
    The BackupAgent class provides the central interface that your application communicates with the Backup Manager. If you extend this class directly, you must override onBackup() and on Restore() to handle the backup and restore operations for your data.
     
    Or
     
  • Extending BackupAgentHelper

     
    The BackupAgentHelper class provides a convenient wrapper around the BackupAgent class that minimizes the amount of code you need to write. In your BackupAgentHelper, you must use one or more "helper" objects that automatically backup and restore certain types of data, so that you do not need to implement onBackup() and onRestore(). Android currently provides backup helpers that will backup and restore complete files from SharedPreferences and internal storage.
Declaring Backup Agent in Manifest File
 
Now it is the very first step to declare an agent in the manifest file but before that you must declare the class name. Declare it as an android:backupAgent attribute in the <application> tag.
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"    
  3.     package="com.example.fragment"    
  4.     android:versionCode="1"    
  5.     android:versionName="1.0" >    
  6.     
  7.     <uses-sdk    
  8.         android:minSdkVersion="16"    
  9.         android:targetSdkVersion="21" />    
  10.     
  11.     <application    
  12.         android:allowBackup="true"    
  13.         android:icon="@drawable/ic_launcher"    
  14.         android:label="@string/app_name"    
  15.         android:theme="@style/AppTheme"    
  16.             
  17.         android:label="MyApplication"    
  18.         android:backupAgent="MyBackupAgent">    
  19.             
  20.            <activity    
  21.             android:name=".MainActivity"    
  22.             android:label="@string/app_name" >    
  23.             <intent-filter>    
  24.                 <action android:name="android.intent.action.MAIN" />    
  25.     
  26.                 <category android:name="android.intent.category.LAUNCHER" />    
  27.             </intent-filter>    
  28.         </activity>    
  29.     </application>    
  30.     
  31. </manifest>   
    Another attribute you might want to use is android : restoreAnyVersion. it takes a Boolean value.
     

    Registering the Android Backup Service

     
    When there is a question of the registration of a backup then Google provides a backup transport that reduces the overhead of managing the transport with the Android Backup service for most Android-powered devices running Android 2.2 or greater. For getting a Backup Service Key, register for the Android Backup Service. When you register, you will be provided a Backup Service Key and the appropriate <meta-data> XML code for your Android manifest file that you must include as a child of the <application> element. For example:
    1. <application android:label="MyApplication"    
    2.    android:backupAgent="MyBackupAgent">    
    3.    ...    
    4.    <meta-data android:name="com.google.android.backup.api_key"    
    5.    android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" />    
    6. </application>   

      Extending BackupAgent

       
      However we can extend the BackupAgent class but it is said from various sources for the greater functionality that we must extend BackupAgentHelper to use helper classes directly. However there are certain methods as shown below but instead we must extend the BackupAgent class.
      • onBackup() 
         
        The Backup Manager calls this method after you request a backup. In this method, you read your application data from the device and the data you want to back up to the Backup Manager, as described below in Performing backup. 
         
      • onRestore()
         
        The Backup Manager calls this method during a restore operation. When it calls this method, the Backup Manager delivers your backup data that you then restore to the device, as described below in Performing restore.

      Parameters es by onBackup() method

      • oldState
         
        An open, read-only ParcelFileDescriptor pointing to the last backup state provided by your application. This is not the backup data from cloud storage, but a local representation of the data that was backed up the last time onBackup(). 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
         
        A BackupDataOutput object that you use to deliver your backup data to the Backup Manager.
          
      • newState
         
        An open, read/write ParcelFileDescriptor pointing to a file in which you must write a representation of the data that you delivered to data (a representation can be as simple as the last-modified timestamp for your file). This object is returned as oldState the next time the Backup Manager calls your onBackup() method. If you do not write your backup data to newState, then oldState will point to an empty file the next time Backup Manager calls onBackup().

      Get old state input stream

       
      Code
      1. // Get the oldState input stream    
      2. FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());    
      3. DataInputStream in = new DataInputStream(instream);    
      4.     
      5. try {    
      6.     // Get the last modified timestamp from the state file and data file    
      7.     long stateModified = in.readLong();    
      8.     long fileModified = mDataFile.lastModified();    
      9.     
      10.     if (stateModified != fileModified) {    
      11.         // The file has been modified, so do a backup    
      12.         // Or the time on the device changed, so be safe and do a backup    
      13.     } else {    
      14.         // Don't back up because the file hasn't changed    
      15.         return;    
      16.     }    
      17. catch (IOException e) {    
      18.     // Unable to read state file... be safe and do a backup    
      19. }    

      Restoring the Data

       
      During the implementation of onRestore() it must use readNextHeader() on the data to iterate through all entities in the data set. 
      • Get the entity key with getKey().
         
      • Compare the entity key to a list of known key values that you should have declared as static final strings inside your BackupAgent class. When the key matches one of your known key strings, enter into a statement to extract the entity data and save it to the device.
         
      • Get the entity data size with getDataSize() and create a byte array of that size.
         
      • Call readEntityData() and it the byte array that is where the data will go and specify the start offset and the size to read.
         
      • Your byte array is now full and you can read the data and write it to the device however you like. 
      Let us have a look at the implementation of these points as shown below.
       
      Code
      1. @Override    
      2. public void onRestore(BackupDataInput data, int appVersionCode,    
      3.                       ParcelFileDescriptor newState) throws IOException {    
      4.     // There should be only one entity, but the safest    
      5.     // way to consume it is using a while loop    
      6.     while (data.readNextHeader()) {    
      7.         String key = data.getKey();    
      8.         int dataSize = data.getDataSize();    
      9.     
      10.         // If the key is ours (for saving top score). Note this key was used when    
      11.         // we wrote the backup entity header    
      12.         if (TOPSCORE_BACKUP_KEY.equals(key)) {    
      13.             // Create an input stream for the BackupDataInput    
      14.             byte[] dataBuf = new byte[dataSize];    
      15.             data.readEntityData(dataBuf, 0, dataSize);    
      16.             ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);    
      17.             DataInputStream in = new DataInputStream(baStream);    
      18.     
      19.             // Read the player name and score from the backup data    
      20.             mPlayerName = in.readUTF();    
      21.             mPlayerScore = in.readInt();    
      22.     
      23.             // Record the score on the device (to a file or something)    
      24.             recordScore(mPlayerName, mPlayerScore);    
      25.         } else {    
      26.             // We don't know this entity key. Skip it. (Shouldn't happen.)    
      27.             data.skipEntityData();    
      28.         }    
      29.     }    
      30.     
      31.     // Finally, write to the state blob (newState) that describes the restored data    
      32.     FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());    
      33.     DataOutputStream out = new DataOutputStream(outstream);    
      34.     out.writeUTF(mPlayerName);    
      35.     out.writeInt(mPlayerScore);    
      36. }   

        Summary

         
        This article explained the basics of the data backup and the classes used in the operation, like creating a restore and managing the backup data. Although this is very much transparent to the user, the complexities of the code is hidden and present lucid steps in the device.