資料備份
快速檢視
· 将使用者資料備份到雲中心以防丢失。
· 可友善地用backupagenthelper備份sharedpreference和私有檔案。
· 需要api level 8支援。
在本文中
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#basic">基本情況</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#required_method">必需的方法</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#performe_backup">執行備份</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#performe_restore">執行恢複</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#backup_other">備份私有檔案</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#check_restore_version">檢查恢複資料的版本</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#request_backup">請求備份</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#request_restore">請求恢複</a>
<a target="_blank" href="http://blog.csdn.net/chaoyu168/article/details/18.htm#test_agent">測試備份代理</a>
關鍵類
<a target="_blank" href="http://developer.android.com/reference/android/app/backup/backupmanager.html">backupmanager</a>
<a target="_blank" href="http://developer.android.com/reference/android/app/backup/backupagent.html">backupagent</a>
<a target="_blank" href="http://developer.android.com/reference/android/app/backup/backupagenthelper.html">backupagenthelper</a>
參閱
<a target="_blank" href="http://developer.android.com/guide/developing/tools/bmgr.html">bmgr tool</a>
注意:備份服務并不是為以下用途設計的:與其它用戶端同步、在程式正常生命周期内儲存資料。備份資料不允許随意讀寫,除通過備份管理器提供的api外無法通路資料。
備份傳輸器是android備份架構的用戶端元件,它可由裝置制造商和提供商定制。備份傳輸器可以因裝置不同而不同,對于應用程式而言它是透明的。備份管理器的api将應用程式和實際備份傳輸器聯接起來——程式通過一組固定的api與備份管理器進行通訊,而不必關心底層的傳輸過程。
并不是所有android平台的裝置都能支援資料備份。不過,即使裝置不支援備份傳輸,對程式運作也不會有什麼影響。如果确信使用者将受益于資料備份服務,隻管按照本文所述去實作、測試并釋出即可,而不必去關心哪些裝置會真正執行備份工作。就算是在不支援備份傳輸的裝置上,程式仍然會正常運作,隻是不能接收備份管理器的請求進行資料備份而已。
盡管對目前所傳輸内容一無所知,但盡管放心,備份資料是不能被裝置上的其它程式讀取的。在備份過程中,隻有備份管理器和備份傳輸器有權限通路被送出的資料。
警告:因為雲存儲和傳輸服務依裝置而各不相同,android不保證使用備份服務資料的安全性。如果要利用備份服務儲存敏感資料(比如使用者名和密碼),應該始終保持謹慎态度。
<a target="_blank">基本情況</a>
為了備份應用程式資料,需要實作一個備份代理。此備份代理将被備份管理器調用,用于提供所需備份的資料。當程式重裝時,還要調用此代理來恢複資料。備份管理器處理所有與雲存儲之間的資料傳輸工作(利用備份傳輸器),備份代理則負責所有對裝置上資料的處理。
要實作備份代理,必須:
3. 用以下兩種方式之一進行備份代理的定義:
<a target="_blank"> </a>
在manifest中聲明備份代理
例如:
<manifest ... >
...
<applicationandroid:label="myapplication"
android:backupagent="mybackupagent">
<activity ... >
...
</activity>
</application>
</manifest>
為android備份服務進行注冊
為了程式能利用android備份服務執行備份操作,必須對程式進行注冊以獲得一個backup service key,然後在android manifest檔案中聲明這個key。
<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備份服務時收到的backup service key。
如果存在多個應用程式,必須根據各自的程式包名稱(package name)為每一個程式進行注冊。
注意:即使裝置能夠支援,android備份服務提供的備份傳輸器也不一定在所有android 平台的裝置上都能執行。有些裝置可能使用不同的傳輸器來為備份提供支援,有些裝置可能根本就不支援備份,程式是無法知道裝置使用何種傳輸器的。不過,假如為程式實作了備份,就必須為備份服務指定backup service key,這樣裝置利用android備份服務進行傳輸時程式就能順利執行備份工作。如果裝置不使用android備份服務,帶backup service key的<meta-data>元素将被忽略。
繼承backupagent
· 不是備份整個檔案,而是指定備份部分資料及指定恢複各部分資料。(這也有助于管理不同版本的資料,因為是把資料作為唯一entity來讀寫,而不是讀寫整個檔案。)
<a target="_blank">必需的方法</a>
<a target="_blank" href="http://developer.android.com/reference/android/app/backup/backupagent.html#onbackup(android.os.parcelfiledescriptor,%20android.app.backup.backupdataoutput,%20android.os.parcelfiledescriptor)">onbackup()</a>
<a target="_blank" href="http://developer.android.com/reference/android/app/backup/backupagent.html#onrestore(android.app.backup.backupdatainput,%20int,%20android.os.parcelfiledescriptor)">onrestore()</a>
執行備份
oldstate
data
newstate
1. 通過比較oldstate,檢查自上次備份以來資料是否發生改變。從oldstate讀取資訊的方式取決于當時寫入的方式(見第3步)。最簡單的記錄檔案狀态的方式是寫入檔案的最後修改時間戳。以下是如何從oldstate讀取并比較時間戳的例子:
// 擷取oldstate輸入流
fileinputstream instream =newfileinputstream(oldstate.getfiledescriptor());
datainputstreamin=newdatainputstream(instream);
try{
// 從state檔案和資料檔案擷取最後修改時間戳
long statemodified =in.readlong();
long filemodified = mdatafile.lastmodified();
if(statemodified != filemodified){
// the file has been modified, so do a backup
// or the time on the device changed, so be safe and do a backup
}else{
// don't back up because the file hasn't changed
return;
}
}catch(ioexception e){
// unable to read state file... be safe and do a backup
}
如果資料沒有發生變化,就不需要進行備份,請跳轉到第3步。
2. 在和oldstate比較後,如果資料發生了變化,則把目前資料寫入data以便将其傳回并上傳到雲存儲中去。
要在備份資料集中增加一個entity,必須:
例如,以下代碼把一些資料拼接為位元組流并寫入一個entity:
// 為資料建立緩沖區流和輸出流
bytearrayoutputstream bufstream =newbytearrayoutputstream();
dataoutputstream outwriter =newdataoutputstream(bufstream);
// 寫入結構化的資料
outwriter.writeutf(mplayername);
outwriter.writeint(mplayerscore);
// 通過backupdataoutput 發送資料到備份管理器backup manager
byte[] buffer = bufstream.tobytearray();
int len = buffer.length;
data.writeentityheader(topscore_backup_key, len);
data.writeentitydata(buffer, len);
對每一塊需備份的資料都要執行以上操作。程式負責把資料切分為多個entity(當然也可以隻用一個entity)。
以下例子把檔案最後修改時間戳作為目前資料的狀态存入newstate:
fileoutputstream outstream = new fileoutputstream(newstate.getfiledescriptor());
dataoutputstream out = new dataoutputstream(outstream);
long modified = mdatafile.lastmodified();
out.writelong(modified);
警告:如果應用程式資料存放于檔案中,請確定使用同步語句(synchronized)來通路檔案。這樣在應用程式的activity寫檔案時,備份代理就不會去讀檔案了。
執行恢複
appversioncode
3. 位元組數組将被填入資料,按需讀取資料并寫入裝置即可。
下面是把前一節例子中所備份的資料進行恢複的示例:
@override
publicvoid onrestore(backupdatainput data,int appversioncode,
parcelfiledescriptor newstate)throwsioexception{
// 應該是隻有一個entity,
// 但最安全的方法還是用循環來處理
while(data.readnextheader()){
string key = data.getkey();
int datasize = data.getdatasize();
// 如果鍵值是所需的(儲存top score),注意這個鍵值是用于
// 寫入備份entity header
if(topscore_backup_key.equals(key)){
// 為backupdatainput建立輸入流
byte[] databuf =newbyte[datasize];
data.readentitydata(databuf,0, datasize);
bytearrayinputstream bastream =newbytearrayinputstream(databuf);
datainputstreamin=newdatainputstream(bastream);
// 從備份資料中讀取player name和score
mplayername =in.readutf();
mplayerscore =in.readint();
// record the score on the device (to a file or something)
recordscore(mplayername, mplayerscore);
}else{
// 不知道這個entity鍵值,跳過,(這本不該發生)
data.skipentitydata();
}
// finally, write to the state blob (newstate) that describes the restored data
fileoutputstream outstream =newfileoutputstream(newstate.getfiledescriptor());
dataoutputstreamout=newdataoutputstream(outstream);
out.writeutf(mplayername);
out.writeint(mplayerscore);
1. 執行個體化所需的helper。在其構造方法裡必須指定需備份的檔案。
下一節描述了如何使用每種helper建立備份代理。
publicclassmyprefsbackupagentextendsbackupagenthelper{
// sharedpreferences 檔案名
staticfinalstring prefs ="user_preferences";
// 唯一辨別備份資料的鍵值
staticfinalstring prefs_backup_key ="prefs";
// 申請helper并加入備份代理
@override
publicvoid oncreate(){
sharedpreferencesbackuphelper helper =newsharedpreferencesbackuphelper(this, prefs);
addhelper(prefs_backup_key, helper);
<a target="_blank">備份其它檔案</a>
publicclassmyfilebackupagentextendsbackupagenthelper{
// sharedpreferences檔案的名稱
staticfinalstring top_scores ="scores";
staticfinalstring player_stats ="stats";
// 唯一辨別備份資料集的鍵值
staticfinalstring files_backup_key ="myfiles";
void oncreate(){
filebackuphelper helper =newfilebackuphelper(this, top_scores, player_stats);
addhelper(files_backup_key, helper);
但是,讀寫内部存儲檔案不是線程安全的。要確定activity操作檔案的時候備份代理不會去讀寫檔案,每次讀寫檔案時必須使用同步語句。比如,activity讀寫檔案時,需要用一個對象作為同步語句的内部鎖。
// 内部鎖對象
staticfinalobject[] sdatalock =newobject[0];
有趣的事實:長度為零的數組要比普通對象更輕量化,是以用作内部鎖會是個好主意。
然後,每次讀寫檔案時用這個鎖建立同步語句。以下是把遊戲分數寫入檔案的同步語句示例:
synchronized(myactivity.sdatalock){
randomaccessfile rafile =newrandomaccessfile(datafile,"rw");
rafile.writeint(score);
log.e(tag,"unable to write to file");
應該用同一個鎖同步讀取檔案的語句。
publicvoid onbackup(parcelfiledescriptor oldstate,backupdataoutput data,
parcelfiledescriptor newstate)throwsioexception{
// hold the lock while the filebackuphelper performs backup
super.onbackup(oldstate, data, newstate);
}
parcelfiledescriptor newstate)throwsioexception{
// hold the lock while the filebackuphelper restores the file
super.onrestore(data, appversioncode, newstate);
<a target="_blank">檢查恢複資料的版本</a>
packageinfo info;
}catch(namenotfoundexception nnfe){
info =null;
int version;
if(info !=null){
version = info.versioncode;
<a target="_blank">請求備份</a>
<a target="_blank">請求恢複</a>
<a target="_blank">測試備份代理</a>
1. 在合适的android系統鏡像上安裝應用程式
o 如果使用仿真器,須建立和使用android 2.2(api level 8)以上版本的avd。
o 如果使用硬體裝置,則此裝置必須運作android 2.2以上版本并内置android market功能。
2. 確定啟用備份功能
o 如果使用仿真器,可以在sdk tools/路徑下用以下指令啟用備份功能:
adb shell bmgr enable true
o 如果使用硬體裝置,則在系統settings, 選擇privacy,啟用back up my data 和automatic restore。
3. 運作程式并初始化一些資料。
adb shell bmgr backup your.package.name
4. 執行備份操作:
adb shell bmgr run
這一步強迫備份管理器執行所有已入隊列的備份請求。
5. 解除安裝應用程式:
adb uninstall your.package.name
6. 重新安裝應用程式。
如果備份代理成功運作,那第4步裡備份的資料将會被恢複。
補充
文章精選
轉載:http://blog.csdn.net/chaoyu168/article/details/49071435