日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

【Android Developers Training】 91. 解決云儲(chǔ)

系統(tǒng) 2163 0

注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術(shù)一般,由于喜愛安卓而產(chǎn)生了翻譯的念頭,純屬個(gè)人興趣愛好。

原文鏈接: http://developer.android.com/training/cloudsave/conflict-res.html


在云存儲(chǔ)中保存和加載過程是很直接的:它只是將用于數(shù)據(jù)和byte數(shù)組之間的序列化轉(zhuǎn)換,并將這些數(shù)組存儲(chǔ)在云端。然而, 當(dāng)你的用戶有多個(gè)設(shè)備,并且兩個(gè)以上的設(shè)備嘗試將它們的數(shù)據(jù)存儲(chǔ)在云端時(shí),這一保存可能會(huì)引起沖突,因此你必須決定應(yīng)該如何處理。你在云端存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)在很大程度上決定了你的沖突解決方案的魯棒性,所以小心地設(shè)計(jì)你的數(shù)據(jù),使得你的沖突檢測解決方案邏輯可以正確地處理每一種情況。

本片文章從描述一些有缺陷的方法入手,并解釋他們?yōu)楹尉哂腥毕荨V蟪尸F(xiàn)一個(gè)解決方案來避免沖突。用于討論的例子關(guān)注于游戲,但解決問題的宗旨是可以適用于任何將數(shù)據(jù)存儲(chǔ)于云端的應(yīng)用的。


一). 沖突時(shí)獲得通知

OnStateLoadedListener 方法負(fù)責(zé)從 Google 服務(wù)器下載應(yīng)用的狀態(tài)數(shù)據(jù)。回調(diào)函數(shù) OnStateLoadedListener.onStateConflict 為你的應(yīng)用在本地狀態(tài)和云端存儲(chǔ)的狀態(tài)發(fā)生沖突時(shí), 提供了一個(gè)解決機(jī)制:

      
        @Override


      
      
        public
      
      
        void
      
       onStateConflict(
      
        int
      
      
         stateKey, String resolvedVersion,

    
      
      
        byte
      
      [] localData, 
      
        byte
      
      
        [] serverData) {

    
      
      
        //
      
      
         resolve conflict, then call mAppStateClient.resolveConflict()
      
      
         ...

}
      
    

此時(shí)你的應(yīng)用必須決定要保留哪一個(gè)數(shù)據(jù),或者它自己提交一個(gè)新的數(shù)據(jù)來表示合并后的數(shù)據(jù)狀態(tài)(譯者注:是不是有點(diǎn)像git/svn呢?),解決沖突的邏輯由你來實(shí)現(xiàn)。

我們必須要意識(shí)到云存儲(chǔ)服務(wù)是在后臺(tái)執(zhí)行同步的。所以你應(yīng)該確保你的應(yīng)用能夠在你創(chuàng)建這一數(shù)據(jù)的context之外接收回調(diào)。特別地,如果Google Play服務(wù)應(yīng)用在后臺(tái)檢測到了一個(gè)沖突,該回調(diào)函數(shù)可以在你下一次加載數(shù)據(jù)時(shí)被調(diào)用,而不是下一次用戶啟動(dòng)該應(yīng)用時(shí)。

因此,你的云存儲(chǔ)代碼和沖突解決代碼的設(shè)計(jì)必須是和當(dāng)前context無關(guān)的:即給兩個(gè)沖突的數(shù)據(jù),你必須僅通過數(shù)據(jù)集中獲取的數(shù)據(jù)區(qū)解決沖突,而不依賴于任何其它外部環(huán)境。


二). 處理簡單地情況

下面列舉一些沖突解決的簡單例子。對(duì)于很多應(yīng)用而言,用這些策略或者其變體就足夠解決大多數(shù)問題了:

新的比舊的更有效 :在一些情況下,新的數(shù)據(jù)總是替代老數(shù)據(jù)。例如,如果數(shù)據(jù)代表了用戶選擇角色的衣服顏色,那么最近的新的選擇就應(yīng)該覆蓋老的選擇。在這種情況下,你可能會(huì)選擇在云存儲(chǔ)數(shù)據(jù)中存儲(chǔ)時(shí)間戳。當(dāng)處理這些沖突時(shí),選擇時(shí)間戳最新的數(shù)據(jù)(記住要選擇一個(gè)可靠的時(shí)鐘,并注意對(duì)不同時(shí)區(qū)的處理)。

有一個(gè)數(shù)據(jù)集中的數(shù)據(jù)比其它的更好 :在一些情況下,我們是可以有方法在若干數(shù)據(jù)集中選取一個(gè)最好的。例如,如果數(shù)據(jù)代表了玩家在賽車比賽中的最佳時(shí)間,那么顯然,在沖突發(fā)生時(shí),你應(yīng)該保留成績最好的那個(gè)數(shù)據(jù)。

進(jìn)行合并 :有可能通過計(jì)算兩個(gè)數(shù)據(jù)集的合并版本來解決沖突。例如,如果你的數(shù)據(jù)代表了用戶解鎖關(guān)卡的進(jìn)度,那么解決的數(shù)據(jù)就是沖突集的并集。通過這個(gè)方法,用戶不會(huì)丟失任何他的游戲進(jìn)度。這里的 例子 使用了這一操作的一個(gè)變形。


三). 為更復(fù)雜的情況設(shè)計(jì)一個(gè)策略

一個(gè)更復(fù)雜的情況是當(dāng)你的游戲允許玩家收集可以互換的東西時(shí)(比如金幣或者經(jīng)驗(yàn)點(diǎn)數(shù)),我們來假想一個(gè)游戲,叫做“金幣跑酷”,一個(gè)無限跑步的角色其目標(biāo)是不斷地收集金幣是自己變的富有。每個(gè)收集到的金幣都會(huì)加入到玩家的儲(chǔ)蓄罐中。

下面的章節(jié)將展示三種在多個(gè)設(shè)備間解決沖突的方案:有兩個(gè)聽上去很不錯(cuò),可惜最終還是不能適用于所有的場景,最后一個(gè)解決方案可以解決多個(gè)設(shè)備間的沖突。

第一個(gè)嘗試:只保存總數(shù)

首先,這個(gè)問題看上去像是說:云存儲(chǔ)的數(shù)據(jù)只要存儲(chǔ)金幣的數(shù)量就行了。但是如果就只有這些數(shù)據(jù)是可用的,那么解決沖突的方案將會(huì)嚴(yán)重受到限制。此時(shí)最佳的方案就是在沖突發(fā)生時(shí)存儲(chǔ)最大數(shù)值得數(shù)據(jù)。

想一下表一中所展現(xiàn)的場景。假設(shè)玩家一開始有20枚硬幣,然后再設(shè)備A上收集了10個(gè),在設(shè)備B上收集了15個(gè)。然后設(shè)備B將數(shù)據(jù)存儲(chǔ)到了云端。當(dāng)設(shè)備A嘗試去存儲(chǔ)的時(shí)候,沖突發(fā)生了。“只存儲(chǔ)總數(shù)”的沖突解決方案會(huì)存儲(chǔ)35作為這一數(shù)據(jù)的值(兩數(shù)之間最大的)

表1. 值保存最大的數(shù)(不佳的策略)

事件 設(shè)備A的數(shù)據(jù) 設(shè)備B的數(shù)據(jù) 云端的數(shù)據(jù) 實(shí)際的總數(shù)
開始階段 20 20 20 20
玩家在A設(shè)備上收集了10個(gè)硬幣 30 20 20 30
玩家在B設(shè)備上收集了15個(gè)硬幣 30 35 20 45
設(shè)備B將數(shù)據(jù)存儲(chǔ)至云端 30 35 35 45

設(shè)備A嘗試將數(shù)據(jù)存儲(chǔ)至云端

發(fā)生沖突

30 35 35 45
設(shè)備A通過選擇兩數(shù)中最大的數(shù)來解決沖突 35 35 35 45

這一策略會(huì)失敗——玩家的金幣數(shù)從20變成35,但實(shí)際上玩家總共收集了25個(gè)硬幣(A設(shè)備10個(gè),B設(shè)備15個(gè))。所以有10個(gè)硬幣丟失了。只在云端存儲(chǔ)硬幣的總數(shù)是不足以實(shí)現(xiàn)一個(gè)魯棒的沖突解決算法的。

第二個(gè)嘗試:存儲(chǔ)總數(shù)和變化值

另一個(gè)方法是在存儲(chǔ)數(shù)據(jù)中包括一些額外的數(shù)據(jù):自上次提交后硬幣增加的數(shù)量(delta)。在這一方法中,存儲(chǔ)的數(shù)據(jù)可以用一個(gè)二元組來表示(T, d),其中T是硬幣的總數(shù),而d是硬幣增加的數(shù)量。

在這個(gè)結(jié)構(gòu)中,你的沖突檢測算法在魯棒性上有更大的提升空間,如下將要講的那樣。但是這個(gè)方法還是無法給出一個(gè)可靠的玩家最終的狀態(tài)。

下面是包含delta的沖突解決算法過程:

  • 本地?cái)?shù)據(jù): (T, d)
  • 云端數(shù)據(jù): (T', d')
  • 解決后的數(shù)據(jù): (T'+d, d)

例如,當(dāng)你在本地狀態(tài)( T, d )和云端狀態(tài)( T', d )之間發(fā)生了沖突時(shí),你可以將它們合并成 T'+d, d )。意味著你從本地拿出delta數(shù)據(jù),并將它和云端的數(shù)據(jù)結(jié)合起來,乍一看,這種方法可以很好的計(jì)量多個(gè)設(shè)備所收集的金幣。

看上去很可靠的方法,但這個(gè)方法在動(dòng)態(tài)移動(dòng)環(huán)境中難以適用:

  • 用戶可能在設(shè)備不在線時(shí)存儲(chǔ)數(shù)據(jù)。這些改變會(huì)以隊(duì)列形式等待手機(jī)聯(lián)網(wǎng)后提交。
  • 這個(gè)方法的同步機(jī)制是用最新的變化覆蓋掉任何之前的變化。換句話說,第二次寫入的變化會(huì)提交到云端(當(dāng)設(shè)備聯(lián)網(wǎng)了以后),而第一次寫入的變化就被忽略了。

為了進(jìn)一步說明,我們考慮一下表2所列的場景。在表2的一系列操作后,云端的狀態(tài)將是(130, +5),之后最終沖突解決后的狀態(tài)時(shí)(140, +10)。這是不正確的,因?yàn)閺目傮w上而言,用戶一共在A上收集了110枚硬幣而在B上收集了120枚硬幣。總數(shù)應(yīng)該為250。

表2. “總數(shù)+增量”策略的失敗案例

事件? 設(shè)備A的數(shù)據(jù)? 設(shè)備B的數(shù)據(jù)? 云端的數(shù)據(jù)? 實(shí)際的數(shù)據(jù)?
?開始階段 ?(20, x) ?(20, x) (20, x)? 20?
?玩家在A設(shè)備上收集了100個(gè)硬幣 ?(120, +100) ?(20, x) (20, x)? 120?
?玩家在A設(shè)備上又收集了10個(gè)硬幣 ? (130, +10) ?(20, x) (20, x)
130?
?玩家在B設(shè)備上收集了115個(gè)硬幣 (130, +10) (125, +115)
(20, x) 245?
?玩家在B設(shè)備上又收集了5個(gè)硬幣 (130, +10) (130, +5)? (20, x)
250?
?設(shè)備B將數(shù)據(jù)存儲(chǔ)至云端 (130, +10) (130, +5)? (130, +5) 250?
?

設(shè)備A嘗試將數(shù)據(jù)存儲(chǔ)至云端

發(fā)生沖突

(130, +10) ? (130, +5)? (130, +5)? 250?
?設(shè)備A通過將本地的增量和云端的總數(shù)相加來解決沖突 ?(140, +10) (130, +5)? (140, +10) ? 250?

注:x代表與該場景無關(guān)的數(shù)據(jù)

你可能會(huì)嘗試在每次保存后不重置增量數(shù)據(jù)來解決此問題,這樣的話在每個(gè)設(shè)備上的第二次存儲(chǔ)所收集到的硬幣將不會(huì)產(chǎn)生問題。這樣的話設(shè)備A在第二次本地存儲(chǔ)完成后,數(shù)據(jù)將是( 130, +110 )而不是( 130, +10 )。然而,這樣做的話就會(huì)發(fā)生如表3所述的情況:

表3. 算法改進(jìn)后的失敗案例

事件 設(shè)備A的數(shù)據(jù) 設(shè)備B的數(shù)據(jù)? 云端的數(shù)據(jù) 實(shí)際的數(shù)據(jù)
?開始階段 (20, x) (20, x) (20, x) 20
?玩家在A設(shè)備上收集了100個(gè)硬幣 (120, +100) (20, x) (20, x) 120
設(shè)備A將狀態(tài)存儲(chǔ)到云端 (120, +100) (20, x) (120, +100) 120
?玩家在A設(shè)備上又收集了10個(gè)硬幣 (130, +110) (20, x) (120, +100) 130
?玩家在B設(shè)備上收集了1個(gè)硬幣 (130, +110) (21, +1) (120, +100) 131

?設(shè)備B嘗試向云端存儲(chǔ)數(shù)據(jù)

發(fā)生沖突

(130, +110) (21, +1) (120, +100) 131
?設(shè)備B通過將本地的增量和云端的總數(shù)相加來解決沖突 (130, +110) (121, +1) (121, +1) 131
?

設(shè)備A嘗試將數(shù)據(jù)存儲(chǔ)至云端

發(fā)生沖突

(130, +110) (121, +1) (121, +1) 131
?設(shè)備A通過將本地的增量和云端的總數(shù)相加來解決沖突 (231, +110) (121, +1) (231, +110) 131

注:x代表與該場景無關(guān)的數(shù)據(jù)

現(xiàn)在你碰到了另一個(gè)問題:你給予了玩家過多的硬幣。這個(gè)玩家拿到了211枚硬幣,但實(shí)際上他只收集了111枚。

解決辦法:

我們分析之前的幾次嘗試,我們發(fā)現(xiàn)這些策略都沒有這樣一個(gè)能力:知曉哪些硬幣已經(jīng)計(jì)數(shù)了,哪些硬幣沒有被計(jì)數(shù),尤其是當(dāng)多個(gè)設(shè)備連續(xù)提交的時(shí)候,算法會(huì)出現(xiàn)混亂。

該問題的解決辦法將你云端的存儲(chǔ)結(jié)構(gòu)改為字段,使用字符串+整形的鍵值對(duì)。每一個(gè)鍵值對(duì)都會(huì)代表一個(gè)包含硬幣的“委托”,而總數(shù)就應(yīng)該是將所有值加起來。這一設(shè)計(jì)的宗旨是每個(gè)設(shè)備有它自己的委托,并且只有設(shè)備自己可以吧硬幣放到其委托中。

字典的結(jié)構(gòu)是: (A:a, B:b, C:c, ...) ,其中a代表了委托A所擁有的硬幣,b是委托B所擁有的硬幣,以此類推。

這樣的話,新的沖突解決策略算法將如下所示:

本地?cái)?shù)據(jù): (A:a, B:b, C:c, ...)

云端數(shù)據(jù): (A:a', B:b', C:c', ...)

解決后的數(shù)據(jù) (A: max (a,a'), B: max (b,b'), C: max (c,c'), ...)

例如,如果本地?cái)?shù)據(jù)是 (A:20, B:4, C:7) 并且云端數(shù)據(jù)是 (B:10, C:2, D:14) ,這樣的話解決沖突后的數(shù)據(jù)將會(huì)是 (A:20, B:10, C:7, D:14) 。注意,你應(yīng)用的沖突解決邏輯會(huì)根據(jù)具體的場景可能有所差異。比如,有一些應(yīng)用你可能希望挑選最小的值。

為了測試新的算法,將它應(yīng)用于任何一個(gè)之前提到過的場景。你將會(huì)發(fā)現(xiàn)它都能取得正確地結(jié)果。

表4闡述了這一點(diǎn),它基于表3的場景。注意下面所列的:

在初始狀態(tài),玩家有20枚硬幣。此數(shù)值在所有設(shè)備和云端都是正確的,我們用(X:20)這一元祖代表它,其中X我們不用太多關(guān)心,我們不去追求這個(gè)初始化的數(shù)據(jù)是哪兒來的。

當(dāng)玩家在設(shè)備A上收集了100枚硬幣,這一變化會(huì)作為一個(gè)元組保存到云端。它的值是100是因?yàn)檫@就是玩家在設(shè)備A上收集的硬幣數(shù)量。在這一過程中,沒有要執(zhí)行數(shù)據(jù)的計(jì)算——設(shè)備A僅僅是將玩家所收集的數(shù)據(jù)匯報(bào)給了云端。

每一個(gè)新的硬幣提交會(huì)打包成一個(gè)于設(shè)備關(guān)聯(lián)的元組并保存到云端。例如,假設(shè)玩家又在設(shè)備A上收集了100枚硬幣,那么元組的值被更新為110。

最終的結(jié)果就是,應(yīng)用知道了玩家在每個(gè)設(shè)備上收集硬幣的總數(shù)。這樣它就能輕易地計(jì)算總數(shù)了。

表4. 鍵值對(duì)策略的成功應(yīng)用案例

事件 設(shè)備A的數(shù)據(jù) 設(shè)備B的數(shù)據(jù) 云端的數(shù)據(jù) 實(shí)際的數(shù)據(jù)
開始階段 (X:20, x) (X:20, x) (X:20, x) 20
玩家在A設(shè)備上收集了100個(gè)硬幣 (X:20, A:100) (X:20) (X:20) 120
設(shè)備A將狀態(tài)存儲(chǔ)到云端 (X:20, A:100) (X:20) (X:20, A:100) 120
玩家在A設(shè)備上又收集了10個(gè)硬幣 (X:20, A:110) (X:20) (X:20, A:100) 130
玩家在B設(shè)備上收集了1個(gè)硬幣 (X:20, A:110) (X:20, B:1) (X:20, A:100) 131

設(shè)備B嘗試向云端存儲(chǔ)數(shù)據(jù)

發(fā)生沖突

(X:20, A:110) (X:20, B:1) (X:20, A:100) 131
設(shè)備B解決沖突 (X:20, A:110) (X:20, A:100, B:1) (X:20, A:100, B:1) 131

設(shè)備A嘗試將數(shù)據(jù)存儲(chǔ)至云端

發(fā)生沖突

(X:20, A:110) (X:20, A:100, B:1) (X:20, A:100, B:1) 131
設(shè)備A解決沖突 (X:20, A:110, B:1) (X:20, A:100, B:1) (X:20, A:110, B:1)?
total 131
131

四). 清除你的數(shù)據(jù)

在云端存儲(chǔ)數(shù)據(jù)的大小是由限制的,所以在后續(xù)的論述中,我們將會(huì)關(guān)注與如何避免創(chuàng)建過大的詞典。一開始,看上去每個(gè)設(shè)備只會(huì)有一個(gè)詞典字段,即使是非常激進(jìn)的用戶也不太會(huì)擁有上千條字段。然而, 獲取設(shè)備ID的方法很難,并且我們認(rèn)為這是一種不好的實(shí)踐方式,所以你應(yīng)該使用一個(gè)安裝ID,這更容易獲取也更可靠。這樣的話就意味著,每一次用戶在每臺(tái)設(shè)備安裝一次就會(huì)產(chǎn)生一個(gè)ID。假設(shè)每個(gè)鍵值對(duì)占據(jù)32字節(jié),由于一個(gè)個(gè)人云存儲(chǔ)緩存最多可以有128K的大小,那么你最多可以存儲(chǔ)4096個(gè)字段。

在現(xiàn)實(shí)場景中,你的數(shù)據(jù)可能更加復(fù)雜。在這種情況下,存儲(chǔ)的數(shù)據(jù)字段數(shù)也會(huì)進(jìn)一步受到限制。具體而言則需要取決于實(shí)現(xiàn),比如可能需要添加時(shí)間戳來指明每個(gè)字段是何時(shí)修改的。當(dāng)你檢測到有一個(gè)字段在過去幾個(gè)禮拜或者幾個(gè)月的時(shí)間內(nèi)都沒有被修改,那么就可以安全地將它轉(zhuǎn)移到另一個(gè)字段中并刪除老的字段。

【Android Developers Training】 91. 解決云儲(chǔ)存沖突


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 洞口县| 神农架林区| 洮南市| 无极县| 湟中县| 淅川县| 宣化县| 鸡西市| 凤庆县| 万年县| 翁源县| 池州市| 漯河市| 永顺县| 自贡市| 申扎县| 泗洪县| 封开县| 米脂县| 西宁市| 宁波市| 资溪县| 亚东县| 松滋市| 乐至县| 西乡县| 株洲县| 乌拉特中旗| 娄烦县| 巴林右旗| 平潭县| 惠州市| 同心县| 清镇市| 通海县| 固原市| 扶绥县| 兴宁市| 浠水县| 汕尾市| 满城县|