Android清單文件詳解(四) ---- backupAgent的用法

在<application>節點中有一個很是重要的屬性,那就是backupAgent。這裏咱們將它單獨列出來,從基本含義,用法及其相關屬性等方面來詳細介紹一下。java


1.backupAgent簡介


android:backupAgent用來設置備份代理。對於大部分應用程序來講,都或多或少保存着一些持久性的數據,好比數據庫和共享文件,或者有本身的配置信息。爲了保證這些數據和配置信息的安全性以及完整性,Android提供了這樣一個機制。android


咱們能夠經過這個備份機制來保存配置信息和數據以便爲應用程序提供恢復點。若是用戶將設備恢復出廠設置或者轉換到一個新的Android設備上,系統就會在應用程序從新安裝時自動恢復備份數據。這樣,用戶就不須要從新產生它們之前的數據或者設置了。這個進程對於用戶是徹底透明的,而且不影響其自身的功能或者應用程序的用戶體驗。shell


在備份操做的過程當中,Android備份管理器查詢應用程序須要備份的數據,接着將這些數據發送到備份傳輸點上,由備份傳輸點發送到雲存儲器上。數據庫


在恢復操做中,備份管理器從備份傳輸點中檢索到備份數據而且將它返回到應用程序上,這樣該程序就能將數據恢復到設備上了。恢復操做也能夠由應用程序主動發起,在應用程序被安裝而且存在與用戶相關的備份數據時,Android能自動恢復操做。恢復備份數據主要發生在兩個場景,一是在用戶重置設備或者升級到新設備後,二是之前裝過的應用程序再次被安裝的時候。安全


另外,咱們須要注意,備份服務不能將數據傳輸到另外一個客戶端上,不能用於保存應用程序生命週期中須要訪問的數據,不能任意讀寫,且只能經過備份服務來訪問。app


備份傳輸點是Android備份框架的客戶端組件,它是由設備製造商以及服務提供商定製的。備份傳輸點對於不一樣的設備或許不一樣,而且對於應用程序是透明的。備份管理器API使得應用程序獨立於實際的備份傳輸,也就是說,應用程序經過一套固定的API與備份管理器進行通訊,無論底層傳輸如何處理。但不是全部設備都支持備份,不過這不會對應用程序的運行產生任何負面影響。框架


2.如何使用backupAgent來實現備份



爲了實現應用程序數據的備份,就必須實現一個備份代理。實現的備份代理是由備份管理器調用的,它用來提供須要備份的數據。當從新安裝應用程序時,它也能夠被調用以便於恢復備份數據。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 命令,將會有相同的結果。


 

3.從備份中實現恢復


對於上述知識,咱們經過一個簡單的實例來講明如何實現備份。其實你們都知道,備份是爲了以防萬一,既然備份了,那麼怎麼從備份中恢復呢?接下來就對工程稍加改動,從而實現恢復功能,具體步驟以下:


修改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行所示。



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類的過程當中,咱們發現直接使用這個類來實現備份時,須要管理的細節有不少,這致使使用時不太方便。好比,須要管理備份數據的新老狀態以及備份數據的關鍵字等細節問題。在某些特定的場景下,好比在咱們打算備份一個完整的文件時,這些文件能夠是保存在內部存儲器中的文件或者共享文件等,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屬性的做用及其用法了。

相關文章
相關標籤/搜索