android Data Backup(下)

来源:互联网 发布:淘宝美工培训视频 编辑:程序博客网 时间:2024/05/29 17:56

执行恢复

恢复程序数据时,备份管理器将调用备份代理的onRestore()方法。调用此方法时,备份管理器会把备份的数据传入,以供恢复到设备中去。

只有备份服务器能够调用onRestore(),在系统安装应用程序并且发现有备份数据存在时,调用会自动发生。不过,也可以通过调用requestRestore()来发起恢复数据的请求(详情参阅请求恢复)。

: 在开发应用程序的过程中,可以用bmgr工具发起恢复数据的请求。

当备份管理器调用onRestore() 方法时,传入以下三个参数:

data

BackupDataInput对象,用以读取备份数据。

appVersionCode

整数,表示备份数据时应用程序manifest中的android:versionCode属性。可以用于核对当前程序版本并确定数据格式的兼容性。关于利用此版本号来处理不同版本恢复数据的详细情况,请参阅下文检查恢复数据的版本

newState

已打开的,可读写的文件描述符ParcelFileDescriptor,指向一个文件,这里必须写入最后一次提交data数据的备份状态。本对象在下次调用onBackup()时作为oldState 返回。回想一下,onBackup()方法也必须写入newState 对象——这里也同样要这么做。这样即使设备重置后第一次调用onBackup(),也能确保有可用的oldState对象能传给onBackup()方法。

在实现onRestore()时,应该对data 调用readNextHeader(),以遍历数据集里所有的entity。对其中每个entity须进行以下操作:

1. getKey()获取entity的键值。

2. 将此entity键值和已知键值清单进行比较,这个清单应该已经在BackupAgent继承类中作为字符串常量(staticfinal string)进行定义。一旦键值匹配其中一个键,就执行读取entity数据并保存到设备的语句:

1. getDataSize()读取entity数据大小并据其创建字节数组。

2. 调用readEntityData() ,传入字节数组作为获取数据的缓冲区,并指定起始位置和读取字节数。

3. 字节数组将被填入数据,按需读取数据并写入设备即可。

3. 把数据读出并写回设备以后,和上面onBackup()过程类似,把数据的状态写入newState 参数。

下面是把前一节例子中所备份的数据进行恢复的示例:

@Override 

public void onRestore(BackupDataInput data, int appVersionCode, 

                  ParcelFileDescriptor newState) throws IOException { 

   // 应该是只有一个entity

   // 但最安全的方法还是用循环来处理

   while (data.readNextHeader()) { 

       String key = data.getKey(); 

       int dataSize = data.getDataSize(); 

 

       // 如果键值是所需的(保存TopScore),注意这个键值是用于

       // 写入备份entityheader 

       if (TOPSCORE_BACKUP_KEY.equals(key)) { 

          // BackupDataInput创建输入流

          byte[] dataBuf = new byte[dataSize]; 

          data.readEntityData(dataBuf, 0, dataSize); 

          ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); 

          DataInputStream in = new DataInputStream(baStream); 

 

          // 从备份数据中读取playernamescore

          mPlayerName = in.readUTF(); 

          mPlayerScore = in.readInt(); 

 

          //Record the score on the device (to a file orsomething) 

          recordScore(mPlayerName, mPlayerScore); 

       } else { 

          // 不知道这个entity键值,跳过,(这本不该发生)

          data.skipEntityData(); 

       } 

   } 

 

   //Finally, write to the state blob (newState) that describes therestored data 

   FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); 

   DataOutputStream out = new DataOutputStream(outstream); 

   out.writeUTF(mPlayerName); 

   out.writeInt(mPlayerScore); 

}

在以上例子中,传给onRestore()appVersionCode 参数没有被用到。假如用户程序的版本已经降级(比如从1.5降到1.0),可能就会用此参数来选择备份数据。更多信息请参阅检查恢复数据的版本

关于BackupAgent的完整例子,请参阅例程备份和恢复中的ExampleAgent类。

 

 

继承BackupAgentHelper

如果要备份整个文件(来自SharedPreferences内部存储),应该用BackupAgentHelper创建备份代理来实现。因为不必实现onBackup()onRestore()了,BackupAgentHelper 创建备份代理所需的代码量将远远少于BackupAgent

BackupAgentHelper 的实现必须要使用一个或多个backuphelperbackuphelper是一种专用组件,BackupAgentHelper 用它来对特定类型的数据执行备份和恢复操作。Android框架目前提供两种helpers

·       SharedPreferencesBackupHelper用于备份SharedPreferences文件。

·       FileBackupHelper 用于备份来自内部存储的文件。

BackupAgentHelper中可包含多个helper,但对于每种数据类型只需用到一个helper 。也就是说,即使存在多个SharedPreferences 文件,也只需要一个SharedPreferencesBackupHelper

对于每个要加入BackupAgentHelperhelper,必须在onCreate() 中执行以下步骤:

1.         实例化所需的helper。在其构造方法里必须指定需备份的文件。

2.         调用addHelper() helper加入BackupAgentHelper

下一节描述了如何使用每种helper创建备份代理。

 

备份SharedPreferences

实例化SharedPreferencesBackupHelper时,必须包括一个或多个SharedPreferences 文件。

例如,假设需备份的SharedPreferences文件名为“user_preferences”,完整的使用BackupAgentHelper的备份代理代码类似如下:

public class MyPrefsBackupAgent extends BackupAgentHelper { 

   //SharedPreferences 文件名

   static final String PREFS = "user_preferences"; 

 

   // 唯一标识备份数据的键值

   static final String PREFS_BACKUP_KEY = "prefs"; 

 

   // 申请helper并加入备份代理

   @Override 

   public void onCreate() { 

       SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); 

       addHelper(PREFS_BACKUP_KEY, helper); 

   } 

}

好,这就是一个备份代理的完整实现。SharedPreferencesBackupHelper内含了备份和恢复SharedPreferences文件的所有代码。

当备份管理器调用onBackup() onRestore()时,BackupAgentHelper 调用helper来对给定文件执行备份和恢复操作。

注: SharedPreferences 是线程安全的,因此可以从备份代理和其它activity中安全地读写sharedpreferences文件。

 

备份其它文件

在实例化FileBackupHelper时,必须包含一个或多个保存于程序内部存储中的文件名称。(路径的描述方式类似getFilesDir(),并且作为openFileOutput() 写入文件的路径。)

比如,需要备份两个名为“scores”“stats”的文件,备份代理使用BackupAgentHelper 示例如下:

public class MyFileBackupAgent extends BackupAgentHelper { 

   //SharedPreferences文件的名称

   static final String TOP_SCORES = "scores"; 

   static final String PLAYER_STATS = "stats"; 

 

   // 唯一标识备份数据集的键值

   static final String FILES_BACKUP_KEY = "myfiles"; 

 

   // 申请helper并加入备份代理

   void onCreate() { 

       FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); 

       addHelper(FILES_BACKUP_KEY, helper); 

   } 

}

FileBackupHelper 包含了备份和恢复存于内部存储的文件所需的全部代码。

但是,读写内部存储文件不是线程安全的。要确保activity操作文件的时候备份代理不会去读写文件,每次读写文件时必须使用同步语句。比如,Activity读写文件时,需要用一个对象作为同步语句的内部锁。

// 内部锁对象

static final Object[] sDataLock = new Object[0];

有趣的事实:长度为零的数组要比普通对象更轻量化,因此用作内部锁会是个好主意。

然后,每次读写文件时用这个锁创建同步语句。以下是把游戏分数写入文件的同步语句示例:

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, "Unableto write to file"); 

}

应该用同一个锁同步读取文件的语句。

然后,在BackupAgentHelper内,必须覆盖onBackup()onRestore()方法,用同一个内部锁同步备份和恢复操作。比如,上例中MyFileBackupAgent需要以下方法:

@Override 

public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 

        ParcelFileDescriptor newState) throws IOException { 

   //Hold the lock while the FileBackupHelper performsbackup 

   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 thefile 

   synchronized (MyActivity.sDataLock) { 

       super.onRestore(data, appVersionCode, newState); 

   } 

}

好了,所有要做的工作仅仅是在onCreate()方法内加入FileBackupHelper,覆盖onBackup()onRestore() 并同步读写。

关于用FileBackupHelper实现BackupAgentHelper的例子,请参阅例程备份和恢复中的FileHelperExampleAgent 类。

 

 

检查恢复数据的版本

在把数据保存到云存储中去时,备份管理器会自动包含应用程序的版本号,版本号是在manifest文件的android:versionCode 属性中定义的。在调用备份代理恢复数据之前,备份管理器会查询已安装程序的android:versionCode并与记录在备份数据中的版本号相比较。如果备份数据的版本比设备上的要,则意味着用户安装了旧版本的程序。这时备份管理器将停止恢复操作,onRestore()方法也不会被调用,因为把数据恢复给旧版本的程序是没有意义的。

android:restoreAnyVersion属性可以取代以上规则。此属性用truefalse标明是否在恢复时忽略数据集的版本,默认值是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 中的version 和传入onRestore()appVersionCode 即可。

警告:请确认已经理解了android:restoreAnyVersion 设为true的后果。如果不是所有版本的程序都能在onRestore()时正确解析数据格式的差异,那么保存到设备上的数据格式可能会与已安装的版本不兼容。

 

 

请求备份

任何时候都可以通过调用dataChanged()来发起备份请求。此方法通知备份管理器用备份代理来备份数据。然后,备份管理器将会适时调用备份代理的onBackup()方法。通常每次数据发生变化时都应该请求备份数据(比如用户修改了需保存的程序配置)。如果在备份管理器实际执行前连续调用了dataChanged()很多次,代理仅会执行一次onBackup()

注: 在程序开发过程中,可以用bmgrtool发起备份请求,备份将会立即执行。

 

请求恢复

在程序正常的生命周期内,应该不需要发起恢复数据的请求。在程序安装完成时,系统会自动检查备份数据并执行恢复操作。不过必要时,也可以通过调用requestRestore()来人工发起恢复数据的请求。这时,备份管理器会调用onRestore(),并把现有备份数据集作为数据传入该方法。

注:在程序开发过程中,可以用bmgrtool发起恢复数据的请求。

 

 

测试备份代理

一旦实现了备份代理,就可以用bmgr按以下步骤测试备份和恢复功能了:

1. 在合适的Android系统镜像上安装应用程序

o    如果使用仿真器,须创建和使用Android2.2API Level8)以上版本的AVD

o    如果使用硬件设备,则此设备必须运行Android2.2以上版本并内置AndroidMarket功能。

2. 确保启用备份功能

o    如果使用仿真器,可以SDK tools/路径下用以下命令启用备份功能:

adb shell bmgr enable true

o    如果使用硬件设备,则在系统Settings 选择Privacy,启用Backup my data Automaticrestore

3. 运行程序并初始化一些数据。

如果程序已经正确地实现了备份代码,那每次数据变化时都会请求备份。例如,每当用户改变了一些数据,程序将调用dataChanged(),这就往备份管理器的请求队列里加入了一个备份请求。出于测试的目的,也可以用以下bmgr命令发起一个请求:

adb shell bmgrbackup your.package.name

4. 执行备份操作:

adb shell bmgr run

这一步强迫备份管理器执行所有已入队列的备份请求。

5. 卸载应用程序:

adbuninstall your.package.name

6. 重新安装应用程序。

如果备份代理成功运行,那第4步里备份的数据将会被恢复。

 

补充

         文章精选

                  [IBM]基于 android 数据备份恢复的一种实现

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 护照号变了机票怎么办 苹果se指纹坏了怎么办 月经推迟怎么办才能快点来 车载支架不粘了怎么办 otpc平板电脑无法开机怎么办 手表表轴掉了怎么办 鸡肉放冰箱臭了怎么办 鸡胸肉熟了腥怎么办 梦幻西游手游手机号换了怎么办 ppt做一半卡住了怎么办 吃了发霉的蚝油怎么办 蛋皮干燥起皮怎么办 wps卡顿资料没保存怎么办 手指受伤肉掉了怎么办 手机被wifi禁了怎么办 母乳一边是咸的怎么办 tcl电视蓝频了怎么办 长虹电视蓝频了怎么办 电视突然蓝频了怎么办 海信电视蓝频了怎么办 连网电视蓝频了怎么办 英雄联盟画面卡顿怎么办 长残了怎么办原来很帅 被吓到了怎么办没精神 宝宝吓着怎么办最有效 4个月婴儿易惊吓怎么办 心里有问题的人怎么办 减肥的时候想吃东西怎么办 大联盟ping很高怎么办 酷派手机弹广告怎么办 孕早期肚子紧绷怎么办 怀孕2个月同房了怎么办 怀孕前三月同房了怎么办 人流前三天同房了怎么办 人流后三天同房了怎么办 吃避孕药后月经量少怎么办 排卵日同房不想怀孕怎么办 排卵日同房没有怀孕怎么办 想怀孕想生儿子怎么办 到期大姨妈不来怎么办 怕怀孕月经不来怎么办