首页 > 代码库 > Android清单文件具体解释(四) ---- backupAgent的使用方法

Android清单文件具体解释(四) ---- backupAgent的使用方法

在<application>节点中有一个很重要的属性,那就是backupAgent。这里我们将它单独列出来,从基本含义,使用方法及其相关属性等方面来具体介绍一下。


1.backupAgent简单介绍


android:backupAgent用来设置备份代理。对于大部分应用程序来说,都或多或少保存着一些持久性的数据,比方数据库和共享文件。或者有自己的配置信息。为了保证这些数据和配置信息的安全性以及完整性。Android提供了这样一个机制。


我们能够通过这个备份机制来保存配置信息和数据以便为应用程序提供恢复点。

假设用户将设备恢复出厂设置或者转换到一个新的Android设备上,系统就会在应用程序又一次安装时自己主动恢复备份数据。

这样,用户就不须要又一次产生它们曾经的数据或者设置了。这个进程对于用户是全然透明的,而且不影响其自身的功能或者应用程序的用户体验。


在备份操作的过程中。Android备份管理器查询应用程序须要备份的数据,接着将这些数据发送到备份传输点上。由备份传输点发送到云存储器上。


在恢复操作中。备份管理器从备份传输点中检索到备份数据而且将它返回到应用程序上。这样该程序就能将数据恢复到设备上了。恢复操作也能够由应用程序主动发起。在应用程序被安装而且存在与用户相关的备份数据时,Android能自己主动恢复操作。恢复备份数据主要发生在两个场景,一是在用户重置设备或者升级到新设备后,二是曾经装过的应用程序再次被安装的时候。


另外。我们须要注意,备份服务不能将传输数据到还有一个client上,不能用于保存应用程序生命周期中须要訪问的数据。不能随意读写,且仅仅能通过备份服务来訪问。


备份传输点是Android备份框架的client组件,它是由设备制造商以及服务提供商定制的。备份传输点对于不同的设备也许不同,而且对于应用程序是透明的。备份管理器API使得应用程序独立于实际的备份传输,也就是说,应用程序通过一套固定的API与备份管理器进行通信,无论底层传输怎样处理。但不是全部设备都支持备份,只是这不会相应用程序的执行产生不论什么负面影响。


2.怎样使用backupAgent来实现备份



为了实现应用程序数据的备份。就必须实现一个备份代理。实现的备份代理是由备份管理器调用的。它用来提供须要备份的数据。

当又一次安装应用程序时。它也能够被调用以便于恢复备份数据。


要实现备份代理,就必须做两件事,一是实现BackupAgent或者BackupAgentHelper的子类。二是在Manifest文件内用android:backupAgent属性声明备份代理。

首先。我们来看看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文件使其具有备份的功能。此外,还要加入一个button。这个button的作用是当你单击它后运行备份动作。改动后的代码例如以下:


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


值得注意的是,这个时候单击button,代码就强制运行了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步的时候,通过在button的单击事件中加入dataChanged()方法来对备份队列进行操作。

只是,bmgr工具提供了还有一个手段来完毕dataChanged()所做的事情:


adb shell bmgr backup you.package.name


对于应用程序来说,这个命令应该是adb shell bmgr backup com.example.liyuanjing.helloworld。运行此命令后,在运行adb shell bmgr run 命令,将会有同样的结果。


3.从备份中实现恢复


对于上述知识,我们通过一个简单的实例来说明怎样实现备份。事实上大家都知道,备份是为了以防万一,既然备份了,那么怎么从备份中恢复呢?接下来就对project稍加修改,从而实现恢复功能。详细过程例如以下:


改动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文件,加入button运行恢复操作,详细代码例如以下:


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行所看到的。



4.怎样使用bmgr工具


此时,我们已经介绍完了备份功能。还原功能及其用法。在这个过程中不难发现,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类的过程中,我们发现直接使用这个类来实现备份时。须要管理的细节有非常多。这导致使用时不太方便。

比方,须要管理备份数据的新老状态以及备份数据的keyword等细节问题。在某些特定的场景下,比方在我们打算备份一个完整的文件时,这些文件能够是保存在内部存储器中的文件或者共享文件等。Android SDK就提供了一个帮助类用以简化代码复杂度,它的名字叫BackupAgentHelper。


Android框架提供了两种不同的帮助类,它们是SharedPreferencesBackupHelper和 FileBackupHelper,前者用于备份SharedPreferences文件,后者用于备份来自内部存储器的文件。


值得注意的是,对于每个须要加到BackAgentHelper中的帮助类,我们都必须在BackupAgentHelper和onCreate()方法中做两件事:实例化所须要的帮助类,调用addHelper()方法将帮助类加入到BackupAgentHelper中。


以下来尝试改动前面的HelloWorld项目。

在这个过程中,我们还将使用BackupAgentHelper类来实现对一个文件的备份,详细操作过程例如以下:


改动MainActivity类。在myBackupbutton的单击事件中写一个文件,并将其存储在内部存储器中。改动后的代码例如以下所看到的:


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”button并执行adb shell bmgr run命令之后,Android就開始备份了。但当单击应用程序的restorebutton时。Android将恢复这个文件。

到这里,我们就介绍完Android:backupAgent属性的作用及其使用方法了。

Android清单文件具体解释(四) ---- backupAgent的使用方法