在<application>节点中有一个很是重要的属性,那就是backupAgent。这里咱们将它单独列出来,从基本含义,用法及其相关属性等方面来详细介绍一下。java
android:backupAgent用来设置备份代理。对于大部分应用程序来讲,都或多或少保存着一些持久性的数据,好比数据库和共享文件,或者有本身的配置信息。为了保证这些数据和配置信息的安全性以及完整性,Android提供了这样一个机制。android
咱们能够经过这个备份机制来保存配置信息和数据以便为应用程序提供恢复点。若是用户将设备恢复出厂设置或者转换到一个新的Android设备上,系统就会在应用程序从新安装时自动恢复备份数据。这样,用户就不须要从新产生它们之前的数据或者设置了。这个进程对于用户是彻底透明的,而且不影响其自身的功能或者应用程序的用户体验。shell
在备份操做的过程当中,Android备份管理器查询应用程序须要备份的数据,接着将这些数据发送到备份传输点上,由备份传输点发送到云存储器上。数据库
在恢复操做中,备份管理器从备份传输点中检索到备份数据而且将它返回到应用程序上,这样该程序就能将数据恢复到设备上了。恢复操做也能够由应用程序主动发起,在应用程序被安装而且存在与用户相关的备份数据时,Android能自动恢复操做。恢复备份数据主要发生在两个场景,一是在用户重置设备或者升级到新设备后,二是之前装过的应用程序再次被安装的时候。安全
另外,咱们须要注意,备份服务不能将数据传输到另外一个客户端上,不能用于保存应用程序生命周期中须要访问的数据,不能任意读写,且只能经过备份服务来访问。app
备份传输点是Android备份框架的客户端组件,它是由设备制造商以及服务提供商定制的。备份传输点对于不一样的设备或许不一样,而且对于应用程序是透明的。备份管理器API使得应用程序独立于实际的备份传输,也就是说,应用程序经过一套固定的API与备份管理器进行通讯,无论底层传输如何处理。但不是全部设备都支持备份,不过这不会对应用程序的运行产生任何负面影响。框架
为了实现应用程序数据的备份,就必须实现一个备份代理。实现的备份代理是由备份管理器调用的,它用来提供须要备份的数据。当从新安装应用程序时,它也能够被调用以便于恢复备份数据。dom
要实现备份代理,就必须作两件事,一是实现BackupAgent或者BackupAgentHelper的子类,二是在Manifest文件内用android:backupAgent属性声明备份代理。ide
首先,咱们来看看BackupAgent类提供的方法,以下表所示:函数
方法描述 | 说明 |
public final void fullBackupFile(File file,FullBackupDataOutput output) | 写入做为备份操做一部分的一个完整文件,该方法中的参数以下所示: file:须要备份的文件。这个文件必须存在而且能够被调用者读取。 output:须要备份的文件中的数据将要保存的目的地 |
public abstract void onBackup(ParcelFileDescriptor oldState,BackupDataOutput data,ParcelFileDescriptor newState) | 请求应用程序写入全部上次执行的备份操做后有变更的数据。以前备份的状态经过oldState文件描述,若是oldState是null,则说明没有有效的旧状态而且此时应用程序应该执行一次彻底备份。该方法中的参数以下所示: oldState:应用程序提供的打开的只读ParcelFileDescriptor,它指向最后备份的状态。它能够是null。 data:它是一个结构化封装的,打开的,可读写的文件描述,指向备份数据的目的地。 newState:打开的,可读写的ParcelFileDescriptor,指向一个空文件,应用程序应该在这里记录须要备份的状态。 注意:这个函数可能抛出IOException异常 |
public void onCreate() | 在真正执行备份或者操做以前执行一次初始化操做的地方 |
public void onDestroy() | 销毁此代理时被调用 |
public abstract void onRestore(BackupDataInput data,int appVersionCode,ParcelFileDescriptor newState) | 应用程序正在从备份文件中恢复而且应该使用备份的内容替换掉全部已经存在的数据。该方法的参数以下所示。 data:结构化封装了一个打开的,只读的文件描述,指向应用程序数据的快照。 appVersionCode:由AndroidManifest.xml文件中的android:versionCode属性提供的应用程序版本信息。 newState:打开的,可读写的ParcelFileDescriptor,指向一个空文件,应用程序应该在恢复它的数据以后记录它的最终备份状态。 |
而后,咱们经过一个简单的实例来讲明上表中一些重要方法的调用时间点,包括onBackup(),onCreate()和onRestore()。在这个实例中,仅仅在里面添加一些日志来讲明问题,具体步骤以下:
①仍是之前面的HelloWorld为例,建立一个HelloWorld,或者删除掉前面的实验代码,恢复刚建立时候的样子。
②在应用程序项目中添加一个继承自BackupAgent的类,名叫MyBackupAgent
③添加完MyBackupAgent类后,这个类中已经默认添加了onBackup()和onRestore()两个回调方法。这里咱们还须要添加onCreate()回调方法。
④在各个回调方法中添加打印日志的代码,完成后的代码以下所示:
public class MyBackupAgent extends BackupAgent { private static final String TAG="MyBackupAgent"; @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { Log.e(TAG,"onBackup running"); } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { Log.e(TAG,"onRestore running"); } @Override public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate running"); }
⑤将MyBackupAgent类配置到AndroidManifest.xml中,代码以下所示:
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:backupAgent=".MyBackupAgent">
⑥此时咱们就已经完成基本配置。经过上面的描述可知,执行备份有两种方法,一种是经过BackupManager.dataChanged()方法执行备份,另外一种则是经过bmgr工具执行备份。这里咱们首先演示第一种方法。在建立项目时,默认生成的MainActivity.java文件和AndroidManifest.xml文件使其具备备份的功能。此外,还要添加一个按钮,这个按钮的做用是当你单击它后执行备份动做。修改后的代码以下:
public class MainActivity extends Activity { private Button myBackup; private BackupManager backupManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.myBackup=(Button)findViewById(R.id.myBackup); this.backupManager=new BackupManager(this); this.myBackup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { backupManager.dataChanged();//此处执行备份 } }); }}
⑦完成了这些工做,就能够开始执行一次备份。须要说明是,咱们的测试可能在真机或者模拟器上巡行的时候要确保备份功能处于打开状态,所以,咱们执行以下命令:
adb shell bmgr enabled
若是获得提示是“Backup Manager currently disable”,则说明备份管理器处于禁用状态,此时就须要执行步骤8启用备份管理器,不然,能够跳过这个步骤。
⑧使用如下命令启用备份管理器:
adb shell bmgr enable true
值得注意的是,这个时候单击按钮,代码就强制执行了backupManager.dataChanged(),但此时Android系统只是简单地将此次备份请求加入了备份消息队列中,并无执行MyBackupAgent的onBackup()方法。要执行备份与还原,还须要继续完成下面的步骤。
⑨使用bmgr工执行一次备份操做,相关命令以下所示:
adb shell bmgr run
全部的命令步骤以下图:
此时咱们会获得以下所示的执行结果:
如今来解读一下这个日志。
第一,经过日志的1-4行能够看到,此时备份管理器(BackupManagerService)已经作好了备份的准备。
第二,经过日志的第5行到最后一行能够看到,这时的Android正在执行一个备份任务,这个任务作了不少重要的工做,具体以下:
㈠初始化实现的备份代理类,并调用类的onCreate()方法。
㈡执行备份并调用实现的备份代理类的onBackup()方法。
㈢完成之后会造成一个LocalTransport:。
此时值得注意的是,当执行 adb shell bmgr run命令后,它会通知ActivityThread咱们须要一个备份代理,而后由ActivityThread按照输入到ActivityThread中的参数找到须要初始化的备份代理MyBackAgent,接着它会回调onCreate()方法作一次初始化操做,最后备份服务会回调onBackup()方法,开始执行真正的备份。
还有另外一种方法,以下所示:
没有执行第9步的时候,经过在按钮的单击事件中添加dataChanged()方法来对备份队列进行操做。不过,bmgr工具提供了另外一个手段来完成dataChanged()所作的事情:
adb shell bmgr backup you.package.name
对于应用程序来讲,这个命令应该是adb shell bmgr backup com.example.liyuanjing.helloworld。执行此命令后,在执行adb shell bmgr run 命令,将会有相同的结果。
对于上述知识,咱们经过一个简单的实例来讲明如何实现备份。其实你们都知道,备份是为了以防万一,既然备份了,那么怎么从备份中恢复呢?接下来就对工程稍加改动,从而实现恢复功能,具体步骤以下:
①修改MyBackupAgent类,这里写一些备份数据以便恢复时使用,修改后的代码以下:
public class MyBackupAgent extends BackupAgent { private static final String TAG="MyBackupAgent"; @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { Log.e(TAG,"onBackup running"); ByteArrayOutputStream bufStream=new ByteArrayOutputStream(); DataOutputStream outWrite=new DataOutputStream(bufStream); outWrite.write(1); byte[] buffer=bufStream.toByteArray(); int len =buffer.length; data.writeEntityHeader("DATA",len); data.writeEntityData(buffer,len); } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { Log.e(TAG,"onRestore running"); } @Override public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate running"); } }
②修改MainActivity文件,添加按钮执行恢复操做,具体代码以下:
public class MainActivity extends Activity { private static final String TAG="MainActivity"; private Button myBackup; private Button restore; private BackupManager backupManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.myBackup=(Button)findViewById(R.id.myBackup); this.backupManager=new BackupManager(this); this.myBackup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { backupManager.dataChanged();//此处执行备份 } }); this.restore=(Button)findViewById(R.id.restore); this.restore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { backupManager.requestRestore(new RestoreObserver() { @Override public void restoreFinished(int error) { super.restoreFinished(error); Log.e(TAG,"restoreFinished running"); } }); } }); }}
③运行程序,获得下图:
只要按照前面的操做作一遍便可。当完成回复操做后,获得以下日志:
有必要对这个日志进行讲解一下,咱们能够看到第1行备份管理器以@pm@为条件检索须要还原的备份数据,这里找到了3个符合条件的数据,分别在2-4这三行。
其次,调用了nextRestorePackage()方法查询下一个须要恢复的应用程序的包名,如第6行所示。
而后,,初始化备份代理类,并调用该类的onCreate()方法,如第7行所示。
再者,查询须要恢复的数据,并调用onRestore()方法执行恢复操做,如8-11行所示。
最后,结束恢复操做并通知应用程序,如18-21行所示。
此时,咱们已经介绍完了备份功能,还原功能及其使用方法。在这个过程当中不难发现,bmgr工具起到了相当重要的做用。接下来,咱们就将结合bmgr的源代码进一步讲解如何使用这个重要的工具。
bmgr是Android提供的一个shell工具,它使咱们能方便地与备份管理器进行交互。但须要注意的是,备份与还原的相关功能只在Android2.2或者更高的版本中才可使用。
bmgr还提供了一些命令来触发备份和还原操做,所以,咱们不须要反复去擦除数据以测试应用程序的备份代理。这里提供的操做主要有强制备份操做,强制还原操做,擦出备份数据以及启用与禁用备份。
①强制备份操做
一般,应用程序必须经过dataChanged()方法来通知备份管理器咱们的数据发生了变化,而后备份管理器会在未来的某一个时刻调用实现备份代理的onBackup()方法。此外,还可使用命令行形式来取代调用dataChanged()方法,其语法结构以下:
adb shell bmgr backup <package-name>
咱们先来看看下面的代码片断:
private void doBackup(){
boolean isFull=false;
String pkg=nextArg();
.......
try{
mBmgr.dataChanged(pkg);
}catch(RemoteException e){
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
能够看到,备份操做实际上也是执行了一次dataChanged()操做。
当完成以上命令后,备份队列里面就会增长一个备份请求,它会在未来的某一个时刻执行备份,而咱们执行一下命令时:
adb shell bmgr run
Android系统的行为将会是怎样的呢?再来看看下面的代码:
private void doRun(){
try{
mBmgr.dataChanged(pkg);
}catch(RemoteException e){
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
上面的代码代表当咱们执行run命令后,系统会强制执行备份队列的备份请求,由于它执行了备份管理器的backupNow()方法。
②强制还原操做
和备份操做不同的是,还原操做会当即执行。目前,Android系统提供了两种类型的还原操做:第一种是使用已有的备份数据去还原整个设备的数据,第二种则是使用某个特定的应用程序将已经备份的数据还原。它们的命令格式以下所示:
Ⅰadb shell bmgr restore <token>
Ⅱadb shell bmgr restore <package>
当执行1命令的时候,它会按照token的输入值找到合适的备份数据去还原整个系统。
当执行2命令的时候,与代码中执行备份管理器的requestRestore()方法同样,它会直接调用备份代理类的onRestore()方法。
如今来看看还原操做的源代码:
private void doRestore(){
String arg=nextArg();
....
if(arg.indexOf(',')>=0){
//包名
doRestorePackage(arg);
}else{
try{
long token=Long.parseLong(arg,16);
doRestoreAll(token);
}catch(NumberFormatException e){
.....
}
}
}
从上面的代码能够看到,当输入的是包名时,将执行一个名叫doRestorePackage()的方法,这个方法主要调用了还原接口的restorePackage()方法,用来还原一个应用程序的备份数据。而当输入的是token的时候,则执行了一个名叫doRestoreAll()方法,这个方法调用了还原接口的restoreAll() 方法,将查询到的全部应用程序的备份数据还原到对应的应用程序上去。
③擦除备份数据
该操做用于单一的应用程序数据,它在开发备份代理的时候很是有用。使用bmgr工具的wipe命令,能够擦除应用程序的数据:
adb shell bmgr wipe <package>
其中<package>是应用程序正式的包名称,该应用程序的数据是但愿被擦除的。
Android执行该命令的过程以下所示:
private void doWipe(){
String pkg=nextArg();
....
try{
mBmgr.clearBackupData(pkg);
System.out.println("Wiped backup data for"+pkg);
}catch(RemoteException e){
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
如上面代码第5行所示,此命令执行了备份管理器上的clearBackupData()方法,用于擦除对应应用程序备份的数据。
④启动与禁用备份
使用一下命令,能够查看备份管理器是不是可操做的:
adb shell bmgr enabled
也能够用以下命令来直接禁用或启用备份管理器:
adb shell bmgr enable <boolean>
其中boolean或者为true,或者为false,这与设备的设置里禁用或启用备份是一致的。
做为对知识的深挖,有必要介绍一下备份管理器,方法以下表:
方法原型 | 说明 | 使用方法示例 |
public Backup Manager(Context context) | 经过此方法,可经过上下文构造一个备份管理器实例。经过这个实例,咱们能够与Android备份系统交互 | BackupManager mBackupManager; mBackupManager=new BackupManager (Context); |
public void dataChanged() | 调用此方法的目的是通知Android备份系统,应用程序但愿备份新的修改到它的备份数据上 | mBackupManager.dataChanged(); |
public static void dataChanged(String packageName) | 调用此方法的目的是指明packageName所对应的应用程序为一次备份。 注意:当调用者与参数描述的应用程序包没有运行在相同的uid下时,使用这个方法则须要在引用程序的AndroidManifest.xml文件中声明android.permission.BACKUP权限 |
mBackupManager.dataChanged("com.example.liyuanjing.helloworld"); |
public int requestRestore(RestoreObserver observer) | 调用此方法是目的是强制从备份数据集中恢复应用程序的数据。 observer是一个恢复执行的观察者用于通知应用程序恢复的执行状态,包括以下的方法。 1.onUpdate():通知调用者应用程序当前的恢复操做正在执行。 2.restoreStarting():通知调用者应用程序当前的恢复操做已经启动。 3.restoreFinished():通知调用者应用程序当前的恢复操做已经完成。 |
mBackupManager.requestRestore(new RestoreObserver(){ @Override public void restoreFinished(int error){ super.restoreFinished(error); } }); |
如今你们已经学习如何使用backupAgent类和bmgr工具实现备份与恢复。在使用backupAgent类的过程当中,咱们发现直接使用这个类来实现备份时,须要管理的细节有不少,这致使使用时不太方便。好比,须要管理备份数据的新老状态以及备份数据的关键字等细节问题。在某些特定的场景下,好比在咱们打算备份一个完整的文件时,这些文件能够是保存在内部存储器中的文件或者共享文件等,Android SDK就提供了一个帮助类用以简化代码复杂度,它的名字叫BackupAgentHelper。
Android框架提供了两种不一样的帮助类,它们是SharedPreferencesBackupHelper和 FileBackupHelper,前者用于备份SharedPreferences文件,后者用于备份来自内部存储器的文件。
值得注意的是,对于每个须要加到BackAgentHelper中的帮助类,咱们都必须在BackupAgentHelper和onCreate()方法中作两件事:实例化所须要的帮助类,调用addHelper()方法将帮助类添加到BackupAgentHelper中。
下面来尝试修改前面的HelloWorld项目。在这个过程当中,咱们还将使用BackupAgentHelper类来实现对一个文件的备份,具体操做步骤以下:
①修改MainActivity类,在myBackup按钮的单击事件中写一个文件,并将其存储在内部存储器中。修改后的代码以下所示:
public class MainActivity extends Activity { private static final String TAG="MainActivity"; public static final String DATA_FILE_NAME="saved_data"; private Button myBackup; private Button restore; private BackupManager backupManager; public static final Object[] sDataLock=new Object[0]; private File myFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.myBackup=(Button)findViewById(R.id.myBackup); this.backupManager=new BackupManager(this); this.myFile=new File(getFilesDir(),MainActivity.DATA_FILE_NAME); this.myBackup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { synchronized (sDataLock){ try { RandomAccessFile file=new RandomAccessFile(myFile,"rw"); file.writeInt(1); } catch (IOException e) { e.printStackTrace(); } backupManager.dataChanged();//加入备份队列准备备份 } } }); this.restore=(Button)findViewById(R.id.restore); this.restore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { backupManager.requestRestore(new RestoreObserver() { @Override public void restoreFinished(int error) { super.restoreFinished(error); Log.e(TAG,"restoreFinished running"); } }); } }); this.initalFile();//初始化文件 } private void initalFile(){ RandomAccessFile file; synchronized (sDataLock){ boolean exists=this.myFile.exists(); try { file=new RandomAccessFile(this.myFile,"rw"); if(exists){ file.writeInt(1); }else{ file.setLength(0L); file.write(1); } } catch (IOException e) { e.printStackTrace(); } } }}
②新建一个继承自BackupAgentHelper的类来替代原有的BackupAgent子类,用以实现备份及恢复,完成后的代码以下所示:
public class MyBackupAgentHelper extends android.app.backup.BackupAgentHelper { private static final String TAG="BackupAgentHelper"; public static final String FILE_HELPER_KEY="myback"; @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { //这里咱们无须要作任何任何事情,只须要把它交给框架便可 synchronized (MainActivity.sDataLock){ super.onBackup(oldState, data, newState); } Log.e(TAG,"onBackup is running"); } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { //这里咱们无须要作任何任何事情,只须要把它交给框架便可 synchronized (MainActivity.sDataLock){ super.onRestore(data, appVersionCode, newState); } Log.e(TAG,"onRestore is running"); } @Override public void onCreate() { //这里咱们首先实例化一个FileBackupHelper实例 //并使用它做为参数之一调用addHelper()方法完成初始化 FileBackupHelper file_helper=new FileBackupHelper(this,MainActivity.DATA_FILE_NAME); addHelper(FILE_HELPER_KEY,file_helper); } }
③修改AndroidManifest.xml文件中的android:backupAgent,将MyBackupAgentHelper做为其属性
④编译并运行应用程序。此时,当单击应用程序的“Backup”按钮并运行adb shell bmgr run命令以后,Android就开始备份了。但当单击应用程序的restore按钮时,Android将恢复这个文件。
到这里,咱们就介绍完Android:backupAgent属性的做用及其用法了。