1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
|
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>
<li><a href="#DevelopingTesting">Developing and Testing Your Backup Agent</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>
<h2 id="DevelopingTesting">Developing and Testing Your Backup Agent</h2>
<p>To develop and test your backup agent:</p>
<ul>
<li>Set your build target to a platform using API Level 8 or higher</li>
<li>Run your application on a suitable Android system image:
<ul>
<li>If using the emulator, create and use an AVD with the Google APIs add-on (API Level
8) — the Google APIs add-on is available as an SDK component through the SDK and AVD
Manager</li>
<li>If using a device, the device must be running Android 2.2 or greater and have Android
Market built in</li>
</ul>
</li>
<li>Test your backup agent using the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code
bmgr}</a> tool to initiate backup and restore operations</li>
</ul>
|