diff options
author | Scott Main <smain@google.com> | 2010-04-29 13:26:53 -0700 |
---|---|---|
committer | Scott Main <smain@google.com> | 2010-05-14 11:47:42 -0700 |
commit | b83a283ac178ab0a72f1d811189d79b26097835e (patch) | |
tree | 2be72c9a1ca56ea65b6f35608c867bf8c21a79af /docs/html/guide | |
parent | fb473bb6118f6b82fdeda443b4fdc4ce31d3ed60 (diff) | |
download | frameworks_base-b83a283ac178ab0a72f1d811189d79b26097835e.zip frameworks_base-b83a283ac178ab0a72f1d811189d79b26097835e.tar.gz frameworks_base-b83a283ac178ab0a72f1d811189d79b26097835e.tar.bz2 |
docs: add dev guide for backup
Change-Id: I168f6b15d3441c9cbea2cd9699612476c7244530
Diffstat (limited to 'docs/html/guide')
-rw-r--r-- | docs/html/guide/guide_toc.cs | 12 | ||||
-rw-r--r-- | docs/html/guide/topics/data/backup.jd | 761 |
2 files changed, 771 insertions, 2 deletions
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 @@ <li><a href="<?cs var:toroot ?>guide/topics/intents/intents-filters.html"> <span class="en">Intents and Intent Filters</span> </a></li> - <li><a href="<?cs var:toroot ?>guide/topics/data/data-storage.html"> + <li class="toggle-list"> + <div><a href="<?cs var:toroot ?>guide/topics/data/data-storage.html"> <span class="en">Data Storage</span> - </a></li> + </a> <span class="new">new!</span></div> + <ul> + <li><a href="<?cs var:toroot ?>guide/topics/data/backup.html"> + <span class="en">Data Backup</span> + </a> <span class="new">new!</span> + </li> + </ul> + </li> <li><a href="<?cs var:toroot ?>guide/topics/providers/content-providers.html"> <span class="en">Content Providers</span> </a></li> 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 + + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>Quickview</h2> + <ul> + <li>Back up your data to the cloud in case the user looses it</li> + <li>Easily back up SharedPreferences and private files with BackupAgentHelper</li> + <li>Requires API Level 8</li> + </ul> + + <h2>In this document</h2> + <ol> + <li><a href="#Basics">The Basics</a></li> + <li><a href="#BackupAgent">Extending BackupAgent</a> + <ol> + <li><a href="#RequiredMethods">Required Methods</a></li> + <li><a href="#PerformingBackup">Performing backup</a></li> + <li><a href="#PerformingRestore">Performing restore</a></li> + </ol> + </li> + <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a> + <ol> + <li><a href="#SharedPreferences">Backing up SharedPreferences</a></li> + <li><a href="#Files">Backing up Private Files</a></li> + </ol> + </li> + <li><a href="#RestoreVersion">Checking the Restore Data Version</a></li> + <li><a href="#RequestingBackup">Requesting Backup</a></li> + <li><a href="#RequestingRestore">Requesting Restore</a></li> + </ol> + + <h2>Key classes</h2> + <ol> + <li>{@link android.app.backup.BackupManager}</li> + <li>{@link android.app.backup.BackupAgent}</li> + <li>{@link android.app.backup.BackupAgentHelper}</li> + </ol> + +</div> +</div> + +<p>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.</p> + +<p>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 <em>not</em> designed for data +synchronization (you do not have access the backup data, except during a restore operation on the +device).</p> + +<p>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.</p> + +<p class="caution"><strong>Caution:</strong> 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.</p> + + +<h2 id="Basics">The Basics</h2> + +<p>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.</p> + +<p>To implement a backup agent, you must:</p> + +<ol> + <li>Declare your backup agent in your manifest file with the <a +href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code +android:backupAgent}</a> attribute.</li> + <li>Define a backup agent by either:</p> + <ol type="a"> + <li><a href="#backupAgent">Extending BackupAgent</a> + <p>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.</p> + <p><em>Or</em></p> + <li><a href="#backupAgentHelper">Extending BackupAgentHelper</a> + <p>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()}.</p> + <p>Android currently provides backup helpers that will backup and restore complete files +from {@link android.content.SharedPreferences} and <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</p> + </li> + </ol> + </li> +</ol> + + + + +<h2 id="BackupManifest">Declaring the Backup Agent in Your Manifest</h2> + +<p>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 <a +href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code +android:backupAgent}</a> attribute in the <a +href="{@docRoot}guide/topics/manifest/application-element.html">{@code +<application>}</a> tag.</p> + +<p>For example:</p> + +<pre> +<manifest ... > + <application android:label="MyApplication" + <b>android:backupAgent="MyBackupAgent"</b>> + <activity ... > + ... + </activity> + </application> +</manifest> +</pre> + +<p>Another attribute you might want to use is <a +href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code +android:restoreAnyVersion}</a>. 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 <a +href="#RestoreVersion">Checking the Restore Data Version</a> for more information.</p> + +<p class="note"><strong>Note:</strong> 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 <a +href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a> +attribute to "8". However, if you implement proper <a +href="{@docRoot}resources/articles/backward-compatibility.html">backward compatibility</a> in +your application, you can support this feature for devices running API Level 8 or greater, while +remaining compatible with older devices.</p> + + + + + +<h2 id="BackupAgent">Extending BackupAgent</h2> + +<p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class +directly, but should instead <a href="BackupAgentHelper">extend BackupAgentHelper</a> 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:</p> +<ul> + <li>Version your data format. For instance, if you anticipate the need to revise the +format in which you write your application data, you can build a backup agent to cross-check your +application version during a restore operation and perform any necessary compatibility work if the +version on the device is different than that of the backup data. For more information, see <a +href="#RestoreVersion">Checking the Restore Data Version</a>.</li> + <li>Instead of backing up an entire file, you can specify the portions of data the should be +backed up and how each portion is then restored to the device. (This can also help you manage +different versions, because you read and write your data as unique entities, rather than +complete files.)</li> + <li>Back up data in a database. If you have an SQLite database that you want to restore when +the user re-installs your application, you need to build a custom {@link +android.app.backup.BackupAgent} that reads the appropriate data during a backup operation, then +create your table and insert the data during a restore operation.</li> +</ul> + +<p>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 <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you +should skip to <a href="BackupAgentHelper">Extending BackupAgentHelper</a>.</p> + + + +<h3 id="RequiredMethods">Required Methods</h3> + +<p>When you create a backup agent by extending {@link android.app.backup.BackupAgent}, you +must implement the following callback methods:</p> + +<dl> + <dt>{@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()}</dt> + <dd>The Backup Manager calls this method after you <a href="#RequestBackup">request a +backup</a>. 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 <a href="#PerformingBackup">Performing +backup</a>.</dd> + + <dt>{@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()}</dt> + <dd>The Backup Manager calls this method during a restore operation (you can <a +href="#RequestRestore">request a restore</a>, 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 <a +href="#PerformingRestore">Performing restore</a>.</dd> +</dl> + + + +<h3 id="PerformingBackup">Performing backup</h3> + + +<p>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.</p> + +<p>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 <a href="#RequestingBackup">Requesting +Backup</a> 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.</p> + +<p class="note"><strong>Tip:</strong> While developing your application, you can initiate an +immediate backup operation from the Backup Manager with the <a +href="{@docRoot}guide/developing/tools/bmgr.html">bmgr tool</a>.</p> + +<p>When the Backup Manager calls your {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method, it passes three parameters:</p> + +<dl> + <dt>{@code oldState}</dt> + <dd>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.</dd> + <dt>{@code data}</dt> + <dd>A {@link android.app.backup.BackupDataOutput} object, which you use to deliver your backup +data to the Backup Manager.</dd> + <dt>{@code newState}</dt> + <dd>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()}.</dd> +</dl> + +<p>Using these parameters, you should implement your {@link +android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) +onBackup()} method to do the following:</p> + +<ol> + <li>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}: + <pre> +// 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 +} +</pre> + <p>If nothing has changed and you don't need to back up, skip to step 3.</p> + </li> + <li>If your data has changed, compared to {@code oldState}, write the current data to +{@code data} to back it up to the cloud storage. + <p>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.</p> + <p>To add an entity to your backup data set, you must:</p> + <ol> + <li>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.</li> + <li>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()}).</li> + </ol> + <p>For example, the following code flattens some data into a byte stream and writes it into a +single entity:</p> + <pre> +// 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); +</pre> + <p>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).</p> + </li> + <li>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. + <p>Again, the following example saves a representation of the data using the file's +last-modified timestamp:</p> + <pre> +FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); +DataOutputStream out = new DataOutputStream(outstream); + +long modified = mDataFile.lastModified(); +out.writeLong(modified); +</pre> + </li> +</ol> + +<p class="caution"><strong>Caution:</strong> 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.</p> + + + + +<h3 id="PerformingRestore">Performing restore</h3> + +<p>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.</p> + +<p>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 <a +href="#RequestingRestore">Requesting restore</a> for more information).</p> + +<p class="note"><strong>Note:</strong> While developing your application, you can also request a +restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr +tool</a>.</p> + +<p>When the Backup Manager calls your {@link +android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) +onRestore()} method, it passes three parameters:</p> + +<dl> + <dt>{@code data}</dt> + <dd>A {@link android.app.backup.BackupDataInput}, which allows you to read your backup +data.</dd> + <dt>{@code appVersionCode}</dt> + <dd>An integer representing the value of your application's <a +href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a> +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 <a href="#RestoreVersion">Checking the Restore Data Version</a>.</dd> + <dt>{@code newState}</dt> + <dd>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.</dd> +</dl> + +<p>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:</p> + +<ol> + <li>Get the entity key with {@link android.app.backup.BackupDataInput#getKey()}.</li> + <li>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: + <ol> + <li>Get the entity data size with {@link +android.app.backup.BackupDataInput#getDataSize()} and create a byte array of that size.</li> + <li>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.</li> + <li>Your byte array is now full and you can read the data and write it to the device +however you like.</li> + </ol> + </li> + <li>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()}. +</ol> + +<div class="special"> +<p>For an example implementation of {@link android.app.backup.BackupAgent}, see the <a +href="{@docRoot}resources/samples/BackupRestore/src/com/example/android/backuprestore/ExampleAgent.html">{@code +ExampleAgent}</a> class in the <a +href="{@docRoot}}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample +application.</p> +</div> + + + + + + +<h2 id="BackupAgentHelper">Extending BackupAgentHelper</h2> + +<p>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 <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>). +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()}.</p> + +<p>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:</p> +<ul> + <li>{@link android.app.backup.SharedPreferencesBackupHelper} to backup {@link +android.content.SharedPreferences} files.</li> + <li>{@link android.app.backup.FileBackupHelper} to backup files from <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</li> +</ul> + +<p>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}.</p> + +<p>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:</p> +<ol> + <li>Instantiate in instance of the desired helper class. In the class constructor, you must +specify the appropriate file(s) you want to backup.</li> + <li>Call {@link android.app.backup.BackupAgentHelper#addHelper(String,BackupHelper) addHelper()} +to add the helper to your {@link android.app.backup.BackupAgentHelper}.</li> +</ol> + +<p>The following sections describe how to create a backup agent using each of the available +helpers.</p> + + + +<h3 id="SharedPreferences">Backing up SharedPreferences</h3> + +<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must the +name of one or more {@link android.content.SharedPreferences} files.</p> + +<p>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:</p> + +<pre> +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); + } +} +</pre> + +<p>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.</p> + +<p>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.</p> + +<p class="note"><strong>Note:</strong> {@link android.content.SharedPreferences} are threadsafe, so +you can safely read and write the shared preferences file from your backup agent and +other activities.</p> + + + +<h3 id="Files">Backing up other files</h3> + +<p>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 <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a> +(as specified by {@link android.content.ContextWrapper#getFilesDir()}, which is the same +location where {@link android.content.Context#openFileOutput(String,int) openFileOutput()} writes +files).</p> + +<p>For example, to backup two files named "scores" and "stats," a backup agent using {@link +android.app.backup.BackupAgentHelper} looks like this:</p> + +<pre> +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); + } +} +</pre> + +<p>The {@link android.app.backup.FileBackupHelper} includes all the code necessary to backup and +restore files that are saved to your application's <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>..</p> + +<p>However, reading and writing to files on internal storage is <strong>not threadsafe</strong>. 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:</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<p><strong>Interesting Fact:</strong></p> +<p>A zero-length array is lighter-weight than a normal Object, so it's great for an +intrinsic lock.</p> +</div> +</div> + +<pre> +// Object for intrinsic lock +static final Object[] sDataLock = new Object[0]; +</pre> + +<p>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:</p> + +<pre> +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"); +} +</pre> + +<p>You should synchronize your read statements with the same lock.</p> + +<p>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:</p> + +<pre> +@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); + } +} +</pre> + +<p>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.</p> + +<div class="special"> +<p>For an example implementation of {@link +android.app.backup.BackupAgentHelper} with {@link android.app.backup.FileBackupHelper}, see the +{@code FileHelperExampleAgent} class in the <a +href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample +application.</p> +</div> + + + + + + +<h2 id="RestoreVersion">Checking the Restore Data Version</h2> + +<p>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 <a +href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a> +attribute. Before the Backup Manager calls your backup agent to restore your data, it +looks at the <a +href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code +android:versionCode}</a> 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 +<em>newer</em> 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.</p> + +<p>You can override this behavior with the <a +href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code +android:restoreAnyVersion}</a> 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 <a +href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a> +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.</p> + +<p>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:</p> + +<pre> +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; +} +</pre> + +<p>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()}. +</p> + +<p class="caution"><strong>Caution:</strong> Be certain you understand the consequences of setting +<a href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code +android:restoreAnyVersion}</a> 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.</p> + + + +<h2 id="RequestingBackup">Requesting Backup</h2> + +<p>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()}.</p> + +<p class="note"><strong>Note:</strong> While developing your application, you can request a +backup and initiate an immediate backup operation with the <a +href="{@docRoot}guide/developing/tools/bmgr.html">bmgr +tool</a>.</p> + + +<h2 id="RequestingRestore">Requesting Restore</h2> + +<p>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.</p> + +<p class="note"><strong>Note:</strong> While developing your application, you can request a +restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr +tool</a>.</p> + |