继承BackupAgentHelper类
如果要备份完整的文件(既可以是SharedPreferences文件也可以是内部存储文件),那么就应该使用BackupAgentHelper类来构建备份代理。用BackupAgentHelper类构建的备份代理比继承BackupAgent类所需要的代码要少的多,因为它不需要实现onBackup()和onRestore()方法。
BackupAgentHelper类的实现必须使用一个或多个备份帮助器。备份帮助器BackupAgentHelper类的一个特殊组件,它对特定类型的数据执行备份和恢复操作。Android框架目前提供两种不同类型的帮助器:
1. 备份SharedPreferences文件用的SharedPreferencesBackupHelper类
2. 备份内部存储器中文件的FileBackupHelper类。
可以在BackupAgentHelper类中包含多个帮助器,但是每种数据类型只能有一个帮助器。也就是说,如果有多个SharedPreferences文件,那么也只需要一个SharedPreferencesBackupHelper类。
对于每个要在BackupAgentHelper的实现类中添加的的帮助器,在实现类的onCreate()方法执行期间必须做以下事情:
1. 实例化一个帮助器类的实例,在这个类的构造器中,必须指定相应的要备份的文件。
2. 调用addHelper()方法,把帮助器添加给BackupAgentHelper的实现类。
备份SharedPreferences数据
当实例化了一个SharedPreferencesBackupHelper帮助器时,必须包含一个或多个SharedPreferences的文件名。
例如,要备份一个名叫“user_preferences”的SharedPreferences文件,一个完成的备份代理帮助器的实现如下:
publicclassMyPrefsBackupAgentextendsBackupAgentHelper{
// The name of the SharedPreferences file
staticfinalString PREFS ="user_preferences";
// A key to uniquely identify the set of backup data
staticfinalString PREFS_BACKUP_KEY ="prefs";
// Allocate a helper and add it to the backup agent
@Override
publicvoid onCreate(){
SharedPreferencesBackupHelper helper =newSharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
以上代码就是一个完整的备份代理,SharedPreferencesBackupHelper类包含了备份和恢复SharedPreferences文件所需的所有代码。
当备份管理器调用onBackup()和onRestore()时,BackupAgentHelper对象会调用备份帮助器来对指定的文件执行备份和恢复操作。
注意:SharedPreferences对象是线程安全的,因此可以在备份代理和其他的Activity中安全的读写共享偏好文件。
备份其他文件
在实例化一个FileBackupHelper类时,必须要包含一个或多个保存的应用程序的内部存储器中的文件名称。
例如,要备份两个名叫“scores”和“stats”的文件,备份代理应该像如下代码这样使用备份代理帮助器:
publicclassMyFileBackupAgentextendsBackupAgentHelper{
// The name of the SharedPreferences file
staticfinalString TOP_SCORES ="scores";
staticfinalString PLAYER_STATS ="stats";
// A key to uniquely identify the set of backup data
staticfinalString FILES_BACKUP_KEY ="myfiles";
// Allocate a helper and add it to the backup agent
void onCreate(){
FileBackupHelper helper =newFileBackupHelper(this, TOP_SCORES, PLAYER_STATS);
addHelper(FILES_BACKUP_KEY, helper);
}
}
FileBackupHelpler类包含了备份和恢复内部存储器中的文件的所有必需代码。
但是,读写内部存储器上的文件不是线程安全的,要确保备份代理与Activity不会同时读写文件,必需在每次执行读写操作时使用同步语句。例如,在任何一个读写内部存储器文件的Activity中,需要在同步语句中使用一个对象作为内部锁,如:
// Object for intrinsic lock
static final Object[] sDataLock = new Object[0];
每次读写文件时,都要用这个锁来创建一个同步语句。如,下面代码中的锁定语句用于把最后的成绩写入一个游戏文件:
try{
synchronized(MyActivity.sDataLock){
File dataFile =newFile(getFilesDir(), TOP_SCORES);
RandomAccessFile raFile =newRandomAccessFile(dataFile,"rw");
raFile.writeInt(score);
}
}catch(IOException e){
Log.e(TAG,"Unable to write to file");
}
应该使用相同的锁来锁定读数据的同步语句。
然后,在BackupAgentHelper的实现类中,必须重写onBackup()和onRestore()方法,用相同的内部锁来同步备份和恢复操作。例如,上面的MyFileBackupAgent类需要下面的方法:
@Override
publicvoid onBackup(ParcelFileDescriptor oldState,BackupDataOutput data,
ParcelFileDescriptor newState)throwsIOException{
// Hold the lock while the FileBackupHelper performs backup
synchronized(MyActivity.sDataLock){
super.onBackup(oldState, data, newState);
}
}
@Override
publicvoid onRestore(BackupDataInput data,int appVersionCode,
ParcelFileDescriptor newState)throwsIOException{
// Hold the lock while the FileBackupHelper restores the file
synchronized(MyActivity.sDataLock){
super.onRestore(data, appVersionCode, newState);
}
}
也就是说,需要你做的所有事情就是在onCreate()方法中把自己的FileBackupHelper的实现类对象添加到BackupAgentHelper的实现对象中,并重写BackupAgentHelper实现类的onBackup()和onRestore()方法。
检查恢复数据的版本
当备份管理器把数据保存到云端时,它会自动包含应用程序在清单文件的android:versionCode属性中定义的应用程序的版本号。在备份管理器调用备份代理来恢复数据之前,它会查看应用程序的android:versionCode属性中的版本号,并把它跟备份数据中记录的版本号进行比较,如果恢复数据集中记录的版本号比设备上当前应用程序的版本号新,那么说明用户使用了低版本的应用程序。因此备份管理器会中断恢复操作,并不调用onRestore()方法,因为在低版本的应用上恢复数据集被认为是毫无意义的。
可以使用android:restoreAnyVersion属性来改变这种行为。这个属性值可以true也可以是false,它用来指定应用程序的数据恢复操作是否要忽略版本号的设置。默认值是false。如果把这个属性定义为true,那么备份管理器会忽略android:versionCode属性,并在所有的情况中调用onRestore()方法。在重写的onRestore()方法中能够手动的检查版本号的差异,并采取必要的步骤让数据兼容,以避免版本冲突。
onRestore()方法的appVersionCode参数包含要恢复的数据集的版本号,它会在数据恢复操作期间为处理版本差异提供帮助。然后,用PackageInfo.versionCode成员属性来查询当前应用程序的版本号,如:
PackageInfo info;
try{
String name =getPackageName();
info =getPackageManager().getPackageInfo(name,0);
}catch(NameNotFoundException nnfe){
info =null;
}
int version;
if(info !=null){
version = info.versionCode;
}
然后就可以把从PackageInfo中获取的版本号与onRestore()方法中传入的版本号进行比较。
警告:必须理解设置android:restoreAnyVersion属性为true时给应用程序所带来的影响。如果在onRestore()方法执行期间,支持备份的每个应用程序版本没有针对数据格式的变化来使正确的账号,那么恢复到设备上的数据格式可能与设备上当前的应用程序版本不兼容。
请求备份数据
通过调用dataChanaged()方法,能够在任何时候请求备份操作。这个方法会通知备份管理器使用备份代理来数据备份操作。然后,备份管理器会在未来适当时机调用备份代理的onBackup()方法。通常,应该在每次数据变化时来请求备份操作(如因用户改变了应用程序偏好而执行的备份操作)。如果连续多次的调用了dataChanged()方法,那么在备份管理器请求备份代理之前,备份代理依然只会接受一次对onBackup()方法的调用。
注意:在开发应用程序时,能够使用bmgr工具来请求立即执行一个备份操作。
请求恢复数据
在应用程序的普通生存期间,不应该请求数据恢复操作。系统会自动检查备份数据,并在应用程序被安装的时候执行数据恢复操作。但是如果需要,通过调用requestRestore()方法可以手动的请求一个数据恢复操作。在这种情况下,备份管理器会调用onRestore()方法的实现,来恢复当前备份数据集中的数据。
注意:在开发应用程序时,用bmgr工具能够请求一个数据恢复的操作。
测试备份代理
一旦实现了备份代理,就能够使用bmgr工具来测试数据备份和数据恢复功能:
1. 把应用程序安装在一个合适的Android系统镜像上。
如果使用模拟器,要创建一个使用Android2.2(API级别8)的AVD;
如果使用一个实际设备,那么设备必须运行在Android2.2或更高的版本上,并且有内置的Google Play。
2. 确保备份功能可用
如果使用模拟器,用tools/路径下的下列命令来启用数据备份功能:
adb shell bmgr enable true
如果使用实际的设备,那么打开系统的Settings应用,选择Privacy,然后启用Back up my data和Automatic restore。
3. 打开应用程序并初始化一些数据
如果在应用程序中正确的实现了备份处理,那么每次数据变化时都应该请求一个备份操作。例如每次用户改变数据时,应用程序都应该调用dataChanged()方法,它会把备份请求添加到备份管理器的队列中。为了达到测试的目的,也可以用下列的bmgr命令来发起一个备份请求:
adb shell bmgr backup your.package.name
4. 初始化备份操作:
adb shell bmgr run
这样会强制备份管理器来执行它的队列中的所有备份请求。
5. 卸载应用程序
adb uninstall your.package.name
6. 重装应用程序
如果备份代理成功的执行了,那么所有在上述步骤4中初始化的数据都会被恢复。