原文
http://developer.android.com/guide/topics/data/backup.html
快速查看
· 將用戶數據備份到雲中心以防丟失。
· 如果用戶升級到運行Android的新設備,程序可以恢復用戶數據到新設備中。
· 可方便地用BackupAgentHelper備份SharedPreference和私有文件。
· 需要APILevel 8支持。
在本文中
關鍵類
參閱
爲了給應用程序的數據和配置信息提供數據還原點,Android的備份backup服務允許把需持久保存的數據拷貝到遠程“雲”存儲中。如果用戶恢復了出廠設置或者換用新的Android設備,系統將在再次安裝應用程序時自動恢復備份數據。這樣,就不需要用戶複製之前的數據和程序配置信息。整個過程對於用戶而言完全透明,不會影響程序的功能和用戶體驗度。
在備份過程中(應用程序可發起請求),Android的備份管理器(BackupManager)將查找應用程序中需備份的數據,並把數據交給備份傳輸器,傳輸器再把數據傳送給雲存儲。在恢復時,備份管理器從備份傳輸器取回備份數據並將其返回給應用程序,然後應用程序就能把數據恢復到設備上。應用程序也能夠發起恢復請求,但不是必須的——如果程序安裝完畢且存在用戶相關的備份數據,Android會自動執行恢復操作。恢復備份數據主要發生於以下場合:用戶重置設備或者升級到新設備後,以前裝過的應用程序又被再次安裝。
注意:備份服務並不是爲以下用途設計的:與其它客戶端同步、在程序正常生命週期內保存數據。備份數據不允許隨意讀寫,除通過備份管理器提供的API外無法訪問數據。
備份傳輸器是Android備份框架的客戶端組件,它可由設備製造商和提供商定製。備份傳輸器可以因設備不同而不同,對於應用程序而言它是透明的。備份管理器的API將應用程序和實際備份傳輸器聯接起來——程序通過一組固定的API與備份管理器進行通訊,而不必關心底層的傳輸過程。
並不是所有Android平臺的設備都能支持數據備份。不過,即使設備不支持備份傳輸,對程序運行也不會有什麼影響。如果確信用戶將受益於數據備份服務,只管按照本文所述去實現、測試併發布即可,而不必去關心哪些設備會真正執行備份工作。就算是在不支持備份傳輸的設備上,程序仍然會正常運行,只是不能接收備份管理器的請求進行數據備份而已。
儘管對當前所傳輸內容一無所知,但儘管放心,備份數據是不能被設備上的其它程序讀取的。在備份過程中,只有備份管理器和備份傳輸器有權限訪問被提交的數據。
警告:因爲雲存儲和傳輸服務依設備而各不相同,Android不保證使用備份服務數據的安全性。如果要利用備份服務保存敏感數據(比如用戶名和密碼),應該始終保持謹慎態度。
爲了備份應用程序數據,需要實現一個備份代理。此備份代理將被備份管理器調用,用於提供所需備份的數據。當程序重裝時,還要調用此代理來恢復數據。備份管理器處理所有與雲存儲之間的數據傳輸工作(利用備份傳輸器),備份代理則負責所有對設備上數據的處理。
要實現備份代理,必須:
1. 在manifest文件內用android:backupAgent屬性聲明備份代理。
2. 用備份服務對應用程序進行註冊。Google爲大多數Android平臺的設備提供了Android備份服務 ,必須對應用程序進行註冊以便服務生效。爲了在它們的服務器上存儲數據,其它所有的備份服務提供方也都可能需要註冊。
3. 用以下兩種方式之一進行備份代理的定義:
BackupAgent 類提供了核心接口,程序通過這些接口與備份管理器進行通訊。如果直接繼承此類,必須覆蓋onBackup()和onRestore()方法來處理數據的備份和恢復操作。
BackupAgentHelper 類提供了BackupAgent 類的易用性封裝,它減少了需編寫的代碼數量。在BackupAgentHelper內,必須用一個或多個“helper”對象來自動備份和恢復特定類型的數據,因此不再需要實現onBackup()和onRestore()方法了。
Android目前提供兩種backuphelper,用於從SharedPreferences 和internalstorage備份和恢復整個的文件。
在Manifest中聲明備份代理
這是最容易的一步,一旦確定了類名,就可在manifest的<application> 標籤內用android:backupAgent屬性聲明備份代理了。
例如:
<manifest ... >
...
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
<activity ... >
...
</activity>
</application>
</manifest>
其它可能會用到的屬性是android:restoreAnyVersion。這個屬性用布爾值標明恢復數據時是否忽略當前程序和產生備份數據的程序之間的版本差異(默認值是“false”)。詳情請參閱檢查恢復數據的版本。
注意:備份服務和API只在運行APILevel 8(Android2.2)以上版本的設備上纔可用,因此應把android:minSdkVersion 屬性設爲“8”。當然,如果程序實現了良好的向後兼容性,可以僅針對APILevel 8以上版本的設備提供備份功能,而對其它舊版本設備則保持兼容即可。
爲Android備份服務進行註冊
Google爲大多數Android2.2以上版本的設備提供了利用Android備份服務進行的備份傳輸服務。
爲了程序能利用Android備份服務執行備份操作,必須對程序進行註冊以獲得一個BackupService Key,然後在Androidmanifest文件中聲明這個Key。
要獲取BackupService Key,請爲Android服務進行註冊。註冊時會得到一個BackupService Key和Androidmanifest文件內相應的<meta-data> XML代碼,這段代碼必須包含在<application> 元素下。例如:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" />
</application>
android:name 必須是"com.google.android.backup.api_key" ,android:value 也必須是註冊Android備份服務時收到的BackupService Key。
如果存在多個應用程序,必須根據各自的程序包名稱(packagename)爲每一個程序進行註冊。
注意:即使設備能夠支持,Android備份服務提供的備份傳輸器也不一定在所有Android 平臺的設備上都能執行。有些設備可能使用不同的傳輸器來爲備份提供支持,有些設備可能根本就不支持備份,程序是無法知道設備使用何種傳輸器的。不過,假如爲程序實現了備份,就必須爲備份服務指定BackupService Key,這樣設備利用Android備份服務進行傳輸時程序就能順利執行備份工作。如果設備不使用Android備份服務,帶BackupService Key的<meta-data>元素將被忽略。
繼承BackupAgent
大多數應用程序應該不需要直接繼承使用BackupAgent 類,取而代之的是繼承BackupAgentHelper類,並利用BackupAgentHelper內建的helper類自動備份和恢復文件。不過,如果需要實現以下目標的話,也許希望能直接繼承BackupAgent :
· 將數據格式版本化。例如需要在恢復數據時修正格式,可以建立一個備份代理,在數據恢復過程中如果發現當前版本和備份時的版本不一致,可以執行必要的兼容性修正工作。詳情請參閱檢查恢復數據的版本。
· 不是備份整個文件,而是指定備份部分數據及指定恢復各部分數據。(這也有助於管理不同版本的數據,因爲是把數據作爲唯一Entity來讀寫,而不是讀寫整個文件。)
· 備份數據庫中的數據。如果用到SQLite數據庫並且希望用戶重裝系統時能恢復其中數據,需要建立自定義的BackupAgent。它在備份時讀取庫中數據,而在恢復時建表並插入數據。
如果不需要執行以上的任務,而只是從SharedPreferences或內部存儲備份完整的文件,請跳轉到繼承BackupAgentHelper。
通過繼承BackupAgent創建備份代理時,必須實現以下回調方法:
備份管理器在程序請求備份後將調用本方法。如下文執行備份所述,在本方法中實現從設備讀取應用程序數據,並把需備份的數據傳遞給備份管理器。
備份管理器在恢復數據時調用本方法(也可以主動請求恢復,但在用戶重裝應用程序時系統會自動執行數據恢復。)如下文執行恢復所述,備份管理器調用本方法時將傳入備份的數據,然後就可把數據恢復到設備上。
執行備份
備份應用程序數據時,備份管理器將調用onBackup() 方法。在此方法內必須把數據提供給備份管理器,然後數據被保存到雲存儲中。
只有備份管理器能夠調用備份代理中的onBackup()方法。每當數據發生改變並需要執行備份時,必須調用dataChanged()發起一次備份請求(詳情請參閱請求備份)。備份請求並不會立即導致onBackup()方法的調用。備份服務器會等待合適的時機,爲上次備份操作後又發出備份請求的所有應用程序執行備份。
提示:在開發應用程序的過程中,可以用bmgr工具讓備份管理器立即執行備份操作。
當備份管理器調用onBackup()方法時,傳入以下三個參數:
oldState
已打開的、只讀的文件描述符ParcelFileDescriptor,指向應用程序提供的有關上次備份數據狀態的文件。這不是來自雲存儲的備份數據,而是記錄上次調用onBackup()所備份數據相關狀態信息的本地文件(如下文newState所定義,或來自下節onRestore())。因爲onBackup()不允許讀取保存於雲存儲的數據,可以根據此信息來判斷數據自上次備份以來是否變動過。
data
BackupDataOutput對象,用於將備份數據傳給備份管理器。
newState
已打開的、可讀寫的文件描述符ParcelFileDescriptor,指向一個文件,必須將提交給data參數的數據相關狀態信息寫入此文件(此狀態信息可以簡單到只是文件的最後修改時間戳)。備份管理器下次調用onBackup()時,本對象作爲oldState傳入。如果沒有往newState寫入信息,則備份管理器下次調用onBackup()時oldState 將指向一個空文件。
利用以上參數,可以實現onBackup()方法如下:
1. 通過比較oldState,檢查自上次備份以來數據是否發生改變。從oldState讀取信息的方式取決於當時寫入的方式(見第3步)。最簡單的記錄文件狀態的方式是寫入文件的最後修改時間戳。以下是如何從oldState讀取並比較時間戳的例子:
// 獲取oldState輸入流
FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
DataInputStream in = new DataInputStream(instream);
try {
// 從state文件和數據文件獲取最後修改時間戳
long stateModified = in.readLong();
long fileModified = mDataFile.lastModified();
if (stateModified != fileModified) {
//The file has been modified, so do abackup
//Or the time on the device changed, so be safe and do abackup
} else {
//Don't back up because the file hasn'tchanged
return;
}
} catch (IOException e) {
// Unable to read state file... be safe and do abackup
}
如果數據沒有發生變化,就不需要進行備份,請跳轉到第3步。
2. 在和oldState比較後,如果數據發生了變化,則把當前數據寫入data 以便將其返回並上傳到雲存儲中去。
必須以BackupDataOutput中的“entity”方式寫入每一塊數據。一個entity是用一個唯一字符串鍵值標識的拼接二進制數據記錄。因此,所備份的數據集其實上是一組鍵值對。
要在備份數據集中增加一個entity,必須:
1. 調用writeEntityHeader(),傳入代表寫入數據的唯一字符串鍵值和數據大小。
2. 調用writeEntityData(),傳入存放數據的字節類型緩衝區,以及需從緩衝區寫入的字節數(必須與傳給writeEntityHeader()的數據大小一致)。
例如,以下代碼把一些數據拼接爲字節流並寫入一個entity:
// 爲數據創建緩衝區流和輸出流
ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
DataOutputStream outWriter = new DataOutputStream(bufStream);
// 寫入結構化的數據
outWriter.writeUTF(mPlayerName);
outWriter.writeInt(mPlayerScore);
// 通過BackupDataOutput 發送數據到備份管理器BackupManager
byte[] buffer = bufStream.toByteArray();
int len = buffer.length;
data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len);
data.writeEntityData(buffer, len);
對每一塊需備份的數據都要執行以上操作。程序負責把數據切分爲多個entity(當然也可以只用一個entity)。
3. 無論是否執行備份(第2步),都要把當前數據的狀態信息寫入newState ParcelFileDescriptor指向的文件內。備份管理器會在本地保持此對象,以代表當前備份數據。下次調用onBackup()時,此對象作爲oldState返回給應用程序,由此可以決定是否需要再做一次備份(如第1步所述)。如果不把當前數據的狀態寫入此文件,下次調用時oldState 將返回空值。
以下例子把文件最後修改時間戳作爲當前數據的狀態存入newState:
FileOutputStream outstream = newFileOutputStream(newState.getFileDescriptor());
DataOutputStream out = newDataOutputStream(outstream);
long modified =mDataFile.lastModified();
out.writeLong(modified);
警告:如果應用程序數據存放於文件中,請確保使用同步語句(synchronized)來訪問文件。這樣在應用程序的Activity寫文件時,備份代理就不會去讀文件了。