page.title=Data Backup @jd:body
Android's {@link android.app.backup backup} service allows you to copy your persistent application data to a remote "cloud" storage, in order to provide a restore point for the application data and settings. If a user performs a factory reset or converts to a new Android-powered device, the system automatically restores your backup data when the application is re-installed. This way, your users are not required to reproduce their previous data or application settings. This process is completely transparent to the user and does not affect the functionality or user experience in your application.
Android-powered devices that support the backup service provide a cloud storage area that saves your backup data and a backup transport that delivers your data to the storage area and back to the device. During a backup operation, Android's Backup Manager requests backup data from your application, then delivers it to the cloud storage using the backup transport. During a restore operation, the Backup Manager retrieves the backup data from the backup transport and returns it to your application so it can restore the data to the device. The backup service is not designed for data synchronization (you do not have access the backup data, except during a restore operation on the device).
The cloud storage used for backup won't necessarily be the same on all Android-powered devices. The cloud storage and backup transport may differ between devices and service providers. Where the backup data is stored is transparent to your application, but you are assured that your application data cannot be read by other applications.
Caution: Because the cloud storage and transport service can differ from device to device, Android makes no guarantees about the security of your data while using backup. You should be cautious about using backup to store sensitive data, such as usernames and passwords.
To backup your application data, you need to implement a backup agent. Your backup agent is called by the Backup Manager to provide the data you want to back up. It is also called to restore your backup data when the application is re-installed. The Backup Manager handles all your data transactions with the cloud storage and your backup agent handles all your data transactions on the device.
To implement a backup agent, you must:
The {@link android.app.backup.BackupAgent} class provides the central interface with which your application communicates with the Backup Manager. If you extend this class directly, you must override {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} to handle the backup and restore operations for your data.
Or
The {@link android.app.backup.BackupAgentHelper} class provides a convenient wrapper around the {@link android.app.backup.BackupAgent} class, which minimizes the amount of code you need to write. In your {@link android.app.backup.BackupAgentHelper}, you must use one or more "helper" objects, which automatically backup and restore certain types of data, so that you do not need to implement {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}.
Android currently provides backup helpers that will backup and restore complete files from {@link android.content.SharedPreferences} and internal storage.
This is the easiest step, so once you've decided on the class name for your backup agent, declare it in your manifest with the {@code android:backupAgent} attribute in the {@code <application>} tag.
For example:
<manifest ... > <application android:label="MyApplication" android:backupAgent="MyBackupAgent"> <activity ... > ... </activity> </application> </manifest>
Another attribute you might want to use is {@code android:restoreAnyVersion}. This attribute takes a boolean value to indicate whether you want to restore the application data regardless of the current application version compared to the version that produced the backup data. (The default value is "{@code false}".) See Checking the Restore Data Version for more information.
Note: The backup service and the APIs you must use are available only on devices running API Level 8 (Android 2.2) or greater, so you should also set your {@code android:minSdkVersion} attribute to "8". However, if you implement proper backward compatibility in your application, you can support this feature for devices running API Level 8 or greater, while remaining compatible with older devices.
Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class directly, but should instead extend BackupAgentHelper to take advantage of the built-in helper classes that automatically backup and restore your files. However, you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:
If you don't need to perform any of the tasks above and want to back up complete files from {@link android.content.SharedPreferences} or internal storage, you should skip to Extending BackupAgentHelper.
When you create a backup agent by extending {@link android.app.backup.BackupAgent}, you must implement the following callback methods:
When it's time to back up your application data, the Backup Manager calls your {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} method. This is where you must provide your application data to the Backup Manager so it can be saved to cloud storage.
Only the Backup Manager can call your backup agent's {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} method. Each time that your application data changes and you want to perform a backup, you must request a backup operation by calling {@link android.app.backup.BackupManager#dataChanged()} (see Requesting Backup for more information). A backup request does not result in an immediate call to your {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} method. Instead, the Backup Manager waits for an appropriate time, then performs backup for all applications that have requested a backup since the last backup was performed.
Tip: While developing your application, you can initiate an immediate backup operation from the Backup Manager with the bmgr tool.
When the Backup Manager calls your {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} method, it passes three parameters:
Using these parameters, you should implement your {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} method to do the following:
// Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = mDataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
If nothing has changed and you don't need to back up, skip to step 3.
You must write each chunk of data as an "entity" in the {@link android.app.backup.BackupDataOutput}. An entity is a flattened binary data record that is identified by a unique key string. Thus, the data set that you back up is conceptually a set of key-value pairs.
To add an entity to your backup data set, you must:
For example, the following code flattens some data into a byte stream and writes it into a single entity:
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeString(playerName); outWriter.writeInt(playerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Perform this for each piece of data that you want to back up. How you divide your data into entities is up to you (and you might use just one entity).
Again, the following example saves a representation of the data using the file's last-modified timestamp:
FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = mDataFile.lastModified(); out.writeLong(modified);
Caution: If your application data is saved to a file, make sure that you use synchronized statements while accessing the file so that your backup agent does not read the file while an Activity in your application is also writing the file.
When it's time to restore your application data, the Backup Manager calls your backup agent's {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} method. When it calls this method, the Backup Manager delivers your backup data so you can restore it onto the device.
Only the Backup Manager can call {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}, which happens automatically when the system installs your application and finds existing backup data. However, you can request a restore operation for your application by calling {@link android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} (see Requesting restore for more information).
Note: While developing your application, you can also request a restore operation with the bmgr tool.
When the Backup Manager calls your {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} method, it passes three parameters:
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:
For an example implementation of {@link android.app.backup.BackupAgent}, see the {@code ExampleAgent} class in the Backup and Restore sample application.
You should build your backup agent using {@link android.app.backup.BackupAgentHelper} if you want to back up complete files (from either {@link android.content.SharedPreferences} or internal storage). Building your backup agent with {@link android.app.backup.BackupAgentHelper} requires far less code than extending {@link android.app.backup.BackupAgent}, because you don't have to implement {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}.
Your implementation of {@link android.app.backup.BackupAgentHelper} must use one or more backup helpers. A backup helper is a specialized component that {@link android.app.backup.BackupAgentHelper} summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
You can include multiple helpers in your {@link android.app.backup.BackupAgentHelper}, but only one helper is needed for each data type. That is, if you have multiple {@link android.content.SharedPreferences} files, then you need only one {@link android.app.backup.SharedPreferencesBackupHelper}.
For each helper you want to add to your {@link android.app.backup.BackupAgentHelper}, you must do the following during your {@link android.app.backup.BackupAgent#onCreate()} method:
The following sections describe how to create a backup agent using each of the available helpers.
When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must the name of one or more {@link android.content.SharedPreferences} files.
For example, to back up a {@link android.content.SharedPreferences} file named "user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks like this:
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
That's it! That's your entire backup agent. The {@link android.app.backup.SharedPreferencesBackupHelper} includes all the code needed to backup and restore a {@link android.content.SharedPreferences} file.
When the Backup Manager calls {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}, {@link android.app.backup.BackupAgentHelper} calls your backup helpers to perform backup and restore for your specified files.
Note: {@link android.content.SharedPreferences} are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
When you instantiate a {@link android.app.backup.FileBackupHelper}, you must include the name of one or more files that are saved to your application's internal storage (as specified by {@link android.content.ContextWrapper#getFilesDir()}, which is the same location where {@link android.content.Context#openFileOutput(String,int) openFileOutput()} writes files).
For example, to backup two files named "scores" and "stats," a backup agent using {@link android.app.backup.BackupAgentHelper} looks like this:
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
The {@link android.app.backup.FileBackupHelper} includes all the code necessary to backup and restore files that are saved to your application's internal storage..
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
Interesting Fact:
A zero-length array is lighter-weight than a normal Object, so it's great for an intrinsic lock.
// Object for intrinsic lock static final Object[] sDataLock = new Object[0];
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try { synchronized (MyActivity.sDataLock) { File dataFile = new File({@link android.content.Context#getFilesDir()}, TOP_SCORES); RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw"); raFile.writeInt(score); } } catch (IOException e) { Log.e(TAG, "Unable to write to file"); }
You should synchronize your read statements with the same lock.
Then, in your {@link android.app.backup.BackupAgentHelper}, you must override {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} to synchronize the backup and restore operations with the same intrinsic lock. For example, the {@code MyFileBackupAgent} example from above needs the following methods:
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
That's it. All you need to do is add your {@link android.app.backup.FileBackupHelper} in the {@link android.app.backup.BackupAgent#onCreate()} method and override {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} to synchronize read and write operations.
For an example implementation of {@link android.app.backup.BackupAgentHelper} with {@link android.app.backup.FileBackupHelper}, see the {@code FileHelperExampleAgent} class in the Backup and Restore sample application.
When the Backup Manager saves your data to cloud storage, it automatically includes the version of your application, as defined by your manifest file's {@code android:versionCode} attribute. Before the Backup Manager calls your backup agent to restore your data, it looks at the {@code android:versionCode} of the installed application and compares it to the value recorded in the restore data set. If the version recorded in the restore data set is newer than the application version on the device, then the user has downgraded their application. In this case, the Backup Manager will abort the restore operation for your application and not call your {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} method, because the restore set is considered meaningless to an older version.
You can override this behavior with the {@code android:restoreAnyVersion} attribute. This attribute is either "{@code true}" or "{@code false}" to indicate whether you want to restore the application regardless of the restore set version. The default value is "{@code false}". If you define this to be "{@code true}" then the Backup Manager will ignore the {@code android:versionCode} and call your {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} method in all cases. In doing so, you can manually check for the version difference in your {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} method and take any steps necessary to make the data compatible if the versions conflict.
To help you handle different versions during a restore operation, the {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} method passes you the version code included with the restore data set as the {@code appVersionCode} parameter. You can then query the current application's version code with the {@link android.content.pm.PackageInfo#versionCode PackageInfo.versionCode} field. For example:
PackageInfo info; try { String name = {@link android.content.ContextWrapper#getPackageName() getPackageName}(); info = {@link android.content.ContextWrapper#getPackageManager getPackageManager}().{@link android.content.pm.PackageManager#getPackageInfo(String,int) getPackageInfo}(name,0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Then simply compare the {@code version} acquired from {@link android.content.pm.PackageInfo} to the {@code appVersionCode} passed into {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}.
Caution: Be certain you understand the consequences of setting {@code android:restoreAnyVersion} to "{@code true}" for your application. If each version of your application that supports backup does not properly account for variations in your data format during {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}, then the data on the device could be saved in a format incompatible with the version currently installed on the device.
You can request a backup operation at any time by calling {@link android.app.backup.BackupManager#dataChanged()}. This method notifies the Backup Manager that you'd like to backup your data using your backup agent. The Backup Manager then calls your backup agent's {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()} method at an opportune time in the future. Typically, you should request a backup each time your data changes (such as when the user changes an application preference that you'd like to back up). If you call {@link android.app.backup.BackupManager#dataChanged()} several times consecutively, before the Backup Manager requests a backup from your agent, your agent still receives just one call to {@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()}.
Note: While developing your application, you can request a backup and initiate an immediate backup operation with the bmgr tool.
During the normal life of your application, you shouldn't need to request a restore operation. They system automatically checks for backup data and performs a restore when your application is installed. However, you can manually request a restore operation by calling {@link android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()}, if necessary. In which case, the Backup Manager calls your {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()} implementation, passing the data from the current set of backup data.
Note: While developing your application, you can request a restore operation with the bmgr tool.
To develop and test your backup agent: