From b83a283ac178ab0a72f1d811189d79b26097835e Mon Sep 17 00:00:00 2001 From: Scott Main Date: Thu, 29 Apr 2010 13:26:53 -0700 Subject: docs: add dev guide for backup Change-Id: I168f6b15d3441c9cbea2cd9699612476c7244530 --- docs/html/guide/guide_toc.cs | 12 +- docs/html/guide/topics/data/backup.jd | 761 ++++++++++++++++++++++++++++++++++ 2 files changed, 771 insertions(+), 2 deletions(-) create mode 100644 docs/html/guide/topics/data/backup.jd (limited to 'docs') diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 51ca3cc..19f0f1d 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -144,9 +144,17 @@
  • Intents and Intent Filters
  • -
  • +
  • +
    Data Storage -
  • + new! + +
  • Content Providers
  • diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd new file mode 100644 index 0000000..98f7a4f --- /dev/null +++ b/docs/html/guide/topics/data/backup.jd @@ -0,0 +1,761 @@ +page.title=Data Backup +@jd:body + + +
    +
    + +

    Quickview

    +
      +
    • Back up your data to the cloud in case the user looses it
    • +
    • Easily back up SharedPreferences and private files with BackupAgentHelper
    • +
    • Requires API Level 8
    • +
    + +

    In this document

    +
      +
    1. The Basics
    2. +
    3. Extending BackupAgent +
        +
      1. Required Methods
      2. +
      3. Performing backup
      4. +
      5. Performing restore
      6. +
      +
    4. +
    5. Extending BackupAgentHelper +
        +
      1. Backing up SharedPreferences
      2. +
      3. Backing up Private Files
      4. +
      +
    6. +
    7. Checking the Restore Data Version
    8. +
    9. Requesting Backup
    10. +
    11. Requesting Restore
    12. +
    + +

    Key classes

    +
      +
    1. {@link android.app.backup.BackupManager}
    2. +
    3. {@link android.app.backup.BackupAgent}
    4. +
    5. {@link android.app.backup.BackupAgentHelper}
    6. +
    + +
    +
    + +

    Android's {@link android.app.backup backup} service allows you to copy your persistent +application data to a remote "cloud" storage, in order to provide a restore point for the +application data and settings. If a user performs a factory reset or converts to a new +Android-powered device, the system automatically restores your backup data when the application +is re-installed. This way, your users are not required to reproduce their previous data or +application settings. This process is completely transparent to the user and does not affect the +functionality or user experience in your application.

    + +

    Android-powered devices that support the backup service provide a cloud storage area that +saves your backup data and a backup transport that delivers your data to +the storage area and back to the device. During a backup +operation, Android's Backup Manager requests backup data from your application, then delivers it to +the cloud storage using the backup transport. During a restore operation, the Backup Manager +retrieves the backup data from the backup transport and returns it to your application +so it can restore the data to the device. The backup service is not designed for data +synchronization (you do not have access the backup data, except during a restore operation on the +device).

    + +

    The cloud storage used for backup won't necessarily be the same on all Android-powered devices. +The cloud storage and backup transport may differ between devices and service providers. +Where the backup data is stored is transparent to your application, but you are assured that your +application data cannot be read by other applications.

    + +

    Caution: Because the cloud storage and transport service can +differ from device to device, Android makes no guarantees about the security of your data while +using backup. You should be cautious about using backup to store sensitive data, such as usernames +and passwords.

    + + +

    The Basics

    + +

    To backup your application data, you need to implement a backup agent. Your backup +agent is called by the Backup Manager to provide the data you want to back up. It is also called +to restore your backup data when the application is re-installed. The Backup Manager handles all +your data transactions with the cloud storage and your backup agent handles all your data +transactions on the device.

    + +

    To implement a backup agent, you must:

    + +
      +
    1. Declare your backup agent in your manifest file with the {@code +android:backupAgent} attribute.
    2. +
    3. Define a backup agent by either:

      +
        +
      1. Extending BackupAgent +

        The {@link android.app.backup.BackupAgent} class provides the central interface with +which your application communicates with the Backup Manager. If you extend this class +directly, you must override {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} and {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()} to handle the backup and restore operations for your data.

        +

        Or

        +
      2. Extending BackupAgentHelper +

        The {@link android.app.backup.BackupAgentHelper} class provides a convenient +wrapper around the {@link android.app.backup.BackupAgent} class, which minimizes the amount of code +you need to write. In your {@link android.app.backup.BackupAgentHelper}, you must use one or more +"helper" objects, which automatically backup and restore certain types of data, so that you do not +need to implement {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} and {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}.

        +

        Android currently provides backup helpers that will backup and restore complete files +from {@link android.content.SharedPreferences} and internal storage.

        +
      3. +
      +
    4. +
    + + + + +

    Declaring the Backup Agent in Your Manifest

    + +

    This is the easiest step, so once you've decided on the class name for your backup agent, declare +it in your manifest with the {@code +android:backupAgent} attribute in the {@code +<application>} tag.

    + +

    For example:

    + +
    +<manifest ... >
    +    <application android:label="MyApplication"
    +                 android:backupAgent="MyBackupAgent">
    +        <activity ... >
    +            ...
    +        </activity>
    +    </application>
    +</manifest>
    +
    + +

    Another attribute you might want to use is {@code +android:restoreAnyVersion}. This attribute takes a boolean value to indicate whether you +want to restore the application data regardless of the current application version compared to the +version that produced the backup data. (The default value is "{@code false}".) See Checking the Restore Data Version for more information.

    + +

    Note: The backup service and the APIs you must use are +available only on devices running API Level 8 (Android 2.2) or greater, so you should also +set your {@code android:minSdkVersion} +attribute to "8". However, if you implement proper backward compatibility in +your application, you can support this feature for devices running API Level 8 or greater, while +remaining compatible with older devices.

    + + + + + +

    Extending BackupAgent

    + +

    Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class +directly, but should instead extend BackupAgentHelper to take +advantage of the built-in helper classes that automatically backup and restore your files. However, +you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:

    + + +

    If you don't need to perform any of the tasks above and want to back up complete files from +{@link android.content.SharedPreferences} or internal storage, you +should skip to Extending BackupAgentHelper.

    + + + +

    Required Methods

    + +

    When you create a backup agent by extending {@link android.app.backup.BackupAgent}, you +must implement the following callback methods:

    + +
    +
    {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()}
    +
    The Backup Manager calls this method after you request a +backup. In this method, you read your application data from the device and pass the data you +want to back up to the Backup Manager, as described below in Performing +backup.
    + +
    {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}
    +
    The Backup Manager calls this method during a restore operation (you can request a restore, but the system automatically performs restore when the +user re-installs your application). When it calls this method, the Backup Manager delivers your +backup data, which you then restore to the device, as described below in Performing restore.
    +
    + + + +

    Performing backup

    + + +

    When it's time to back up your application data, the Backup Manager calls your {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method. This is where you must provide your application data to the Backup Manager so +it can be saved to cloud storage.

    + +

    Only the Backup Manager can call your backup agent's {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method. Each time that your application data changes and you want to perform a backup, +you must request a backup operation by calling {@link +android.app.backup.BackupManager#dataChanged()} (see Requesting +Backup for more information). A backup request does not result in an immediate call to your +{@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method. Instead, the Backup Manager waits for an appropriate time, then performs +backup for all applications that have requested a backup since the last backup was performed.

    + +

    Tip: While developing your application, you can initiate an +immediate backup operation from the Backup Manager with the bmgr tool.

    + +

    When the Backup Manager calls your {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method, it passes three parameters:

    + +
    +
    {@code oldState}
    +
    An open, read-only {@link android.os.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 {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} was called (as defined by {@code newState}, below, or from {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}—more about this in the next section). Because {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +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.
    +
    {@code data}
    +
    A {@link android.app.backup.BackupDataOutput} object, which you use to deliver your backup +data to the Backup Manager.
    +
    {@code newState}
    +
    An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which +you must write a representation of the data that you delivered to {@code data} (a representation +can be as simple as the last-modified timestamp for your file). This object is +returned as {@code oldState} the next time the Backup Manager calls your {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method. If you do not write your backup data to {@code newState}, then {@code oldState} +will point to an empty file next time Backup Manager calls {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()}.
    +
    + +

    Using these parameters, you should implement your {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method to do the following:

    + +
      +
    1. Check whether your data has changed since the last backup by comparing {@code oldState} to +your current data. How you read data in {@code oldState} depends on how you originally wrote it to +{@code newState} (see step 3). The easiest way to record the state of a file is with its +last-modified timestamp. For example, here's how you can read and compare a timestamp from {@code +oldState}: +
      +// 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 = mDataFile.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
      +}
      +
      +

      If nothing has changed and you don't need to back up, skip to step 3.

      +
    2. +
    3. If your data has changed, compared to {@code oldState}, write the current data to +{@code data} to back it up to the cloud storage. +

      You must write each chunk of data as an "entity" in the {@link +android.app.backup.BackupDataOutput}. An entity is a flattened binary data +record that is identified by a unique key string. Thus, the data set that you back up is +conceptually a set of key-value pairs.

      +

      To add an entity to your backup data set, you must:

      +
        +
      1. Call {@link android.app.backup.BackupDataOutput#writeEntityHeader(String,int) +writeEntityheader()}, passing a unique string key for the data you're about to write and the data +size.
      2. +
      3. Call {@link android.app.backup.BackupDataOutput#writeEntityData(byte[],int) +writeEntityData()}, passing a byte buffer that contains your data and the number of bytes to write +from the buffer (which should match the size passed to {@link +android.app.backup.BackupDataOutput#writeEntityHeader(String,int) writeEntityHeader()}).
      4. +
      +

      For example, the following code flattens some data into a byte stream and writes it into a +single entity:

      +
      +// Create buffer stream and data output stream for our data
      +ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
      +DataOutputStream outWriter = new DataOutputStream(bufStream);
      +// Write structured data
      +outWriter.writeString(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);
      +
      +

      Perform this for each piece of data that you want to back up. How you divide your data into +entities is up to you (and you might use just one entity).

      +
    4. +
    5. Whether or not you perform a backup (in step 2), write a representation of the current data to +the {@code newState} {@link android.os.ParcelFileDescriptor}. The Backup Manager retains this object +locally as a representation of the data that is currently backed up. It passes this back to you as +{@code oldState} the next time it calls {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} so you can determine whether another backup is necessary (as handled in step 1). If you +do not write the current data state to this file, then +{@code oldState} will be empty during the next callback. +

      Again, the following example saves a representation of the data using the file's +last-modified timestamp:

      +
      +FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
      +DataOutputStream out = new DataOutputStream(outstream);
      +
      +long modified = mDataFile.lastModified();
      +out.writeLong(modified);
      +
      +
    6. +
    + +

    Caution: If your application data is saved to a file, make sure +that you use synchronized statements while accessing the file so that your backup agent does not +read the file while an Activity in your application is also writing the file.

    + + + + +

    Performing restore

    + +

    When it's time to restore your application data, the Backup Manager calls your backup +agent's {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()} method. When it calls this method, the Backup Manager delivers your backup data so +you can restore it onto the device.

    + +

    Only the Backup Manager can call {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}, which happens automatically when the system installs your application and +finds existing backup data. However, you can request a restore operation for +your application by calling {@link +android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} (see Requesting restore for more information).

    + +

    Note: While developing your application, you can also request a +restore operation with the bmgr +tool.

    + +

    When the Backup Manager calls your {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()} method, it passes three parameters:

    + +
    +
    {@code data}
    +
    A {@link android.app.backup.BackupDataInput}, which allows you to read your backup +data.
    +
    {@code appVersionCode}
    +
    An integer representing the value of your application's {@code android:versionCode} +manifest attribute, as it was when this data was backed up. You can use this to cross-check the +current application version and determine if the data format is compatible. For more +information about using this to handle different versions of restore data, see the section +below about Checking the Restore Data Version.
    +
    {@code newState}
    +
    An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which +you must write the final backup state that was provided with {@code data}. This object is +returned as {@code oldState} the next time {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} is called. Recall that you must also write the same {@code newState} object in the +{@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} callback—also doing it here ensures that the {@code oldState} object given to +{@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} is valid even the first time {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} is called after the device is restored.
    +
    + +

    In your implementation of {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} to iterate +through all entities in the data set. For each entity found, do the following:

    + +
      +
    1. Get the entity key with {@link android.app.backup.BackupDataInput#getKey()}.
    2. +
    3. Compare the entity key to a list of known key values that you should have declared as static +final strings inside your {@link android.app.backup.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: +
        +
      1. Get the entity data size with {@link +android.app.backup.BackupDataInput#getDataSize()} and create a byte array of that size.
      2. +
      3. Call {@link android.app.backup.BackupDataInput#readEntityData(byte[],int,int) +readEntityData()} and pass it the byte array, which is where the data will go, and specify the +start offset and the size to read.
      4. +
      5. Your byte array is now full and you can read the data and write it to the device +however you like.
      6. +
      +
    4. +
    5. After you read and write your data back to the device, write the state of your data to the +{@code newState} parameter the same as you do during {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()}. +
    + +
    +

    For an example implementation of {@link android.app.backup.BackupAgent}, see the {@code +ExampleAgent} class in the Backup and Restore sample +application.

    +
    + + + + + + +

    Extending BackupAgentHelper

    + +

    You should build your backup agent using {@link android.app.backup.BackupAgentHelper} if you want +to back up complete files (from either {@link android.content.SharedPreferences} or internal storage). +Building your backup agent with {@link android.app.backup.BackupAgentHelper} requires far less +code than extending {@link android.app.backup.BackupAgent}, because you don't have to implement +{@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} and {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}.

    + +

    Your implementation of {@link android.app.backup.BackupAgentHelper} must +use one or more backup helpers. A backup helper is a specialized +component that {@link android.app.backup.BackupAgentHelper} summons to perform backup and +restore operations for a particular type of data. The Android framework currently provides two +different helpers:

    + + +

    You can include multiple helpers in your {@link android.app.backup.BackupAgentHelper}, but only +one helper is needed for each data type. That is, if you have multiple {@link +android.content.SharedPreferences} files, then you need only one {@link +android.app.backup.SharedPreferencesBackupHelper}.

    + +

    For each helper you want to add to your {@link android.app.backup.BackupAgentHelper}, you must do +the following during your {@link android.app.backup.BackupAgent#onCreate()} method:

    +
      +
    1. Instantiate in instance of the desired helper class. In the class constructor, you must +specify the appropriate file(s) you want to backup.
    2. +
    3. Call {@link android.app.backup.BackupAgentHelper#addHelper(String,BackupHelper) addHelper()} +to add the helper to your {@link android.app.backup.BackupAgentHelper}.
    4. +
    + +

    The following sections describe how to create a backup agent using each of the available +helpers.

    + + + +

    Backing up SharedPreferences

    + +

    When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must the +name of one or more {@link android.content.SharedPreferences} files.

    + +

    For example, to back up a {@link android.content.SharedPreferences} file named +"user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks +like this:

    + +
    +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
    +    void onCreate() {
    +        SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
    +        addHelper(PREFS_BACKUP_KEY, helper);
    +    }
    +}
    +
    + +

    That's it! That's your entire backup agent. The {@link +android.app.backup.SharedPreferencesBackupHelper} includes all the code +needed to backup and restore a {@link android.content.SharedPreferences} file.

    + +

    When the Backup Manager calls {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} and {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}, {@link android.app.backup.BackupAgentHelper} calls your backup helpers to perform +backup and restore for your specified files.

    + +

    Note: {@link android.content.SharedPreferences} are threadsafe, so +you can safely read and write the shared preferences file from your backup agent and +other activities.

    + + + +

    Backing up other files

    + +

    When you instantiate a {@link android.app.backup.FileBackupHelper}, you must include the name of +one or more files that are saved to your application's internal storage +(as specified by {@link android.content.ContextWrapper#getFilesDir()}, which is the same +location where {@link android.content.Context#openFileOutput(String,int) openFileOutput()} writes +files).

    + +

    For example, to backup two files named "scores" and "stats," a backup agent using {@link +android.app.backup.BackupAgentHelper} looks like this:

    + +
    +public class MyFileBackupAgent extends BackupAgentHelper {
    +    // The name of the SharedPreferences 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
    +    void onCreate() {
    +        FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS);
    +        addHelper(FILES_BACKUP_KEY, helper);
    +    }
    +}
    +
    + +

    The {@link android.app.backup.FileBackupHelper} includes all the code necessary to backup and +restore files that are saved to your application's internal storage..

    + +

    However, reading and writing to files on internal storage is not threadsafe. To +ensure that your backup agent does not read or write your files at the same time as your activities, +you must use synchronized statements each time you perform a read or write. For example, +in any Activity where you read and write the file, you need an object to use as the intrinsic +lock for the synchronized statements:

    + + + +
    +// Object for intrinsic lock
    +static final Object[] sDataLock = new Object[0];
    +
    + +

    Then create a synchronized statement with this lock each time you read or write the files. For +example, here's a synchronized statement for writing the latest score in a game to a file:

    + +
    +try {
    +    synchronized (MyActivity.sDataLock) {
    +        File dataFile = new File({@link android.content.Context#getFilesDir()}, TOP_SCORES);
    +        RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
    +        raFile.writeInt(score);
    +    }
    +} catch (IOException e) {
    +    Log.e(TAG, "Unable to write to file");
    +}
    +
    + +

    You should synchronize your read statements with the same lock.

    + +

    Then, in your {@link android.app.backup.BackupAgentHelper}, you must override {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} and {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()} to synchronize the backup and restore operations with the same +intrinsic lock. For example, the {@code MyFileBackupAgent} example from above needs the following +methods:

    + +
    +@Override
    +public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
    +          ParcelFileDescriptor newState) throws IOException {
    +    // Hold the lock while the FileBackupHelper performs backup
    +    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);
    +    }
    +}
    +
    + +

    That's it. All you need to do is add your {@link android.app.backup.FileBackupHelper} in the +{@link android.app.backup.BackupAgent#onCreate()} method and override {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} and {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()} to synchronize read and write operations.

    + +
    +

    For an example implementation of {@link +android.app.backup.BackupAgentHelper} with {@link android.app.backup.FileBackupHelper}, see the +{@code FileHelperExampleAgent} class in the Backup and Restore sample +application.

    +
    + + + + + + +

    Checking the Restore Data Version

    + +

    When the Backup Manager saves your data to cloud storage, it automatically includes the version +of your application, as defined by your manifest file's {@code android:versionCode} +attribute. Before the Backup Manager calls your backup agent to restore your data, it +looks at the {@code +android:versionCode} of the installed application and compares it to the value +recorded in the restore data set. If the version recorded in the restore data set is +newer than the application version on the device, then the user has downgraded their +application. In this case, the Backup Manager will abort the restore operation for your application +and not call your {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} +method, because the restore set is considered meaningless to an older version.

    + +

    You can override this behavior with the {@code +android:restoreAnyVersion} attribute. This attribute is either "{@code true}" or "{@code +false}" to indicate whether you want to restore the application regardless of the restore set +version. The default value is "{@code false}". If you define this to be "{@code true}" then the +Backup Manager will ignore the {@code android:versionCode} +and call your {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} +method in all cases. In doing so, you can manually check for the version difference in your {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} +method and take any steps necessary to make the data compatible if the versions conflict.

    + +

    To help you handle different versions during a restore operation, the {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} +method passes you the version code included with the restore data set as the {@code appVersionCode} +parameter. You can then query the current application's version code with the {@link +android.content.pm.PackageInfo#versionCode PackageInfo.versionCode} field. For example:

    + +
    +PackageInfo info;
    +try {
    +    String name = {@link android.content.ContextWrapper#getPackageName() getPackageName}();
    +    info = {@link android.content.ContextWrapper#getPackageManager
    +getPackageManager}().{@link android.content.pm.PackageManager#getPackageInfo(String,int)
    +getPackageInfo}(name,0);
    +} catch (NameNotFoundException nnfe) {
    +    info = null;
    +}
    +
    +int version;
    +if (info != null) {
    +    version = info.versionCode;
    +}
    +
    + +

    Then simply compare the {@code version} acquired from {@link android.content.pm.PackageInfo} +to the {@code appVersionCode} passed into {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}. +

    + +

    Caution: Be certain you understand the consequences of setting +{@code +android:restoreAnyVersion} to "{@code true}" for your application. If each version of your +application that supports backup does not properly account for variations in your data format during +{@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}, +then the data on the device could be saved in a format incompatible with the version currently +installed on the device.

    + + + +

    Requesting Backup

    + +

    You can request a backup operation at any time by calling {@link +android.app.backup.BackupManager#dataChanged()}. This method notifies the Backup Manager that you'd +like to backup your data using your backup agent. The Backup Manager then calls your backup +agent's {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method at an opportune time in the future. Typically, you should +request a backup each time your data changes (such as when the user changes an application +preference that you'd like to back up). If you call {@link +android.app.backup.BackupManager#dataChanged()} several times consecutively, before the Backup +Manager requests a backup from your agent, your agent still receives just one call to {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()}.

    + +

    Note: While developing your application, you can request a +backup and initiate an immediate backup operation with the bmgr +tool.

    + + +

    Requesting Restore

    + +

    During the normal life of your application, you shouldn't need to request a restore operation. +They system automatically checks for backup data and performs a restore when your application is +installed. However, you can manually request a restore operation by calling {@link +android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()}, if necessary. In +which case, the Backup Manager calls your {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} +implementation, passing the data from the current set of backup data.

    + +

    Note: While developing your application, you can request a +restore operation with the bmgr +tool.

    + -- cgit v1.1