| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 1 | page.title=Data Backup |
| 2 | @jd:body |
| 3 | |
| 4 | |
| 5 | <div id="qv-wrapper"> |
| 6 | <div id="qv"> |
| 7 | |
| 8 | <h2>Quickview</h2> |
| 9 | <ul> |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 10 | <li>Back up the user's data to the cloud in case the user loses it</li> |
| 11 | <li>If the user upgrades to a new Android-powered device, your app can restore the user's |
| 12 | data onto the new device</li> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 13 | <li>Easily back up SharedPreferences and private files with BackupAgentHelper</li> |
| 14 | <li>Requires API Level 8</li> |
| 15 | </ul> |
| 16 | |
| 17 | <h2>In this document</h2> |
| 18 | <ol> |
| 19 | <li><a href="#Basics">The Basics</a></li> |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 20 | <li><a href="#BackupManifest">Declaring the Backup Agent in Your Manifest</a></li> |
| 21 | <li><a href="#BackupKey">Registering for Android Backup Service</a></li> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 22 | <li><a href="#BackupAgent">Extending BackupAgent</a> |
| 23 | <ol> |
| 24 | <li><a href="#RequiredMethods">Required Methods</a></li> |
| 25 | <li><a href="#PerformingBackup">Performing backup</a></li> |
| 26 | <li><a href="#PerformingRestore">Performing restore</a></li> |
| 27 | </ol> |
| 28 | </li> |
| 29 | <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a> |
| 30 | <ol> |
| 31 | <li><a href="#SharedPreferences">Backing up SharedPreferences</a></li> |
| 32 | <li><a href="#Files">Backing up Private Files</a></li> |
| 33 | </ol> |
| 34 | </li> |
| 35 | <li><a href="#RestoreVersion">Checking the Restore Data Version</a></li> |
| 36 | <li><a href="#RequestingBackup">Requesting Backup</a></li> |
| 37 | <li><a href="#RequestingRestore">Requesting Restore</a></li> |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 38 | <li><a href="#Testing">Testing Your Backup Agent</a></li> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 39 | </ol> |
| 40 | |
| 41 | <h2>Key classes</h2> |
| 42 | <ol> |
| 43 | <li>{@link android.app.backup.BackupManager}</li> |
| 44 | <li>{@link android.app.backup.BackupAgent}</li> |
| 45 | <li>{@link android.app.backup.BackupAgentHelper}</li> |
| 46 | </ol> |
| 47 | |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 48 | <h2>See also</h2> |
| 49 | <ol> |
| 50 | <li><a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a></li> |
| 51 | </ol> |
| 52 | |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 53 | </div> |
| 54 | </div> |
| 55 | |
| 56 | <p>Android's {@link android.app.backup backup} service allows you to copy your persistent |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 57 | application data to remote "cloud" storage, in order to provide a restore point for the |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 58 | application data and settings. If a user performs a factory reset or converts to a new |
| 59 | Android-powered device, the system automatically restores your backup data when the application |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 60 | is re-installed. This way, your users don't need to reproduce their previous data or |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 61 | application settings. This process is completely transparent to the user and does not affect the |
| 62 | functionality or user experience in your application.</p> |
| 63 | |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 64 | <p>During a backup operation (which your application can request), Android's Backup Manager ({@link |
| 65 | android.app.backup.BackupManager}) queries your application for backup data, then hands it to |
| 66 | a backup transport, which then delivers the data to the cloud storage. During a |
| 67 | restore operation, the Backup Manager retrieves the backup data from the backup transport and |
| 68 | returns it to your application so your application can restore the data to the device. It's |
| 69 | possible for your application to request a restore, but that shouldn't be necessary—Android |
| 70 | automatically performs a restore operation when your application is installed and there exists |
| 71 | backup data associated with the user. The primary scenario in which backup data is restored is when |
| 72 | a user resets their device or upgrades to a new device and their previously installed |
| 73 | applications are re-installed.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 74 | |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 75 | <p class="note"><strong>Note:</strong> The backup service is <em>not</em> designed for |
| 76 | synchronizing application data with other clients or saving data that you'd like to access during |
| 77 | the normal application lifecycle. You cannot read or write backup data on demand and cannot access |
| 78 | it in any way other than through the APIs provided by the Backup Manager.</p> |
| 79 | |
| 80 | <p>The backup transport is the client-side component of Android's backup framework, which is |
| 81 | customizable by |
| 82 | the device manufacturer and service provider. The backup transport may differ from device to device |
| 83 | and which backup transport is available on any given device is transparent to your application. The |
| 84 | Backup Manager APIs isolate your application from the actual backup transport available on a given |
| 85 | device—your application communicates with the Backup Manager through a fixed set of APIs, |
| 86 | regardless of the underlying transport.</p> |
| 87 | |
| 88 | <p>Data backup is <em>not</em> guaranteed to be available on all Android-powered |
| 89 | devices. However, your application is not adversely affected in the event |
| 90 | that a device does not provide a backup transport. If you believe that users will benefit from data |
| 91 | backup in your application, then you can implement it as described in this document, test it, then |
| 92 | publish your application without any concern about which devices actually perform backup. When your |
| 93 | application runs on a device that does not provide a backup transport, your application operates |
| 94 | normally, but will not receive callbacks from the Backup Manager to backup data.</p> |
| 95 | |
| 96 | <p>Although you cannot know what the current transport is, you are always assured that your |
| 97 | backup data cannot be read by other applications on the device. Only the Backup Manager and backup |
| 98 | transport have access to the data you provide during a backup operation.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 99 | |
| 100 | <p class="caution"><strong>Caution:</strong> Because the cloud storage and transport service can |
| 101 | differ from device to device, Android makes no guarantees about the security of your data while |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 102 | using backup. You should always be cautious about using backup to store sensitive data, such as |
| 103 | usernames and passwords.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 104 | |
| 105 | |
| 106 | <h2 id="Basics">The Basics</h2> |
| 107 | |
| 108 | <p>To backup your application data, you need to implement a backup agent. Your backup |
| 109 | agent is called by the Backup Manager to provide the data you want to back up. It is also called |
| 110 | to restore your backup data when the application is re-installed. The Backup Manager handles all |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 111 | your data transactions with the cloud storage (using the backup transport) and your backup agent |
| 112 | handles all your data transactions on the device.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 113 | |
| 114 | <p>To implement a backup agent, you must:</p> |
| 115 | |
| 116 | <ol> |
| 117 | <li>Declare your backup agent in your manifest file with the <a |
| 118 | href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code |
| 119 | android:backupAgent}</a> attribute.</li> |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 120 | <li>Register your application with a backup service. Google offers <a |
| 121 | href="http://code.google.com/android/backup/index.html">Android Backup Service</a> as a backup |
| 122 | service for most Android-powered devices, which requires that you register your application in |
| 123 | order for it to work. Any other backup services available might also require you to register |
| 124 | in order to store your data on their servers.</li> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 125 | <li>Define a backup agent by either:</p> |
| 126 | <ol type="a"> |
| Scott Main | 369c1c1 | 2010-12-07 11:17:00 -0800 | [diff] [blame] | 127 | <li><a href="#BackupAgent">Extending BackupAgent</a> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 128 | <p>The {@link android.app.backup.BackupAgent} class provides the central interface with |
| 129 | which your application communicates with the Backup Manager. If you extend this class |
| 130 | directly, you must override {@link |
| 131 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 132 | onBackup()} and {@link |
| 133 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 134 | onRestore()} to handle the backup and restore operations for your data.</p> |
| 135 | <p><em>Or</em></p> |
| Scott Main | 369c1c1 | 2010-12-07 11:17:00 -0800 | [diff] [blame] | 136 | <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 137 | <p>The {@link android.app.backup.BackupAgentHelper} class provides a convenient |
| 138 | wrapper around the {@link android.app.backup.BackupAgent} class, which minimizes the amount of code |
| 139 | you need to write. In your {@link android.app.backup.BackupAgentHelper}, you must use one or more |
| 140 | "helper" objects, which automatically backup and restore certain types of data, so that you do not |
| 141 | need to implement {@link |
| 142 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 143 | onBackup()} and {@link |
| 144 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 145 | onRestore()}.</p> |
| 146 | <p>Android currently provides backup helpers that will backup and restore complete files |
| 147 | from {@link android.content.SharedPreferences} and <a |
| 148 | href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</p> |
| 149 | </li> |
| 150 | </ol> |
| 151 | </li> |
| 152 | </ol> |
| 153 | |
| 154 | |
| 155 | |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 156 | <h2 id="BackupManifest">Declaring the Backup Agent in Your Manifest</h2> |
| 157 | |
| 158 | <p>This is the easiest step, so once you've decided on the class name for your backup agent, declare |
| 159 | it in your manifest with the <a |
| 160 | href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code |
| 161 | android:backupAgent}</a> attribute in the <a |
| 162 | href="{@docRoot}guide/topics/manifest/application-element.html">{@code |
| 163 | <application>}</a> tag.</p> |
| 164 | |
| 165 | <p>For example:</p> |
| 166 | |
| 167 | <pre> |
| 168 | <manifest ... > |
| Scott Main | b94df62 | 2010-12-02 15:43:02 -0800 | [diff] [blame] | 169 | ... |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 170 | <application android:label="MyApplication" |
| 171 | <b>android:backupAgent="MyBackupAgent"</b>> |
| 172 | <activity ... > |
| 173 | ... |
| 174 | </activity> |
| 175 | </application> |
| 176 | </manifest> |
| 177 | </pre> |
| 178 | |
| 179 | <p>Another attribute you might want to use is <a |
| 180 | href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code |
| 181 | android:restoreAnyVersion}</a>. This attribute takes a boolean value to indicate whether you |
| 182 | want to restore the application data regardless of the current application version compared to the |
| 183 | version that produced the backup data. (The default value is "{@code false}".) See <a |
| 184 | href="#RestoreVersion">Checking the Restore Data Version</a> for more information.</p> |
| 185 | |
| 186 | <p class="note"><strong>Note:</strong> The backup service and the APIs you must use are |
| 187 | available only on devices running API Level 8 (Android 2.2) or greater, so you should also |
| 188 | set your <a |
| 189 | href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a> |
| 190 | attribute to "8". However, if you implement proper <a |
| 191 | href="{@docRoot}resources/articles/backward-compatibility.html">backward compatibility</a> in |
| 192 | your application, you can support this feature for devices running API Level 8 or greater, while |
| 193 | remaining compatible with older devices.</p> |
| 194 | |
| 195 | |
| 196 | |
| 197 | |
| Scott Main | 9643849 | 2010-06-21 22:05:12 -0700 | [diff] [blame] | 198 | <h2 id="BackupKey">Registering for Android Backup Service</h2> |
| 199 | |
| 200 | <p>Google provides a backup transport with <a |
| 201 | href="http://code.google.com/android/backup/index.html">Android Backup Service</a> for most |
| 202 | Android-powered devices running Android 2.2 or greater.</p> |
| 203 | |
| 204 | <p>In order for you application to perform backup using Android Backup Service, you must |
| 205 | register your application with the service to receive a Backup Service Key, then |
| 206 | declare the Backup Service Key in your Android manifest.</p> |
| 207 | |
| 208 | <p>To get your Backup Service Key, <a |
| 209 | href="http://code.google.com/android/backup/signup.html">register for Android Backup Service</a>. |
| 210 | When you register, you will be provided a Backup Service Key and the appropriate {@code |
| 211 | <meta-data>} XML code for your Android manifest file, which you must include as a child of the |
| 212 | {@code <application>} element. For example:</p> |
| 213 | |
| 214 | <pre> |
| 215 | <application android:label="MyApplication" |
| 216 | android:backupAgent="MyBackupAgent"> |
| 217 | ... |
| 218 | <meta-data android:name="com.google.android.backup.api_key" |
| 219 | android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" /> |
| 220 | </application> |
| 221 | </pre> |
| 222 | |
| 223 | <p>The <code>android:name</code> must be <code>"com.google.android.backup.api_key"</code> and |
| 224 | the <code>android:value</code> must be the Backup Service Key received from the Android Backup |
| 225 | Service registration.</p> |
| 226 | |
| 227 | <p>If you have multiple applications, you must register each one, using the respective package |
| 228 | name.</p> |
| 229 | |
| 230 | <p class="note"><strong>Note:</strong> The backup transport provided by Android Backup Service is |
| 231 | not guaranteed to be available |
| 232 | on all Android-powered devices that support backup. Some devices might support backup |
| 233 | using a different transport, some devices might not support backup at all, and there is no way for |
| 234 | your application to know what transport is used on the device. However, if you implement backup for |
| 235 | your application, you should always include a Backup Service Key for Android Backup Service so |
| 236 | your application can perform backup when the device uses the Android Backup Service transport. If |
| 237 | the device does not use Android Backup Service, then the {@code <meta-data>} element with the |
| 238 | Backup Service Key is ignored.</p> |
| 239 | |
| 240 | |
| 241 | |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 242 | |
| 243 | <h2 id="BackupAgent">Extending BackupAgent</h2> |
| 244 | |
| 245 | <p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class |
| Scott Main | 1c8b6ca | 2010-07-02 11:11:34 -0700 | [diff] [blame] | 246 | directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 247 | advantage of the built-in helper classes that automatically backup and restore your files. However, |
| 248 | you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p> |
| 249 | <ul> |
| 250 | <li>Version your data format. For instance, if you anticipate the need to revise the |
| 251 | format in which you write your application data, you can build a backup agent to cross-check your |
| 252 | application version during a restore operation and perform any necessary compatibility work if the |
| 253 | version on the device is different than that of the backup data. For more information, see <a |
| 254 | href="#RestoreVersion">Checking the Restore Data Version</a>.</li> |
| 255 | <li>Instead of backing up an entire file, you can specify the portions of data the should be |
| 256 | backed up and how each portion is then restored to the device. (This can also help you manage |
| 257 | different versions, because you read and write your data as unique entities, rather than |
| 258 | complete files.)</li> |
| 259 | <li>Back up data in a database. If you have an SQLite database that you want to restore when |
| 260 | the user re-installs your application, you need to build a custom {@link |
| 261 | android.app.backup.BackupAgent} that reads the appropriate data during a backup operation, then |
| 262 | create your table and insert the data during a restore operation.</li> |
| 263 | </ul> |
| 264 | |
| 265 | <p>If you don't need to perform any of the tasks above and want to back up complete files from |
| 266 | {@link android.content.SharedPreferences} or <a |
| 267 | href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you |
| Scott Main | 1c8b6ca | 2010-07-02 11:11:34 -0700 | [diff] [blame] | 268 | should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 269 | |
| 270 | |
| 271 | |
| 272 | <h3 id="RequiredMethods">Required Methods</h3> |
| 273 | |
| 274 | <p>When you create a backup agent by extending {@link android.app.backup.BackupAgent}, you |
| 275 | must implement the following callback methods:</p> |
| 276 | |
| 277 | <dl> |
| 278 | <dt>{@link |
| 279 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 280 | onBackup()}</dt> |
| Scott Main | 369c1c1 | 2010-12-07 11:17:00 -0800 | [diff] [blame] | 281 | <dd>The Backup Manager calls this method after you <a href="#RequestingBackup">request a |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 282 | backup</a>. In this method, you read your application data from the device and pass the data you |
| 283 | want to back up to the Backup Manager, as described below in <a href="#PerformingBackup">Performing |
| 284 | backup</a>.</dd> |
| 285 | |
| 286 | <dt>{@link |
| 287 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 288 | onRestore()}</dt> |
| 289 | <dd>The Backup Manager calls this method during a restore operation (you can <a |
| Scott Main | 369c1c1 | 2010-12-07 11:17:00 -0800 | [diff] [blame] | 290 | href="#RequestingRestore">request a restore</a>, but the system automatically performs restore when |
| 291 | the user re-installs your application). When it calls this method, the Backup Manager delivers your |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 292 | backup data, which you then restore to the device, as described below in <a |
| 293 | href="#PerformingRestore">Performing restore</a>.</dd> |
| 294 | </dl> |
| 295 | |
| 296 | |
| 297 | |
| 298 | <h3 id="PerformingBackup">Performing backup</h3> |
| 299 | |
| 300 | |
| 301 | <p>When it's time to back up your application data, the Backup Manager calls your {@link |
| 302 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 303 | onBackup()} method. This is where you must provide your application data to the Backup Manager so |
| 304 | it can be saved to cloud storage.</p> |
| 305 | |
| 306 | <p>Only the Backup Manager can call your backup agent's {@link |
| 307 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 308 | onBackup()} method. Each time that your application data changes and you want to perform a backup, |
| 309 | you must request a backup operation by calling {@link |
| 310 | android.app.backup.BackupManager#dataChanged()} (see <a href="#RequestingBackup">Requesting |
| 311 | Backup</a> for more information). A backup request does not result in an immediate call to your |
| 312 | {@link |
| 313 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 314 | onBackup()} method. Instead, the Backup Manager waits for an appropriate time, then performs |
| 315 | backup for all applications that have requested a backup since the last backup was performed.</p> |
| 316 | |
| 317 | <p class="note"><strong>Tip:</strong> While developing your application, you can initiate an |
| 318 | immediate backup operation from the Backup Manager with the <a |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 319 | href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a>.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 320 | |
| 321 | <p>When the Backup Manager calls your {@link |
| 322 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 323 | onBackup()} method, it passes three parameters:</p> |
| 324 | |
| 325 | <dl> |
| 326 | <dt>{@code oldState}</dt> |
| 327 | <dd>An open, read-only {@link android.os.ParcelFileDescriptor} pointing to the last backup |
| 328 | state provided by your application. This is not the backup data from cloud storage, but a |
| 329 | local representation of the data that was backed up the last time {@link |
| 330 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 331 | onBackup()} was called (as defined by {@code newState}, below, or from {@link |
| 332 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 333 | onRestore()}—more about this in the next section). Because {@link |
| 334 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 335 | onBackup()} does not allow you to read existing backup data in |
| 336 | the cloud storage, you can use this local representation to determine whether your data has changed |
| 337 | since the last backup.</dd> |
| 338 | <dt>{@code data}</dt> |
| 339 | <dd>A {@link android.app.backup.BackupDataOutput} object, which you use to deliver your backup |
| 340 | data to the Backup Manager.</dd> |
| 341 | <dt>{@code newState}</dt> |
| 342 | <dd>An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which |
| 343 | you must write a representation of the data that you delivered to {@code data} (a representation |
| 344 | can be as simple as the last-modified timestamp for your file). This object is |
| 345 | returned as {@code oldState} the next time the Backup Manager calls your {@link |
| 346 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 347 | onBackup()} method. If you do not write your backup data to {@code newState}, then {@code oldState} |
| 348 | will point to an empty file next time Backup Manager calls {@link |
| 349 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 350 | onBackup()}.</dd> |
| 351 | </dl> |
| 352 | |
| 353 | <p>Using these parameters, you should implement your {@link |
| 354 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 355 | onBackup()} method to do the following:</p> |
| 356 | |
| 357 | <ol> |
| 358 | <li>Check whether your data has changed since the last backup by comparing {@code oldState} to |
| 359 | your current data. How you read data in {@code oldState} depends on how you originally wrote it to |
| 360 | {@code newState} (see step 3). The easiest way to record the state of a file is with its |
| 361 | last-modified timestamp. For example, here's how you can read and compare a timestamp from {@code |
| 362 | oldState}: |
| 363 | <pre> |
| 364 | // Get the oldState input stream |
| 365 | FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); |
| 366 | DataInputStream in = new DataInputStream(instream); |
| 367 | |
| 368 | try { |
| 369 | // Get the last modified timestamp from the state file and data file |
| 370 | long stateModified = in.readLong(); |
| 371 | long fileModified = mDataFile.lastModified(); |
| 372 | |
| 373 | if (stateModified != fileModified) { |
| 374 | // The file has been modified, so do a backup |
| 375 | // Or the time on the device changed, so be safe and do a backup |
| 376 | } else { |
| 377 | // Don't back up because the file hasn't changed |
| 378 | return; |
| 379 | } |
| 380 | } catch (IOException e) { |
| 381 | // Unable to read state file... be safe and do a backup |
| 382 | } |
| 383 | </pre> |
| 384 | <p>If nothing has changed and you don't need to back up, skip to step 3.</p> |
| 385 | </li> |
| 386 | <li>If your data has changed, compared to {@code oldState}, write the current data to |
| 387 | {@code data} to back it up to the cloud storage. |
| 388 | <p>You must write each chunk of data as an "entity" in the {@link |
| 389 | android.app.backup.BackupDataOutput}. An entity is a flattened binary data |
| 390 | record that is identified by a unique key string. Thus, the data set that you back up is |
| 391 | conceptually a set of key-value pairs.</p> |
| 392 | <p>To add an entity to your backup data set, you must:</p> |
| 393 | <ol> |
| 394 | <li>Call {@link android.app.backup.BackupDataOutput#writeEntityHeader(String,int) |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 395 | writeEntityHeader()}, passing a unique string key for the data you're about to write and the data |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 396 | size.</li> |
| 397 | <li>Call {@link android.app.backup.BackupDataOutput#writeEntityData(byte[],int) |
| 398 | writeEntityData()}, passing a byte buffer that contains your data and the number of bytes to write |
| 399 | from the buffer (which should match the size passed to {@link |
| 400 | android.app.backup.BackupDataOutput#writeEntityHeader(String,int) writeEntityHeader()}).</li> |
| 401 | </ol> |
| 402 | <p>For example, the following code flattens some data into a byte stream and writes it into a |
| 403 | single entity:</p> |
| 404 | <pre> |
| 405 | // Create buffer stream and data output stream for our data |
| 406 | ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); |
| 407 | DataOutputStream outWriter = new DataOutputStream(bufStream); |
| 408 | // Write structured data |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 409 | outWriter.writeUTF(mPlayerName); |
| 410 | outWriter.writeInt(mPlayerScore); |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 411 | // Send the data to the Backup Manager via the BackupDataOutput |
| 412 | byte[] buffer = bufStream.toByteArray(); |
| 413 | int len = buffer.length; |
| 414 | data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); |
| 415 | data.writeEntityData(buffer, len); |
| 416 | </pre> |
| 417 | <p>Perform this for each piece of data that you want to back up. How you divide your data into |
| 418 | entities is up to you (and you might use just one entity).</p> |
| 419 | </li> |
| 420 | <li>Whether or not you perform a backup (in step 2), write a representation of the current data to |
| 421 | the {@code newState} {@link android.os.ParcelFileDescriptor}. The Backup Manager retains this object |
| 422 | locally as a representation of the data that is currently backed up. It passes this back to you as |
| 423 | {@code oldState} the next time it calls {@link |
| 424 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 425 | onBackup()} so you can determine whether another backup is necessary (as handled in step 1). If you |
| 426 | do not write the current data state to this file, then |
| 427 | {@code oldState} will be empty during the next callback. |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 428 | <p>The following example saves a representation of the current data into {@code newState} using |
| 429 | the file's last-modified timestamp:</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 430 | <pre> |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 431 | FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 432 | DataOutputStream out = new DataOutputStream(outstream); |
| 433 | |
| 434 | long modified = mDataFile.lastModified(); |
| 435 | out.writeLong(modified); |
| 436 | </pre> |
| 437 | </li> |
| 438 | </ol> |
| 439 | |
| 440 | <p class="caution"><strong>Caution:</strong> If your application data is saved to a file, make sure |
| 441 | that you use synchronized statements while accessing the file so that your backup agent does not |
| 442 | read the file while an Activity in your application is also writing the file.</p> |
| 443 | |
| 444 | |
| 445 | |
| 446 | |
| 447 | <h3 id="PerformingRestore">Performing restore</h3> |
| 448 | |
| 449 | <p>When it's time to restore your application data, the Backup Manager calls your backup |
| 450 | agent's {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 451 | onRestore()} method. When it calls this method, the Backup Manager delivers your backup data so |
| 452 | you can restore it onto the device.</p> |
| 453 | |
| 454 | <p>Only the Backup Manager can call {@link |
| 455 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 456 | onRestore()}, which happens automatically when the system installs your application and |
| 457 | finds existing backup data. However, you can request a restore operation for |
| 458 | your application by calling {@link |
| 459 | android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} (see <a |
| 460 | href="#RequestingRestore">Requesting restore</a> for more information).</p> |
| 461 | |
| 462 | <p class="note"><strong>Note:</strong> While developing your application, you can also request a |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 463 | restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 464 | tool</a>.</p> |
| 465 | |
| 466 | <p>When the Backup Manager calls your {@link |
| 467 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 468 | onRestore()} method, it passes three parameters:</p> |
| 469 | |
| 470 | <dl> |
| 471 | <dt>{@code data}</dt> |
| 472 | <dd>A {@link android.app.backup.BackupDataInput}, which allows you to read your backup |
| 473 | data.</dd> |
| 474 | <dt>{@code appVersionCode}</dt> |
| 475 | <dd>An integer representing the value of your application's <a |
| 476 | href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a> |
| 477 | manifest attribute, as it was when this data was backed up. You can use this to cross-check the |
| 478 | current application version and determine if the data format is compatible. For more |
| 479 | information about using this to handle different versions of restore data, see the section |
| 480 | below about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</dd> |
| 481 | <dt>{@code newState}</dt> |
| 482 | <dd>An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which |
| 483 | you must write the final backup state that was provided with {@code data}. This object is |
| 484 | returned as {@code oldState} the next time {@link |
| 485 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 486 | onBackup()} is called. Recall that you must also write the same {@code newState} object in the |
| 487 | {@link |
| 488 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 489 | onBackup()} callback—also doing it here ensures that the {@code oldState} object given to |
| 490 | {@link |
| 491 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 492 | onBackup()} is valid even the first time {@link |
| 493 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 494 | onBackup()} is called after the device is restored.</dd> |
| 495 | </dl> |
| 496 | |
| 497 | <p>In your implementation of {@link |
| 498 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 499 | onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} on the |
| 500 | {@code data} to iterate |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 501 | through all entities in the data set. For each entity found, do the following:</p> |
| 502 | |
| 503 | <ol> |
| 504 | <li>Get the entity key with {@link android.app.backup.BackupDataInput#getKey()}.</li> |
| 505 | <li>Compare the entity key to a list of known key values that you should have declared as static |
| 506 | final strings inside your {@link android.app.backup.BackupAgent} class. When the key matches one of |
| 507 | your known key strings, enter into a statement to extract the entity data and save it to the device: |
| 508 | <ol> |
| 509 | <li>Get the entity data size with {@link |
| 510 | android.app.backup.BackupDataInput#getDataSize()} and create a byte array of that size.</li> |
| 511 | <li>Call {@link android.app.backup.BackupDataInput#readEntityData(byte[],int,int) |
| 512 | readEntityData()} and pass it the byte array, which is where the data will go, and specify the |
| 513 | start offset and the size to read.</li> |
| 514 | <li>Your byte array is now full and you can read the data and write it to the device |
| 515 | however you like.</li> |
| 516 | </ol> |
| 517 | </li> |
| 518 | <li>After you read and write your data back to the device, write the state of your data to the |
| 519 | {@code newState} parameter the same as you do during {@link |
| 520 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 521 | onBackup()}. |
| 522 | </ol> |
| 523 | |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 524 | <p>For example, here's how you can restore the data backed up by the example in the previous |
| 525 | section:</p> |
| 526 | |
| 527 | <pre> |
| 528 | @Override |
| 529 | public void onRestore(BackupDataInput data, int appVersionCode, |
| 530 | ParcelFileDescriptor newState) throws IOException { |
| 531 | // There should be only one entity, but the safest |
| 532 | // way to consume it is using a while loop |
| 533 | while (data.readNextHeader()) { |
| 534 | String key = data.getKey(); |
| 535 | int dataSize = data.getDataSize(); |
| 536 | |
| 537 | // If the key is ours (for saving top score). Note this key was used when |
| 538 | // we wrote the backup entity header |
| 539 | if (TOPSCORE_BACKUP_KEY.equals(key)) { |
| 540 | // Create an input stream for the BackupDataInput |
| 541 | byte[] dataBuf = new byte[dataSize]; |
| 542 | data.readEntityData(dataBuf, 0, dataSize); |
| 543 | ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); |
| 544 | DataInputStream in = new DataInputStream(baStream); |
| 545 | |
| 546 | // Read the player name and score from the backup data |
| 547 | mPlayerName = in.readUTF(); |
| 548 | mPlayerScore = in.readInt(); |
| 549 | |
| 550 | // Record the score on the device (to a file or something) |
| 551 | recordScore(mPlayerName, mPlayerScore); |
| 552 | } else { |
| 553 | // We don't know this entity key. Skip it. (Shouldn't happen.) |
| 554 | data.skipEntityData(); |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | // Finally, write to the state blob (newState) that describes the restored data |
| 559 | FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); |
| 560 | DataOutputStream out = new DataOutputStream(outstream); |
| 561 | out.writeUTF(mPlayerName); |
| 562 | out.writeInt(mPlayerScore); |
| 563 | } |
| 564 | </pre> |
| 565 | |
| 566 | <p>In this example, the {@code appVersionCode} parameter passed to {@link |
| 567 | android.app.backup.BackupAgent#onRestore onRestore()} is not used. However, you might want to use |
| 568 | it if you've chosen to perform backup when the user's version of the application has actually moved |
| 569 | backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see |
| 570 | the section about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</p> |
| 571 | |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 572 | <div class="special"> |
| 573 | <p>For an example implementation of {@link android.app.backup.BackupAgent}, see the <a |
| 574 | href="{@docRoot}resources/samples/BackupRestore/src/com/example/android/backuprestore/ExampleAgent.html">{@code |
| 575 | ExampleAgent}</a> class in the <a |
| Scott Main | 20a7612 | 2010-06-02 13:01:49 -0700 | [diff] [blame] | 576 | href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 577 | application.</p> |
| 578 | </div> |
| 579 | |
| 580 | |
| 581 | |
| 582 | |
| 583 | |
| 584 | |
| 585 | <h2 id="BackupAgentHelper">Extending BackupAgentHelper</h2> |
| 586 | |
| 587 | <p>You should build your backup agent using {@link android.app.backup.BackupAgentHelper} if you want |
| 588 | to back up complete files (from either {@link android.content.SharedPreferences} or <a |
| 589 | href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>). |
| 590 | Building your backup agent with {@link android.app.backup.BackupAgentHelper} requires far less |
| 591 | code than extending {@link android.app.backup.BackupAgent}, because you don't have to implement |
| 592 | {@link |
| 593 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 594 | onBackup()} and {@link |
| 595 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 596 | onRestore()}.</p> |
| 597 | |
| 598 | <p>Your implementation of {@link android.app.backup.BackupAgentHelper} must |
| 599 | use one or more backup helpers. A backup helper is a specialized |
| 600 | component that {@link android.app.backup.BackupAgentHelper} summons to perform backup and |
| 601 | restore operations for a particular type of data. The Android framework currently provides two |
| 602 | different helpers:</p> |
| 603 | <ul> |
| 604 | <li>{@link android.app.backup.SharedPreferencesBackupHelper} to backup {@link |
| 605 | android.content.SharedPreferences} files.</li> |
| 606 | <li>{@link android.app.backup.FileBackupHelper} to backup files from <a |
| 607 | href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</li> |
| 608 | </ul> |
| 609 | |
| 610 | <p>You can include multiple helpers in your {@link android.app.backup.BackupAgentHelper}, but only |
| 611 | one helper is needed for each data type. That is, if you have multiple {@link |
| 612 | android.content.SharedPreferences} files, then you need only one {@link |
| 613 | android.app.backup.SharedPreferencesBackupHelper}.</p> |
| 614 | |
| 615 | <p>For each helper you want to add to your {@link android.app.backup.BackupAgentHelper}, you must do |
| 616 | the following during your {@link android.app.backup.BackupAgent#onCreate()} method:</p> |
| 617 | <ol> |
| 618 | <li>Instantiate in instance of the desired helper class. In the class constructor, you must |
| 619 | specify the appropriate file(s) you want to backup.</li> |
| 620 | <li>Call {@link android.app.backup.BackupAgentHelper#addHelper(String,BackupHelper) addHelper()} |
| 621 | to add the helper to your {@link android.app.backup.BackupAgentHelper}.</li> |
| 622 | </ol> |
| 623 | |
| 624 | <p>The following sections describe how to create a backup agent using each of the available |
| 625 | helpers.</p> |
| 626 | |
| 627 | |
| 628 | |
| 629 | <h3 id="SharedPreferences">Backing up SharedPreferences</h3> |
| 630 | |
| Scott Main | 4fb91d9 | 2010-06-29 15:22:03 -0700 | [diff] [blame] | 631 | <p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must |
| 632 | include the name of one or more {@link android.content.SharedPreferences} files.</p> |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 633 | |
| 634 | <p>For example, to back up a {@link android.content.SharedPreferences} file named |
| 635 | "user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks |
| 636 | like this:</p> |
| 637 | |
| 638 | <pre> |
| 639 | public class MyPrefsBackupAgent extends BackupAgentHelper { |
| 640 | // The name of the SharedPreferences file |
| 641 | static final String PREFS = "user_preferences"; |
| 642 | |
| 643 | // A key to uniquely identify the set of backup data |
| 644 | static final String PREFS_BACKUP_KEY = "prefs"; |
| 645 | |
| 646 | // Allocate a helper and add it to the backup agent |
| Scott Main | 54d21da | 2010-11-10 10:43:56 -0800 | [diff] [blame] | 647 | @Override |
| 648 | public void onCreate() { |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 649 | SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); |
| 650 | addHelper(PREFS_BACKUP_KEY, helper); |
| 651 | } |
| 652 | } |
| 653 | </pre> |
| 654 | |
| 655 | <p>That's it! That's your entire backup agent. The {@link |
| 656 | android.app.backup.SharedPreferencesBackupHelper} includes all the code |
| 657 | needed to backup and restore a {@link android.content.SharedPreferences} file.</p> |
| 658 | |
| 659 | <p>When the Backup Manager calls {@link |
| 660 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 661 | onBackup()} and {@link |
| 662 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 663 | onRestore()}, {@link android.app.backup.BackupAgentHelper} calls your backup helpers to perform |
| 664 | backup and restore for your specified files.</p> |
| 665 | |
| 666 | <p class="note"><strong>Note:</strong> {@link android.content.SharedPreferences} are threadsafe, so |
| 667 | you can safely read and write the shared preferences file from your backup agent and |
| 668 | other activities.</p> |
| 669 | |
| 670 | |
| 671 | |
| 672 | <h3 id="Files">Backing up other files</h3> |
| 673 | |
| 674 | <p>When you instantiate a {@link android.app.backup.FileBackupHelper}, you must include the name of |
| 675 | one or more files that are saved to your application's <a |
| 676 | href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a> |
| 677 | (as specified by {@link android.content.ContextWrapper#getFilesDir()}, which is the same |
| 678 | location where {@link android.content.Context#openFileOutput(String,int) openFileOutput()} writes |
| 679 | files).</p> |
| 680 | |
| 681 | <p>For example, to backup two files named "scores" and "stats," a backup agent using {@link |
| 682 | android.app.backup.BackupAgentHelper} looks like this:</p> |
| 683 | |
| 684 | <pre> |
| 685 | public class MyFileBackupAgent extends BackupAgentHelper { |
| 686 | // The name of the SharedPreferences file |
| 687 | static final String TOP_SCORES = "scores"; |
| 688 | static final String PLAYER_STATS = "stats"; |
| 689 | |
| 690 | // A key to uniquely identify the set of backup data |
| 691 | static final String FILES_BACKUP_KEY = "myfiles"; |
| 692 | |
| 693 | // Allocate a helper and add it to the backup agent |
| 694 | void onCreate() { |
| 695 | FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); |
| 696 | addHelper(FILES_BACKUP_KEY, helper); |
| 697 | } |
| 698 | } |
| 699 | </pre> |
| 700 | |
| 701 | <p>The {@link android.app.backup.FileBackupHelper} includes all the code necessary to backup and |
| 702 | restore files that are saved to your application's <a |
| 703 | href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>..</p> |
| 704 | |
| 705 | <p>However, reading and writing to files on internal storage is <strong>not threadsafe</strong>. To |
| 706 | ensure that your backup agent does not read or write your files at the same time as your activities, |
| 707 | you must use synchronized statements each time you perform a read or write. For example, |
| 708 | in any Activity where you read and write the file, you need an object to use as the intrinsic |
| 709 | lock for the synchronized statements:</p> |
| 710 | |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 711 | <pre> |
| 712 | // Object for intrinsic lock |
| Jesse Wilson | ec527e5 | 2011-11-18 17:18:16 -0500 | [diff] [blame] | 713 | static final Object sDataLock = new Object(); |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 714 | </pre> |
| 715 | |
| 716 | <p>Then create a synchronized statement with this lock each time you read or write the files. For |
| 717 | example, here's a synchronized statement for writing the latest score in a game to a file:</p> |
| 718 | |
| 719 | <pre> |
| 720 | try { |
| 721 | synchronized (MyActivity.sDataLock) { |
| 722 | File dataFile = new File({@link android.content.Context#getFilesDir()}, TOP_SCORES); |
| 723 | RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw"); |
| 724 | raFile.writeInt(score); |
| 725 | } |
| 726 | } catch (IOException e) { |
| 727 | Log.e(TAG, "Unable to write to file"); |
| 728 | } |
| 729 | </pre> |
| 730 | |
| 731 | <p>You should synchronize your read statements with the same lock.</p> |
| 732 | |
| 733 | <p>Then, in your {@link android.app.backup.BackupAgentHelper}, you must override {@link |
| 734 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 735 | onBackup()} and {@link |
| 736 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 737 | onRestore()} to synchronize the backup and restore operations with the same |
| 738 | intrinsic lock. For example, the {@code MyFileBackupAgent} example from above needs the following |
| 739 | methods:</p> |
| 740 | |
| 741 | <pre> |
| 742 | @Override |
| 743 | public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, |
| 744 | ParcelFileDescriptor newState) throws IOException { |
| 745 | // Hold the lock while the FileBackupHelper performs backup |
| 746 | synchronized (MyActivity.sDataLock) { |
| 747 | super.onBackup(oldState, data, newState); |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | @Override |
| 752 | public void onRestore(BackupDataInput data, int appVersionCode, |
| 753 | ParcelFileDescriptor newState) throws IOException { |
| 754 | // Hold the lock while the FileBackupHelper restores the file |
| 755 | synchronized (MyActivity.sDataLock) { |
| 756 | super.onRestore(data, appVersionCode, newState); |
| 757 | } |
| 758 | } |
| 759 | </pre> |
| 760 | |
| 761 | <p>That's it. All you need to do is add your {@link android.app.backup.FileBackupHelper} in the |
| 762 | {@link android.app.backup.BackupAgent#onCreate()} method and override {@link |
| 763 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 764 | onBackup()} and {@link |
| 765 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 766 | onRestore()} to synchronize read and write operations.</p> |
| 767 | |
| 768 | <div class="special"> |
| 769 | <p>For an example implementation of {@link |
| 770 | android.app.backup.BackupAgentHelper} with {@link android.app.backup.FileBackupHelper}, see the |
| 771 | {@code FileHelperExampleAgent} class in the <a |
| 772 | href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample |
| 773 | application.</p> |
| 774 | </div> |
| 775 | |
| 776 | |
| 777 | |
| 778 | |
| 779 | |
| 780 | |
| 781 | <h2 id="RestoreVersion">Checking the Restore Data Version</h2> |
| 782 | |
| 783 | <p>When the Backup Manager saves your data to cloud storage, it automatically includes the version |
| 784 | of your application, as defined by your manifest file's <a |
| 785 | href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a> |
| 786 | attribute. Before the Backup Manager calls your backup agent to restore your data, it |
| 787 | looks at the <a |
| 788 | href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code |
| 789 | android:versionCode}</a> of the installed application and compares it to the value |
| 790 | recorded in the restore data set. If the version recorded in the restore data set is |
| 791 | <em>newer</em> than the application version on the device, then the user has downgraded their |
| 792 | application. In this case, the Backup Manager will abort the restore operation for your application |
| 793 | and not call your {@link |
| 794 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} |
| 795 | method, because the restore set is considered meaningless to an older version.</p> |
| 796 | |
| 797 | <p>You can override this behavior with the <a |
| 798 | href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code |
| 799 | android:restoreAnyVersion}</a> attribute. This attribute is either "{@code true}" or "{@code |
| 800 | false}" to indicate whether you want to restore the application regardless of the restore set |
| 801 | version. The default value is "{@code false}". If you define this to be "{@code true}" then the |
| 802 | Backup Manager will ignore the <a |
| 803 | href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a> |
| 804 | and call your {@link |
| 805 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} |
| 806 | method in all cases. In doing so, you can manually check for the version difference in your {@link |
| 807 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} |
| 808 | method and take any steps necessary to make the data compatible if the versions conflict.</p> |
| 809 | |
| 810 | <p>To help you handle different versions during a restore operation, the {@link |
| 811 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} |
| 812 | method passes you the version code included with the restore data set as the {@code appVersionCode} |
| 813 | parameter. You can then query the current application's version code with the {@link |
| 814 | android.content.pm.PackageInfo#versionCode PackageInfo.versionCode} field. For example:</p> |
| 815 | |
| 816 | <pre> |
| 817 | PackageInfo info; |
| 818 | try { |
| 819 | String name = {@link android.content.ContextWrapper#getPackageName() getPackageName}(); |
| 820 | info = {@link android.content.ContextWrapper#getPackageManager |
| 821 | getPackageManager}().{@link android.content.pm.PackageManager#getPackageInfo(String,int) |
| 822 | getPackageInfo}(name,0); |
| 823 | } catch (NameNotFoundException nnfe) { |
| 824 | info = null; |
| 825 | } |
| 826 | |
| 827 | int version; |
| 828 | if (info != null) { |
| 829 | version = info.versionCode; |
| 830 | } |
| 831 | </pre> |
| 832 | |
| 833 | <p>Then simply compare the {@code version} acquired from {@link android.content.pm.PackageInfo} |
| 834 | to the {@code appVersionCode} passed into {@link |
| 835 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}. |
| 836 | </p> |
| 837 | |
| 838 | <p class="caution"><strong>Caution:</strong> Be certain you understand the consequences of setting |
| 839 | <a href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code |
| 840 | android:restoreAnyVersion}</a> to "{@code true}" for your application. If each version of your |
| 841 | application that supports backup does not properly account for variations in your data format during |
| 842 | {@link |
| 843 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}, |
| 844 | then the data on the device could be saved in a format incompatible with the version currently |
| 845 | installed on the device.</p> |
| 846 | |
| 847 | |
| 848 | |
| 849 | <h2 id="RequestingBackup">Requesting Backup</h2> |
| 850 | |
| 851 | <p>You can request a backup operation at any time by calling {@link |
| 852 | android.app.backup.BackupManager#dataChanged()}. This method notifies the Backup Manager that you'd |
| 853 | like to backup your data using your backup agent. The Backup Manager then calls your backup |
| 854 | agent's {@link |
| 855 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 856 | onBackup()} method at an opportune time in the future. Typically, you should |
| 857 | request a backup each time your data changes (such as when the user changes an application |
| 858 | preference that you'd like to back up). If you call {@link |
| 859 | android.app.backup.BackupManager#dataChanged()} several times consecutively, before the Backup |
| 860 | Manager requests a backup from your agent, your agent still receives just one call to {@link |
| 861 | android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) |
| 862 | onBackup()}.</p> |
| 863 | |
| 864 | <p class="note"><strong>Note:</strong> While developing your application, you can request a |
| 865 | backup and initiate an immediate backup operation with the <a |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 866 | href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 867 | tool</a>.</p> |
| 868 | |
| 869 | |
| 870 | <h2 id="RequestingRestore">Requesting Restore</h2> |
| 871 | |
| 872 | <p>During the normal life of your application, you shouldn't need to request a restore operation. |
| 873 | They system automatically checks for backup data and performs a restore when your application is |
| 874 | installed. However, you can manually request a restore operation by calling {@link |
| 875 | android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()}, if necessary. In |
| 876 | which case, the Backup Manager calls your {@link |
| 877 | android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} |
| 878 | implementation, passing the data from the current set of backup data.</p> |
| 879 | |
| 880 | <p class="note"><strong>Note:</strong> While developing your application, you can request a |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 881 | restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} |
| Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 882 | tool</a>.</p> |
| 883 | |
| Scott Main | 371557f | 2010-05-19 19:17:47 -0700 | [diff] [blame] | 884 | |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 885 | <h2 id="Testing">Testing Your Backup Agent</h2> |
| Scott Main | 371557f | 2010-05-19 19:17:47 -0700 | [diff] [blame] | 886 | |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 887 | <p>Once you've implemented your backup agent, you can test the backup and restore functionality |
| 888 | with the following procedure, using <a |
| 889 | href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}</a>.</p> |
| 890 | |
| 891 | <ol> |
| 892 | <li>Install your application on a suitable Android system image |
| Scott Main | 371557f | 2010-05-19 19:17:47 -0700 | [diff] [blame] | 893 | <ul> |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 894 | <li>If using the emulator, create and use an AVD with Android 2.2 (API Level 8).</li> |
| Scott Main | 371557f | 2010-05-19 19:17:47 -0700 | [diff] [blame] | 895 | <li>If using a device, the device must be running Android 2.2 or greater and have Android |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 896 | Market built in.</li> |
| Scott Main | 371557f | 2010-05-19 19:17:47 -0700 | [diff] [blame] | 897 | </ul> |
| 898 | </li> |
| Scott Main | 337b087 | 2010-06-25 15:55:57 -0700 | [diff] [blame] | 899 | <li>Ensure that backup is enabled |
| 900 | <ul> |
| 901 | <li>If using the emulator, you can enable backup with the following command from your SDK |
| 902 | {@code tools/} path: |
| 903 | <pre class="no-pretty-print">adb shell bmgr enable true</pre> |
| 904 | </li> |
| 905 | <li>If using a device, open the system <b>Settings</b>, select <b>Privacy</b>, then enable |
| 906 | <b>Back up my data</b> and <b>Automatic restore</b>. |
| 907 | </ul> |
| 908 | </li> |
| 909 | <li>Open your application and initialize some data |
| 910 | <p>If you've properly implemented backup in your application, then it should request a |
| 911 | backup each time the data changes. For example, each time the user changes some data, your app |
| 912 | should call {@link android.app.backup.BackupManager#dataChanged()}, which adds a backup request to |
| 913 | the Backup Manager queue. For testing purposes, you can also make a request with the following |
| 914 | {@code bmgr} command:</p> |
| 915 | <pre class="no-pretty-print">adb shell bmgr backup <em>your.package.name</em></pre> |
| 916 | </li> |
| 917 | <li>Initiate a backup operation: |
| 918 | <pre class="no-pretty-print">adb shell bmgr run</pre> |
| 919 | <p>This forces the Backup Manager to perform all backup requests that are in its |
| 920 | queue.</p> |
| 921 | <li>Uninstall your application: |
| 922 | <pre class="no-pretty-print">adb uninstall <em>your.package.name</em></pre> |
| 923 | </li> |
| 924 | <li>Re-install your application.</li> |
| 925 | </ol> |
| 926 | |
| 927 | <p>If your backup agent is successful, all the data you initialized in step 4 is restored.</p> |
| 928 | |
| Scott Main | 371557f | 2010-05-19 19:17:47 -0700 | [diff] [blame] | 929 | |