編輯:Android開發實例
前言
本章內容為Android開發者指南的 Framework Topics/Data Storage/Data Backup章節,譯為"數據備份",版本為Android 4.0 r1,翻譯來自:"呆呆大蝦"。
數據備份
譯者署名: 呆呆大蝦
譯者微博:http://weibo.com/popapa
版本:Android 4.0 r1
原文
http://developer.android.com/guide/topics/data/backup.html
快速查看
· 將用戶數據備份到雲中心以防丟失。
· 如果用戶升級到運行Android的新設備,程序可以恢復用戶數據到新設備中。
· 可方便地用BackupAgentHelper備份SharedPreference和私有文件。
· 需要API Level 8支持。
在本文中
基本情況
在Manifest中聲明備份代理
為Android備份服務進行注冊
繼承BackupAgent
必需的方法
執行備份
執行恢復
繼承BackupAgentHelper
備份SharedPreferences
備份私有文件
檢查恢復數據的版本
請求備份
請求恢復
測試備份代理
關鍵類
BackupManager
BackupAgent
BackupAgentHelper
參閱
bmgr tool
為了給應用程序的數據和配置信息提供數據還原點,Android的備份backup服務允許把需持久保存的數據拷貝到遠程“雲”存儲中。如果用戶恢復了出廠設置或者換用新的Android設備,系統將在再次安裝應用程序時自動恢復備份數據。這樣,就不需要用戶復制之前的數據和程序配置信息。整個過程對於用戶而言完全透明,不會影響程序的功能和用戶體驗度。
在備份過程中(應用程序可發起請求),Android的備份管理器(BackupManager)將查找應用程序中需備份的數據,並把數據交給備份傳輸器,傳輸器再把數據傳送給雲存儲。在恢復時,備份管理器從備份傳輸器取回備份數據並將其返回給應用程序,然後應用程序就能把數據恢復到設備上。應用程序也能夠發起恢復請求,但不是必須的——如果程序安裝完畢且存在用戶相關的備份數據,Android會自動執行恢復操作。恢復備份數據主要發生於以下場合:用戶重置設備或者升級到新設備後,以前裝過的應用程序又被再次安裝。
注意:備份服務並不是為以下用途設計的:與其它客戶端同步、在程序正常生命周期內保存數據。備份數據不允許隨意讀寫,除通過備份管理器提供的API外無法訪問數據。
備份傳輸器是Android備份框架的客戶端組件,它可由設備制造商和提供商定制。備份傳輸器可以因設備不同而不同,對於應用程序而言它是透明的。備份管理器的API將應用程序和實際備份傳輸器聯接起來——程序通過一組固定的API與備份管理器進行通訊,而不必關心底層的傳輸過程。
並不是所有Android平台的設備都能支持數據備份。不過,即使設備不支持備份傳輸,對程序運行也不會有什麼影響。如果確信用戶將受益於數據備份服務,只管按照本文所述去實現、測試並發布即可,而不必去關心哪些設備會真正執行備份工作。就算是在不支持備份傳輸的設備上,程序仍然會正常運行,只是不能接收備份管理器的請求進行數據備份而已。
盡管對當前所傳輸內容一無所知,但盡管放心,備份數據是不能被設備上的其它程序讀取的。在備份過程中,只有備份管理器和備份傳輸器有權限訪問被提交的數據。
警告:因為雲存儲和傳輸服務依設備而各不相同,Android不保證使用備份服務數據的安全性。如果要利用備份服務保存敏感數據(比如用戶名和密碼),應該始終保持謹慎態度。
基本情況
為了備份應用程序數據,需要實現一個備份代理。此備份代理將被備份管理器調用,用於提供所需備份的數據。當程序重裝時,還要調用此代理來恢復數據。備份管理器處理所有與雲存儲之間的數據傳輸工作(利用備份傳輸器),備份代理則負責所有對設備上數據的處理。
要實現備份代理,必須:
1. 在manifest文件內用android:backupAgent屬性聲明備份代理。
2. 用備份服務對應用程序進行注冊。Google為大多數Android平台的設備提供了Android備份服務 ,必須對應用程序進行注冊以便服務生效。為了在它們的服務器上存儲數據,其它所有的備份服務提供方也都可能需要注冊。
3. 用以下兩種方式之一進行備份代理的定義:
a) 繼承BackupAgent
BackupAgent 類提供了核心接口,程序通過這些接口與備份管理器進行通訊。如果直接繼承此類,必須覆蓋onBackup()和onRestore()方法來處理數據的備份和恢復操作。
b) 繼承BackupAgentHelper
BackupAgentHelper 類提供了BackupAgent 類的易用性封裝,它減少了需編寫的代碼數量。在BackupAgentHelper內,必須用一個或多個“helper”對象來自動備份和恢復特定類型的數據,因此不再需要實現onBackup()和onRestore()方法了。
Android目前提供兩種backup helper,用於從SharedPreferences 和internal storage備份和恢復整個的文件。
在Manifest中聲明備份代理
這是最容易的一步,一旦確定了類名,就可在manifest的<application> 標簽內用android:backupAgent屬性聲明備份代理了。
例如:
<manifest ... >
...
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
<activity ... >
...
</activity>
</application>
</manifest>
其它可能會用到的屬性是android:restoreAnyVersion。這個屬性用布爾值標明恢復數據時是否忽略當前程序和產生備份數據的程序之間的版本差異(默認值是“false”)。詳情請參閱檢查恢復數據的版本。
注意:備份服務和API只在運行API Level 8(Android 2.2)以上版本的設備上才可用,因此應把android:minSdkVersion 屬性設為“8”。當然,如果程序實現了良好的向後兼容性,可以僅針對API Level 8以上版本的設備提供備份功能,而對其它舊版本設備則保持兼容即可。
為Android備份服務進行注冊
Google為大多數Android 2.2以上版本的設備提供了利用Android備份服務進行的備份傳輸服務。
為了程序能利用Android備份服務執行備份操作,必須對程序進行注冊以獲得一個Backup Service Key,然後在Android manifest文件中聲明這個Key。
要獲取Backup Service Key,請為Android服務進行注冊。注冊時會得到一個Backup Service Key和Android manifest文件內相應的<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備份服務時收到的Backup Service Key。
如果存在多個應用程序,必須根據各自的程序包名稱(package name)為每一個程序進行注冊。
注意:即使設備能夠支持,Android備份服務提供的備份傳輸器也不一定在所有Android 平台的設備上都能執行。有些設備可能使用不同的傳輸器來為備份提供支持,有些設備可能根本就不支持備份,程序是無法知道設備使用何種傳輸器的。不過,假如為程序實現了備份,就必須為備份服務指定Backup Service Key,這樣設備利用Android備份服務進行傳輸時程序就能順利執行備份工作。如果設備不使用Android備份服務,帶Backup Service Key的<meta-data>元素將被忽略。
繼承BackupAgent
大多數應用程序應該不需要直接繼承使用BackupAgent 類,取而代之的是繼承BackupAgentHelper類,並利用BackupAgentHelper內建的helper類自動備份和恢復文件。不過,如果需要實現以下目標的話,也許希望能直接繼承BackupAgent :
· 將數據格式版本化。例如需要在恢復數據時修正格式,可以建立一個備份代理,在數據恢復過程中如果發現當前版本和備份時的版本不一致,可以執行必要的兼容性修正工作。詳情請參閱檢查恢復數據的版本。
· 不是備份整個文件,而是指定備份部分數據及指定恢復各部分數據。(這也有助於管理不同版本的數據,因為是把數據作為唯一Entity來讀寫,而不是讀寫整個文件。)
· 備份數據庫中的數據。如果用到SQLite數據庫並且希望用戶重裝系統時能恢復其中數據,需要建立自定義的BackupAgent。它在備份時讀取庫中數據,而在恢復時建表並插入數據。
如果不需要執行以上的任務,而只是從SharedPreferences或內部存儲備份完整的文件,請跳轉到繼承BackupAgentHelper。
必需的方法
通過繼承BackupAgent創建備份代理時,必須實現以下回調方法:
onBackup()
備份管理器在程序請求備份後將調用本方法。如下文執行備份所述,在本方法中實現從設備讀取應用程序數據,並把需備份的數據傳遞給備份管理器。
onRestore()
備份管理器在恢復數據時調用本方法(也可以主動請求恢復,但在用戶重裝應用程序時系統會自動執行數據恢復。)如下文執行恢復所述,備份管理器調用本方法時將傳入備份的數據,然後就可把數據恢復到設備上。
執行備份
備份應用程序數據時,備份管理器將調用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 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 以便將其返回並上傳到雲存儲中去。
必須以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 發送數據到備份管理器Backup Manager
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 = new FileOutputStream(newState.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
long modified = mDataFile.lastModified();
out.writeLong(modified);
警告:如果應用程序數據存放於文件中,請確保使用同步語句(synchronized)來訪問文件。這樣在應用程序的Activity寫文件時,備份代理就不會去讀文件了。
執行恢復
恢復程序數據時,備份管理器將調用備份代理的onRestore()方法。調用此方法時,備份管理器會把備份的數據傳入,以供恢復到設備中去。
只有備份服務器能夠調用onRestore(),在系統安裝應用程序並且發現有備份數據存在時,調用會自動發生。不過,也可以通過調用requestRestore()來發起恢復數據的請求(詳情參閱請求恢復)。
注: 在開發應用程序的過程中,可以用bmgr工具發起恢復數據的請求。
當備份管理器調用onRestore() 方法時,傳入以下三個參數:
data
BackupDataInput對象,用以讀取備份數據。
appVersionCode
整數,表示備份數據時應用程序manifest中的android:versionCode屬性。可以用於核對當前程序版本並確定數據格式的兼容性。關於利用此版本號來處理不同版本恢復數據的詳細情況,請參閱下文檢查恢復數據的版本。
newState
已打開的,可讀寫的文件描述符ParcelFileDescriptor,指向一個文件,這裡必須寫入最後一次提交data數據的備份狀態。本對象在下次調用onBackup()時作為oldState 返回。回想一下,onBackup()方法也必須寫入newState 對象——這裡也同樣要這麼做。這樣即使設備重置後第一次調用onBackup(),也能確保有可用的oldState對象能傳給onBackup()方法。
在實現onRestore()時,應該對data 調用readNextHeader(),以遍歷數據集裡所有的entity。對其中每個entity須進行以下操作:
1. 用getKey()獲取entity的鍵值。
2. 將此entity鍵值和已知鍵值清單進行比較,這個清單應該已經在BackupAgent繼承類中作為字符串常量(static final string)進行定義。一旦鍵值匹配其中一個鍵,就執行讀取entity數據並保存到設備的語句:
1. 用getDataSize()讀取entity數據大小並據其創建字節數組。
2. 調用readEntityData() ,傳入字節數組作為獲取數據的緩沖區,並指定起始位置和讀取字節數。
3. 字節數組將被填入數據,按需讀取數據並寫入設備即可。
3. 把數據讀出並寫回設備以後,和上面onBackup()過程類似,把數據的狀態寫入newState 參數。
下面是把前一節例子中所備份的數據進行恢復的示例:
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
// 應該是只有一個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 = new byte[dataSize];
data.readEntityData(dataBuf, 0, dataSize);
ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
DataInputStream in = new DataInputStream(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 = new FileOutputStream(newState.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
out.writeUTF(mPlayerName);
out.writeInt(mPlayerScore);
}
在以上例子中,傳給onRestore()的appVersionCode 參數沒有被用到。假如用戶程序的版本已經降級(比如從1.5降到1.0),可能就會用此參數來選擇備份數據。更多信息請參閱檢查恢復數據的版本。
關於BackupAgent的完整例子,請參閱例程備份和恢復中的ExampleAgent類。
繼承BackupAgentHelper
如果要備份整個文件(來自SharedPreferences或內部存儲),應該用BackupAgentHelper創建備份代理來實現。因為不必實現onBackup()和onRestore()了,用BackupAgentHelper 創建備份代理所需的代碼量將遠遠少於BackupAgent。
BackupAgentHelper 的實現必須要使用一個或多個backup helper。backup helper是一種專用組件,BackupAgentHelper 用它來對特定類型的數據執行備份和恢復操作。Android框架目前提供兩種helpers:
· SharedPreferencesBackupHelper用於備份SharedPreferences文件。
· FileBackupHelper 用於備份來自內部存儲的文件。
在BackupAgentHelper中可包含多個helper,但對於每種數據類型只需用到一個helper 。也就是說,即使存在多個SharedPreferences 文件,也只需要一個SharedPreferencesBackupHelper。
對於每個要加入BackupAgentHelper的helper,必須在onCreate() 中執行以下步驟:
1. 實例化所需的helper。在其構造方法裡必須指定需備份的文件。
2. 調用addHelper() 把helper加入BackupAgentHelper。
下一節描述了如何使用每種helper創建備份代理。
備份SharedPreferences
實例化SharedPreferencesBackupHelper時,必須包括一個或多個SharedPreferences 文件。
例如,假設需備份的SharedPreferences文件名為“user_preferences”,完整的使用BackupAgentHelper的備份代理代碼類似如下:
public class MyPrefsBackupAgent extends BackupAgentHelper {
// SharedPreferences 文件名
static final String PREFS = "user_preferences";
// 唯一標識備份數據的鍵值
static final String PREFS_BACKUP_KEY = "prefs";
// 申請helper並加入備份代理
@Override
public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
好,這就是一個備份代理的完整實現。SharedPreferencesBackupHelper內含了備份和恢復SharedPreferences文件的所有代碼。
當備份管理器調用onBackup() 和onRestore()時,BackupAgentHelper 調用helper來對給定文件執行備份和恢復操作。
注: SharedPreferences 是線程安全的,因此可以從備份代理和其它activity中安全地讀寫shared preferences文件。
備份其它文件
在實例化FileBackupHelper時,必須包含一個或多個保存於程序內部存儲中的文件名稱。(路徑的描述方式類似getFilesDir(),並且作為openFileOutput() 寫入文件的路徑。)
比如,需要備份兩個名為“scores”和“stats”的文件,備份代理使用BackupAgentHelper 示例如下:
public class MyFileBackupAgent extends BackupAgentHelper {
// SharedPreferences文件的名稱
static final String TOP_SCORES = "scores";
static final String PLAYER_STATS = "stats";
// 唯一標識備份數據集的鍵值
static final String FILES_BACKUP_KEY = "myfiles";
// 申請helper並加入備份代理
void onCreate() {
FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS);
addHelper(FILES_BACKUP_KEY, helper);
}
}
FileBackupHelper 包含了備份和恢復存於內部存儲的文件所需的全部代碼。
但是,讀寫內部存儲文件不是線程安全的。要確保activity操作文件的時候備份代理不會去讀寫文件,每次讀寫文件時必須使用同步語句。比如,Activity讀寫文件時,需要用一個對象作為同步語句的內部鎖。
// 內部鎖對象
static final Object[] sDataLock = new Object[0];
有趣的事實:長度為零的數組要比普通對象更輕量化,因此用作內部鎖會是個好主意。
然後,每次讀寫文件時用這個鎖創建同步語句。以下是把游戲分數寫入文件的同步語句示例:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir(), TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
應該用同一個鎖同步讀取文件的語句。
然後,在BackupAgentHelper內,必須覆蓋onBackup()和onRestore()方法,用同一個內部鎖同步備份和恢復操作。比如,上例中MyFileBackupAgent需要以下方法:
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
// Hold the lock while the FileBackupHelper performs backup
synchronized (MyActivity.sDataLock) {
super.onBackup(oldState, data, newState);
}
}
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
// Hold the lock while the FileBackupHelper restores the file
synchronized (MyActivity.sDataLock) {
super.onRestore(data, appVersionCode, newState);
}
}
好了,所有要做的工作僅僅是在onCreate()方法內加入FileBackupHelper,覆蓋onBackup()和onRestore() 並同步讀寫。
關於用FileBackupHelper實現BackupAgentHelper的例子,請參閱例程備份和恢復中的FileHelperExampleAgent 類。
檢查恢復數據的版本
在把數據保存到雲存儲中去時,備份管理器會自動包含應用程序的版本號,版本號是在manifest文件的android:versionCode 屬性中定義的。在調用備份代理恢復數據之前,備份管理器會查詢已安裝程序的android:versionCode,並與記錄在備份數據中的版本號相比較。如果備份數據的版本比設備上的要新,則意味著用戶安裝了舊版本的程序。這時備份管理器將停止恢復操作,onRestore()方法也不會被調用,因為把數據恢復給舊版本的程序是沒有意義的。
用android:restoreAnyVersion屬性可以取代以上規則。此屬性用“true”或“false”標明是否在恢復時忽略數據集的版本,默認值是“false”。如果將其設為“true”,備份管理器將忽略android:versionCode 並且每次都會調用onRestore()方法。這時候可以在onRestore()裡人工檢查版本,並在版本沖突時采取必要的措施保證數據的兼容性。
為了便於在恢復數據時對版本號進行判斷處理,onRestore()把備份數據的版本號作為appVersionCode 參數和數據一起傳入方法。而用PackageInfo.versionCode可以查詢當前應用程序的版本號,例如:
PackageInfo info;
try {
String name = getPackageName();
info = getPackageManager().getPackageInfo(name,0);
} catch (NameNotFoundException nnfe) {
info = null;
}
int version;
if (info != null) {
version = info.versionCode;
}
然後,簡單比較一下PackageInfo 中的version 和傳入onRestore()的appVersionCode 即可。
警告:請確認已經理解了android:restoreAnyVersion 設為“true”的後果。如果不是所有版本的程序都能在onRestore()時正確解析數據格式的差異,那麼保存到設備上的數據格式可能會與已安裝的版本不兼容。
請求備份
任何時候都可以通過調用dataChanged()來發起備份請求。此方法通知備份管理器用備份代理來備份數據。然後,備份管理器將會適時調用備份代理的onBackup()方法。通常每次數據發生變化時都應該請求備份數據(比如用戶修改了需保存的程序配置)。如果在備份管理器實際執行前連續調用了dataChanged()很多次,代理僅會執行一次onBackup()。
注: 在程序開發過程中,可以用bmgr tool發起備份請求,備份將會立即執行。
請求恢復
在程序正常的生命周期內,應該不需要發起恢復數據的請求。在程序安裝完成時,系統會自動檢查備份數據並執行恢復操作。不過必要時,也可以通過調用requestRestore()來人工發起恢復數據的請求。這時,備份管理器會調用onRestore(),並把現有備份數據集作為數據傳入該方法。
注:在程序開發過程中,可以用bmgr tool發起恢復數據的請求。
測試備份代理
一旦實現了備份代理,就可以用bmgr按以下步驟測試備份和恢復功能了:
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. 運行程序並初始化一些數據。
如果程序已經正確地實現了備份代碼,那每次數據變化時都會請求備份。例如,每當用戶改變了一些數據,程序將調用dataChanged(),這就往備份管理器的請求隊列裡加入了一個備份請求。出於測試的目的,也可以用以下bmgr命令發起一個請求:
adb shell bmgr backup your.package.name
4. 執行備份操作:
adb shell bmgr run
這一步強迫備份管理器執行所有已入隊列的備份請求。
5. 卸載應用程序:
adb uninstall your.package.name
6. 重新安裝應用程序。
如果備份代理成功運行,那第4步裡備份的數據將會被恢復。
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩
此方法適用於所有母控件無法獲取焦點的情況 開發中很常見的一個問題,項目中的listview不僅僅是簡單的文字,常常需要自己定義listview,自己的Adapte
相信每個項目都會有用戶反饋建議等功能,這個實現的方法很多,下面是我實現的方法,供大家交流。首先看具體界面,三個字段。名字,郵箱為選填,可以為空,建議不能為空。如有
登錄應用程序的屏幕,詢問憑據登錄到一些特定的應用。可能需要登錄到Facebook,微博等本章介紹了,如何創建一個登錄界面,以及如何管理安全問題和錯誤嘗試。首先,必須定義兩