android Data Backup(上)
原文
http://developer.android.com/guide/topics/data/backup.html
快速查看
·将用户数据备份到云中心以防丢失。
·如果用户升级到运行Android的新设备,程序可以恢复用户数据到新设备中。
·可方便地用BackupAgentHelper备份SharedPreference和私有文件。
·需要APILevel 8支持。
在本文中
基本情况
在Manifest中声明备份代理
为Android备份服务进行注册
继承BackupAgent
必需的方法
执行备份
执行恢复
继承BackupAgentHelper
备份SharedPreferences
备份私有文件
检查恢复数据的版本
请求备份
请求恢复
测试备份代理
关键类
BackupManager
BackupAgent
BackupAgentHelper
参阅
bmgrtool
为了给应用程序的数据和配置信息提供数据还原点,Android的备份backup服务允许把需持久保存的数据拷贝到远程“云”存储中。如果用户恢复了出厂设置或者换用新的Android设备,系统将在再次安装应用程序时自动恢复备份数据。这样,就不需要用户复制之前的数据和程序配置信息。整个过程对于用户而言完全透明,不会影响程序的功能和用户体验度。
在备份过程中(应用程序可发起请求),Android的备份管理器(BackupManager)将查找应用程序中需备份的数据,并把数据交给备份传输器,传输器再把数据传送给云存储。在恢复时,备份管理器从备份传输器取回备份数据并将其返回给应用程序,然后应用程序就能把数据恢复到设备上。应用程序也能够发起恢复请求,但不是必须的——如果程序安装完毕且存在用户相关的备份数据,Android会自动执行恢复操作。恢复备份数据主要发生于以下场合:用户重置设备或者升级到新设备后,以前装过的应用程序又被再次安装。
注意:备份服务并不是为以下用途设计的:与其它客户端同步、在程序正常生命周期内保存数据。备份数据不允许随意读写,除通过备份管理器提供的API外无法访问数据。
备份传输器是Android备份框架的客户端组件,它可由设备制造商和提供商定制。备份传输器可以因设备不同而不同,对于应用程序而言它是透明的。备份管理器的API将应用程序和实际备份传输器联接起来——程序通过一组固定的API与备份管理器进行通讯,而不必关心底层的传输过程。
并不是所有Android平台的设备都能支持数据备份。不过,即使设备不支持备份传输,对程序运行也不会有什么影响。如果确信用户将受益于数据备份服务,只管按照本文所述去实现、测试并发布即可,而不必去关心哪些设备会真正执行备份工作。就算是在不支持备份传输的设备上,程序仍然会正常运行,只是不能接收备份管理器的请求进行数据备份而已。
尽管对当前所传输内容一无所知,但尽管放心,备份数据是不能被设备上的其它程序读取的。在备份过程中,只有备份管理器和备份传输器有权限访问被提交的数据。
警告:因为云存储和传输服务依设备而各不相同,Android不保证使用备份服务数据的安全性。如果要利用备份服务保存敏感数据(比如用户名和密码),应该始终保持谨慎态度。
基本情况
为了备份应用程序数据,需要实现一个备份代理。此备份代理将被备份管理器调用,用于提供所需备份的数据。当程序重装时,还要调用此代理来恢复数据。备份管理器处理所有与云存储之间的数据传输工作(利用备份传输器),备份代理则负责所有对设备上数据的处理。
要实现备份代理,必须:
1.在manifest文件内用android:backupAgent属性声明备份代理。
2.用备份服务对应用程序进行注册。Google为大多数Android平台的设备提供了Android备份服务,必须对应用程序进行注册以便服务生效。为了在它们的服务器上存储数据,其它所有的备份服务提供方也都可能需要注册。
3.用以下两种方式之一进行备份代理的定义:
a)继承BackupAgent
BackupAgent类提供了核心接口,程序通过这些接口与备份管理器进行通讯。如果直接继承此类,必须覆盖onBackup()和onRestore()方法来处理数据的备份和恢复操作。
b)继承BackupAgentHelper
BackupAgentHelper类提供了BackupAgent类的易用性封装,它减少了需编写的代码数量。在BackupAgentHelper内,必须用一个或多个“helper”对象来自动备份和恢复特定类型的数据,因此不再需要实现onBackup()和onRestore()方法了。
Android目前提供两种backuphelper,用于从SharedPreferences和internalstorage备份和恢复整个的文件。
在Manifest中声明备份代理
这是最容易的一步,一旦确定了类名,就可在manifest的<application>标签内用android:backupAgent属性声明备份代理了。
例如:
<manifest...>
...
<applicationandroid:label="MyApplication"
android:backupAgent="MyBackupAgent">
<activity...>
...
</activity>
</application>
</manifest>
其它可能会用到的属性是android:restoreAnyVersion。这个属性用布尔值标明恢复数据时是否忽略当前程序和产生备份数据的程序之间的版本差异(默认值是“false”)。详情请参阅检查恢复数据的版本。
注意:备份服务和API只在运行APILevel 8(Android2.2)以上版本的设备上才可用,因此应把android:minSdkVersion属性设为“8”。当然,如果程序实现了良好的向后兼容性,可以仅针对APILevel 8以上版本的设备提供备份功能,而对其它旧版本设备则保持兼容即可。
为Android备份服务进行注册
Google为大多数Android2.2以上版本的设备提供了利用Android备份服务进行的备份传输服务。
为了程序能利用Android备份服务执行备份操作,必须对程序进行注册以获得一个BackupService Key,然后在Androidmanifest文件中声明这个Key。
要获取BackupService Key,请为Android服务进行注册。注册时会得到一个BackupService Key和Androidmanifest文件内相应的<meta-data>XML代码,这段代码必须包含在<application>元素下。例如:
<applicationandroid:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-dataandroid:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ"/>
</application>
android:name必须是"com.google.android.backup.api_key",android:value也必须是注册Android备份服务时收到的BackupService Key。
如果存在多个应用程序,必须根据各自的程序包名称(packagename)为每一个程序进行注册。
注意:即使设备能够支持,Android备份服务提供的备份传输器也不一定在所有Android平台的设备上都能执行。有些设备可能使用不同的传输器来为备份提供支持,有些设备可能根本就不支持备份,程序是无法知道设备使用何种传输器的。不过,假如为程序实现了备份,就必须为备份服务指定BackupService Key,这样设备利用Android备份服务进行传输时程序就能顺利执行备份工作。如果设备不使用Android备份服务,带BackupService Key的<meta-data>元素将被忽略。
继承BackupAgent
大多数应用程序应该不需要直接继承使用BackupAgent类,取而代之的是继承BackupAgentHelper类,并利用BackupAgentHelper内建的helper类自动备份和恢复文件。不过,如果需要实现以下目标的话,也许希望能直接继承BackupAgent:
·将数据格式版本化。例如需要在恢复数据时修正格式,可以建立一个备份代理,在数据恢复过程中如果发现当前版本和备份时的版本不一致,可以执行必要的兼容性修正工作。详情请参阅检查恢复数据的版本。
·不是备份整个文件,而是指定备份部分数据及指定恢复各部分数据。(这也有助于管理不同版本的数据,因为是把数据作为唯一Entity来读写,而不是读写整个文件。)
·备份数据库中的数据。如果用到SQLite数据库并且希望用户重装系统时能恢复其中数据,需要建立自定义的BackupAgent。它在备份时读取库中数据,而在恢复时建表并插入数据。
如果不需要执行以上的任务,而只是从SharedPreferences或内部存储备份完整的文件,请跳转到继承BackupAgentHelper。
必需的方法
通过继承BackupAgent创建备份代理时,必须实现以下回调方法:
onBackup()
备份管理器在程序请求备份后将调用本方法。如下文执行备份所述,在本方法中实现从设备读取应用程序数据,并把需备份的数据传递给备份管理器。
onRestore()
备份管理器在恢复数据时调用本方法(也可以主动请求恢复,但在用户重装应用程序时系统会自动执行数据恢复。)如下文执行恢复所述,备份管理器调用本方法时将传入备份的数据,然后就可把数据恢复到设备上。
执行备份
备份应用程序数据时,备份管理器将调用onBackup()方法。在此方法内必须把数据提供给备份管理器,然后数据被保存到云存储中。
只有备份管理器能够调用备份代理中的onBackup()方法。每当数据发生改变并需要执行备份时,必须调用dataChanged()发起一次备份请求(详情请参阅请求备份)。备份请求并不会立即导致onBackup()方法的调用。备份服务器会等待合适的时机,为上次备份操作后又发出备份请求的所有应用程序执行备份。
提示:在开发应用程序的过程中,可以用bmgr工具让备份管理器立即执行备份操作。
当备份管理器调用onBackup()方法时,传入以下三个参数:
oldState
已打开的、只读的文件描述符ParcelFileDescriptor,指向应用程序提供的有关上次备份数据状态的文件。这不是来自云存储的备份数据,而是记录上次调用onBackup()所备份数据相关状态信息的本地文件(如下文newState所定义,或来自下节onRestore())。因为onBackup()不允许读取保存于云存储的数据,可以根据此信息来判断数据自上次备份以来是否变动过。
data
BackupDataOutput对象,用于将备份数据传给备份管理器。
newState
已打开的、可读写的文件描述符ParcelFileDescriptor,指向一个文件,必须将提交给data参数的数据相关状态信息写入此文件(此状态信息可以简单到只是文件的最后修改时间戳)。备份管理器下次调用onBackup()时,本对象作为oldState传入。如果没有往newState写入信息,则备份管理器下次调用onBackup()时oldState将指向一个空文件。
利用以上参数,可以实现onBackup()方法如下:
1.通过比较oldState,检查自上次备份以来数据是否发生改变。从oldState读取信息的方式取决于当时写入的方式(见第3步)。最简单的记录文件状态的方式是写入文件的最后修改时间戳。以下是如何从oldState读取并比较时间戳的例子:
//获取oldState输入流
FileInputStreaminstream=newFileInputStream(oldState.getFileDescriptor());
DataInputStreamin=newDataInputStream(instream);
try{
//从state文件和数据文件获取最后修改时间戳
longstateModified=in.readLong();
longfileModified=mDataFile.lastModified();
if(stateModified!=fileModified){
//The file has been modified, so do abackup
//Or the time on the device changed, so be safe and do abackup
}else{
//Don't back up because the file hasn'tchanged
return;
}
}catch(IOExceptione){
// Unable to read state file... be safe and do abackup
}
如果数据没有发生变化,就不需要进行备份,请跳转到第3步。
2.在和oldState比较后,如果数据发生了变化,则把当前数据写入data以便将其返回并上传到云存储中去。
必须以BackupDataOutput中的“entity”方式写入每一块数据。一个entity是用一个唯一字符串键值标识的拼接二进制数据记录。因此,所备份的数据集其实上是一组键值对。
要在备份数据集中增加一个entity,必须:
1.调用writeEntityHeader(),传入代表写入数据的唯一字符串键值和数据大小。
2.调用writeEntityData(),传入存放数据的字节类型缓冲区,以及需从缓冲区写入的字节数(必须与传给writeEntityHeader()的数据大小一致)。
例如,以下代码把一些数据拼接为字节流并写入一个entity:
//为数据创建缓冲区流和输出流
ByteArrayOutputStreambufStream=newByteArrayOutputStream();
DataOutputStreamoutWriter=newDataOutputStream(bufStream);
//写入结构化的数据
outWriter.writeUTF(mPlayerName);
outWriter.writeInt(mPlayerScore);
//通过BackupDataOutput发送数据到备份管理器BackupManager
byte[]buffer=bufStream.toByteArray();
intlen=buffer.length;
data.writeEntityHeader(TOPSCORE_BACKUP_KEY,len);
data.writeEntityData(buffer,len);
对每一块需备份的数据都要执行以上操作。程序负责把数据切分为多个entity(当然也可以只用一个entity)。
3.无论是否执行备份(第2步),都要把当前数据的状态信息写入newStateParcelFileDescriptor指向的文件内。备份管理器会在本地保持此对象,以代表当前备份数据。下次调用onBackup()时,此对象作为oldState返回给应用程序,由此可以决定是否需要再做一次备份(如第1步所述)。如果不把当前数据的状态写入此文件,下次调用时oldState将返回空值。
以下例子把文件最后修改时间戳作为当前数据的状态存入newState:
FileOutputStream outstream = newFileOutputStream(newState.getFileDescriptor());
DataOutputStream out = newDataOutputStream(outstream);
long modified =mDataFile.lastModified();
out.writeLong(modified);
警告:如果应用程序数据存放于文件中,请确保使用同步语句(synchronized)来访问文件。这样在应用程序的Activity写文件时,备份代理就不会去读文件了。
更多相关文章
- “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
- Android(安卓)ORM 框架之 ActiveAndroid应用基础
- Android基础篇之AutoCompleteTextView
- android客户端连接人人网之二----获取朋友信息
- Android(安卓)Accessibility : TalkBack的状态读取
- AppWidget数据持久化