The Basics
为了备份应用数据,你需要实现一个 backup agent. 它用来为BackupManager 提供你想要备份的数据,在你重新安装应用时恢复数据。BackupManager 通过backup transport 处理和云存储相关的数据业务,通过你的backup agent 处理设备上的数据相关的业务。
BackupAgent实现步骤:
在 manifest 文件中声明 android:backupAgent 属性。
通过backup服务注册你的应用。谷歌为大多数 Android 设备提供了 Android Backup Service,你需要通过这个服务注册你的应用。其他的云服务也需要注册。
-
定义你的 backup agent:
扩展 BackupAgent
重写 onBackup() onRestore()扩展 BackupAgentHelper
需要使用多个helper对象自动完成备份及恢复。
会备份整个文件。
Declaring the Backup Agent in Your Manifest
在application
标签中声明android:backupAgent
.
<manifest ... >
...
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
<activity ... >
...
</activity>
</application>
</manifest>
android:restoreAnyVersion
默认值为false
, 设为true
则会在该应用的任何版本恢复数据。
Registering for Android Backup Service
不同的设备对 backup 的支持不同。
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" />
</application>
Extending BackupAgent
一般情况下,因为BackupAgentHelper
比BackupAgent
更方便,所以尽可能使用前者。
然而,如果有以下需求,还是要通过BackupAgent
来实现:
对数据进行版本控制
并不想备份整个数据文件
备份数据库中的数据
如果你想要备份SharedPreferences
和内部存储
中的整个文件,简单地使用BackupAgentHelper
即可。
Performing backup
oldState
上一次备份的状态,主要是FileDescriptor
// 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
}
data
要备份的数据,通过byte
字节流的方式存储
// 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);
newState
本次备份的状态,作为下次备份时的oldState
Performing restore
data
用来恢复数据的 `BackupDataInput`.
appVersionCode
android:versionCode
newState
恢复后,需要把data里的ParcelFileDescriptor
写到newState
中,它会以下次onBackup
的oldState
被用到。
@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
SharedPreferencesBackupHelper
FileBackupHelper
Backing up SharedPreferences
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);
}
}
Note:
SharedPreferences
are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
Backing up other files
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);
}
}
非线程安全,需要手动加锁
// Object for intrinsic lock
static final Object sDataLock = new Object();
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");
}
@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);
}
}
Checking the Restore Data Version
android:versionCode
BackupManager
恢复数据前会检查versionCode
字段,如果待恢复的版本高于当前应用版本,BackupManager
不会调用onRestore()
来恢复数据。android:restoreAnyVersion
设定true
orfalse
指明你是否希望通过应用的版本来决定是否恢复数据。设为true
,那么BackupManager
将不会去检查android:versionCode
,而是调用你的onRestore()
方法。
你可以通过下面的方法来获取appVersionCode
.
PackageInfo info;
try {
String name = getPackageName();
info = getPackageManager().getPackageInfo(name,0);
} catch (NameNotFoundException nnfe) {
info = null;
}
int version;
if (info != null) {
version = info.versionCode;
}
Requesting Backup
使用
dataChanged()
来请求备份,这个不会立即执行,会在未来某个合适的时间内执行开发时使用
bmgr
工具来随时备份
Requesting Restore
自动检测备份数据
requestRestore()
bmgr
工具
Testing Your Backup Agent
安装应用
-
确定备份功能可用
使用模拟器
adb shell bmgr enable true
If using a device, open the system Settings, select Backup & reset, then enable Back up my data and Automatic restore.
在用户改变数据的地方调用
dataChanged()
然后执行:adb shell bmgr backup your.package.name
‘
-
初始化备份操作
adb shell bmgr run
-
卸载应用
adb uninstall your.package.name
重新安装
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。