天天看點

資料備份

資料備份

快速檢視

·       将使用者資料備份到雲中心以防丢失。

·       可友善地用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中聲明備份代理

例如:

&lt;manifest ... &gt; 

    ... 

    &lt;applicationandroid:label="myapplication" 

                 android:backupagent="mybackupagent"&gt; 

        &lt;activity ... &gt; 

            ... 

        &lt;/activity&gt; 

    &lt;/application&gt; 

&lt;/manifest&gt;

為android備份服務進行注冊

為了程式能利用android備份服務執行備份操作,必須對程式進行注冊以獲得一個backup service key,然後在android manifest檔案中聲明這個key。

&lt;applicationandroid:label="myapplication" 

             android:backupagent="mybackupagent"&gt; 

    &lt;meta-dataandroid:name="com.google.android.backup.api_key" 

        android:value="aedpqreaaaaidayevgu6djnyjdbmu7klh3kszdxlv_4diseiyq"/&gt; 

&lt;/application&gt;

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的&lt;meta-data&gt;元素将被忽略。

繼承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

繼續閱讀