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屬性的作用及其使用方法了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM