》 Android's backup
service allows you to copy your persistent application data to remote "cloud" storage, in order to provide a restore point for the application data and settings.
During a backup operation (which your application can request), Android's Backup Manager (BackupManager
) queries your application for backup data, then hands it to a backup transport, which then delivers the data to the cloud storage.
Data backup is not guaranteed to be available on all Android-powered devices.
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 always be cautious about using backup to store sensitive data, such as usernames and passwords.
》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 android:backupAgent
attribute in the <application>
tag.
For example:
<manifest ... > ... <application android:label="MyApplication" android:backupAgent="MyBackupAgent"> <activity ... > ... </activity> </application> </manifest>
》To get your Backup Service Key, register for Android Backup Service. When you register, you will be provided a Backup Service Key and the appropriate <meta-data>
XML code for your Android manifest file, which you must include as a child of the <application>
element. For example:
<application android:label="MyApplication" android:backupAgent="MyBackupAgent"> ... <meta-data android:name="com.google.android.backup.api_key" android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" /> </application>
The android:name
must be "com.google.android.backup.api_key"
and the android:value
must be the Backup Service Key received from the Android Backup Service registration.
When you create a backup agent by extending BackupAgent
, you must implement the following callback methods:onBackup();onRestore()。
》Check whether your data has changed since the last backup by comparing oldState
to your current data. How you read data in oldState
depends on how you originally wrote it to 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 oldState
:
// Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = mDataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
If your data has changed, compared to oldState
, write the current data to data
to back it up to the cloud storage.
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(mPlayerName); outWriter.writeInt(mPlayerScore); // 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);Whether or not you perform a backup (in step 2), write a representation of the current data to the
newState
ParcelFileDescriptor
. The Backup Manager retains this object locally as a representation of the data that is currently backed up.
FileOutputStream outstream = new FileOutputStream(newState.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.
In your implementation of onRestore()
, you should call readNextHeader()
on the data
to iterate through all entities in the data set. For each entity found, do the following:
- Get the entity key with
getKey()
. - Compare the entity key to a list of known key values that you should have declared as static final strings inside your
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:- Get the entity data size with
getDataSize()
and create a byte array of that size. - Call
readEntityData()
and pass it the byte array, which is where the data will go, and specify the start offset and the size to read. - Your byte array is now full and you can read the data and write it to the device however you like.
- Get the entity data size with
- After you read and write your data back to the device, write the state of your data to the
newState
parameter the same as you do duringonBackup()
.
For example, here's how you can restore the data backed up by the example in the previous section:
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data mPlayerName = in.readUTF(); mPlayerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(mPlayerName, mPlayerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(mPlayerName); out.writeInt(mPlayerScore); }》 Extending BackupAgentHelper
You should build your backup agent using BackupAgentHelper
if you want to back up complete files (from either SharedPreferences
or internal storage). Building your backup agent with BackupAgentHelper
requires far less code than extending BackupAgent
, because you don't have to implement onBackup()
andonRestore()
.
Your implementation of BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
SharedPreferencesBackupHelper
to backupSharedPreferences
files.FileBackupHelper
to backup files from internal storage.
You can include multiple helpers in your BackupAgentHelper
, but only one helper is needed for each data type. That is, if you have multiple SharedPreferences
files, then you need only oneSharedPreferencesBackupHelper
.
For each helper you want to add to your BackupAgentHelper
, you must do the following during youronCreate()
method:
- Instantiate in instance of the desired helper class. In the class constructor, you must specify the appropriate file(s) you want to backup.
- Call
addHelper()
to add the helper to yourBackupAgentHelper
.
The following sections describe how to create a backup agent using each of the available helpers.
Backing up SharedPreferences
When you instantiate a SharedPreferencesBackupHelper
, you must include the name of one or moreSharedPreferences
files.
For example, to back up a SharedPreferences
file named "user_preferences", a complete backup agent usingBackupAgentHelper
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 @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the 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 @Override public void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
Then, in your BackupAgentHelper
, you must override onBackup()
and onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the 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); } }