<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                > 編寫:[jdneo](https://github.com/jdneo) - 原文:[http://developer.android.com/training/cloudsave/conflict-res.html](http://developer.android.com/training/cloudsave/conflict-res.html) 這篇文章介紹了當應用使用[Cloud Save service](http://developers.google.com/games/services/common/concepts/cloudsave)存儲數據到云端時,如何設計一個魯棒性較高的沖突解決策略。云存儲服務允許你為每一個在Google服務上的應用用戶,存儲他們的應用數據。你的應用可以通過使用云存儲API,從Android設備,iOS設備或者web應用恢復或更新這些數據。 云存儲中的保存和加載過程非常直接:它只是一個數據和byte數組之間序列化轉換,并將這些數組存儲在云端的過程。然而,當你的用戶有多個設備,并且有兩個以上的設備嘗試將它們的數據存儲在云端時,這一保存可能會引起沖突,因此你必須決定應該如何處理這類問題。云端數據的結構在很大程度上決定了沖突解決方案的魯棒性,所以務必小心地設計你的數據存儲結構,使得沖突解決方案的邏輯可以正確地處理每一種情況。 本篇文章從一些有缺陷的解決方案入手,并解釋他們為何具有缺陷。之后會呈現一個可以避免沖突的解決方案。用于討論的例子關注于游戲,但解決問題的核心思想是可以適用于任何將數據存儲于云端的應用的。 ### 沖突時獲得通知 [OnStateLoadedListener](http://developer.android.com/reference/com/google/android/gms/appstate/OnStateLoadedListener.html)方法負責從Google服務器下載應用的狀態數據。回調函數[OnStateLoadedListener.onStateConflict](http://developer.android.com/reference/com/google/android/gms/appstate/OnStateLoadedListener.html#onStateConflict)用來給你的應用在本地狀態和云端存儲的狀態發生沖突時,提供了一個解決機制: ~~~ @Override public void onStateConflict(int stateKey, String resolvedVersion, byte[] localData, byte[] serverData) { // resolve conflict, then call mAppStateClient.resolveConflict() ... } ~~~ 此時你的應用必須決定要保留哪一個數據,或者它自己提交一個新的數據來表示合并后的數據狀態,解決沖突的邏輯由你自己來實現。 我們必須要意識到云存儲服務是在后臺執行同步的。所以你應該確保你的應用能夠在你創建這一數據的Context之外接收回調。特別地,如果Google Play服務應用在后臺檢測到了一個沖突,該回調函數會在你下一次加載數據時被調用,通常來說會是在下一次用戶啟動該應用時。 因此,你的云存儲代碼和沖突解決代碼的設計必須是和當前context無關的:也就是說當你拿到了兩個沖突的數據,你必須僅通過數據集內獲取的數據去解決沖突,而不依賴于任何其它任何外部Context。 ### 處理簡單情況 下面列舉一些解決沖突的簡單例子。對于很多應用而言,用這些策略或者其變體就足夠解決大多數問題了: **新的比舊的更有效:**在一些情況下,新的數據可以替代舊的數據。例如,如果數據代表了用戶所選擇角色的衣服顏色,那么最近的新的選擇就應該覆蓋老的選擇。在這種情況下,你可能會選擇在云存儲數據中存儲時間戳。當處理這些沖突時,選擇時間戳最新的數據(記住要選擇一個可靠的時鐘,并注意對不同時區的處理)。 **一個數據好于其他數據:**在一些情況下,我們是可以有方法在若干數據集中選取一個最好的。例如,如果數據代表了玩家在賽車比賽中的最佳時間,那么顯然,在沖突發生時,你應該保留成績最好的那個數據。 **進行合并:**有可能通過計算兩個數據集的合并版本來解決沖突。例如,你的數據代表了用戶解鎖關卡的進度,那么我們需要的數據就是兩個沖突數據的并集。通過這個方法,用戶的關卡解鎖進度就不會丟失了。這里的[例子](https://github.com/playgameservices/android-samples/tree/master/CollectAllTheStars)使用了這一策略的一個變形。 ### 為更復雜的情況設計一個策略 當你的游戲允許玩家收集可交換物品時(比如金幣或者經驗點數),情況會變得更加復雜一些。我們來假想一個游戲,叫做“金幣跑酷”,游戲中的角色通過跑步不斷地收集金幣使自己變的富有。每個收集到的金幣都會加入到玩家的儲蓄罐中。 下面的章節將展示三種在多個設備間解決沖突的方案:有兩個看上去還不錯,可惜最終還是不能適用于所有情況,最后一個解決方案可以解決多個設備間的數據沖突。 ### 第一個嘗試:只保存總數 首先,這個問題看上去像是說:云存儲的數據只要存儲金幣的數量就行了。但是如果就只有這些數據是可用的,那么解決沖突的方案將會嚴重受到限制。此時最佳的方案只能是在沖突發生時存儲數值最大的數據。 想一下表1中所展現的場景。假設玩家一開始有20枚硬幣,然后在設備A上收集了10個,在設備B上收集了15個。然后設備B將數據存儲到了云端。當設備A嘗試去存儲的時候,沖突發生了。“只保存總數”的沖突解決方案會存儲35作為這一數據的值(兩數之間最大的)。 表1. 值保存最大的數(不佳的策略) | 事件 | 設備A的數據 | 設備B的數據 | 云端的數據 | 實際的總數 | |-----|-----|-----|-----|-----| | 開始階段 | 20 | 20 | 20 | 20 | | 玩家在A設備上收集了10個硬幣 | 30 | 20 | 20 | 30 | | 玩家在B設備上收集了15個硬幣 | 30 | 35 | 20 | 45 | | 設備B將數據存儲至云端 | 30 | 35 | 35 | 45 | | 設備A嘗試將數據存儲至云端,**發生沖突** | 30 | 35 | 35 | 45 | | 設備A通過選擇兩數中最大的數來解決沖突 | 35 | 35 | 35 | 45 | 這一策略顯然會失敗:玩家的金幣數從20變成35,但實際上玩家總共收集了25個硬幣(A設備10個,B設備15個),所以有10個硬幣丟失了。只在云端存儲硬幣的總數是不足以實現一個健壯的沖突解決算法的。 ### 第二個嘗試:存儲總數和變化值 另一個方法是在存儲數據中包括一些額外的數據,如:自上次提交后硬幣增加的數量(delta)。在這一方法中,存儲的數據可以用一個二元組來表示(T, d),其中T是硬幣的總數,而d是硬幣增加的數量。 通過這樣的數據存儲結構,你的沖突檢測算法在魯棒性上會有更大的提升空間。但是這個方法在某些情況下依然會存在問題。 下面是包含delta數值的沖突解決算法過程: - **本地數據**:(T, d) - **云端數據**:(T', d') - **解決后的數據**:(T'+d, d) 例如,當你在本地狀態(T, d)和云端狀態(T', d)之間發生了沖突時,你可以將它們合并成(T'+d, d)。意味著你從本地拿出delta數據,并將它和云端的數據結合起來,乍一看,這種方法可以很好的計量多個設備所收集的金幣。 該方法看上去很可靠,但它在具有移動網絡的環境中難以適用: - 用戶可能在設備不在線時存儲數據。這些改變會以隊列形式等待手機聯網后提交。 - 這個方法的同步機制是用最新的變化覆蓋掉任何之前的變化。換句話說,第二次寫入的變化會提交到云端(當設備聯網了以后),而第一次寫入的變化就被忽略了。 為了進一步說明,我們考慮一下表2所列的場景。在表2列出的一系列操作發生后,云端的狀態將是(130, +5),最終沖突解決后的狀態是(140, +10)。這是不正確的,因為從總體上而言,用戶一共在A上收集了110枚硬幣而在B上收集了120枚硬幣。總數應該為250。 表2. “總數+增量”策略的失敗案例 | 事件 | 設備A的數據 | 設備B的數據 | 云端的數據 | 實際的總數 | |-----|-----|-----|-----|-----| | 開始階段 | (20, x) | (20, x) | (20, x) | 20 | | 玩家在A設備上收集了100個硬幣 | (120, +100) | (20, x) | (20, x) | 120 | | 玩家在A設備上又收集了10個硬幣 | (130, +10) | (20, x) | (20, x) | 130 | | 玩家在B設備上收集了115個硬幣 | (130, +10) | (125, +115) | (20, x) | 245 | | 玩家在B設備上又收集了5個硬幣 | (130, +10) | (130, +5) | (20, x) | 250 | | 設備B將數據存儲至云端 | (130, +10) | (130, +5) | (130, +5) | 250 | | 設備A嘗試將數據存儲至云端,**發生沖突** | (130, +10) | (130, +5) | (130, +5) | 250 | | 設備A通過將本地的增量和云端的總數相加來解決沖突 | (140, +10) | (130, +5) | (140, +10) | 250 | _注:x代表與該場景無關的數據_ 你可能會嘗試在每次保存后不重置增量數據來解決此問題,這樣的話在每個設備上第二次存儲的數據就能夠代表用戶至今為止收集到的所有硬幣。此時,設備A在第二次本地存儲完成后,數據將是(130, +110)而不是(130, +10)。然而,這樣做的話就會發生如表3所述的情況: 表3. 算法改進后的失敗案例 | 事件 | 設備A的數據 | 設備B的數據 | 云端的數據 | 實際的總數 | |-----|-----|-----|-----|-----| | 開始階段 | (20, x) | (20, x) | (20, x) | 20 | | 玩家在A設備上收集了100個硬幣 | (120, +100) | (20, x) | (20, x) | 120 | | 設備A將狀態存儲到云端 | (120, +100) | (20, x) | (120, +100) | 120 | | 玩家在A設備上又收集了10個硬幣 | (130, +110) | (20, x) | (120, +100) | 130 | | 玩家在B設備上收集了1個硬幣 | (130, +110) | (21, +1) | (120, +100) | 131 | | 設備B嘗試向云端存儲數據,**發生沖突** | (130, +110) | (21, +1) | (120, +100) | 131 | | 設備B通過將本地的增量和云端的總數相加來解決沖突 | (130, +110) | (121, +1) | (121, +1) | 131 | | 設備A嘗試將數據存儲至云端,**發生沖突** | (130, +110) | (121, +1) | (121, +1) | 131 | | 設備A通過將本地的增量和云端的總數相加來解決沖突 | (231, +110) | (121, +1) | (231, +110) | 131 | _注:x代表與該場景無關的數據_ 現在你碰到了另一個問題:你給予了玩家過多的硬幣。這個玩家拿到了211枚硬幣,但實際上他只收集了111枚。 ### 解決辦法: 分析之前的幾次嘗試,我們發現這些策略存在這樣的缺陷:無法知曉哪些硬幣已經計數了,哪些硬幣沒有被計數,尤其是當多個設備連續提交的時候,算法會出現混亂。 該問題的解決辦法是將你在云端的數據存儲結構改為字典類型,使用字符串+整形的鍵值對。每一個鍵值對都代表了一個包含硬幣的“委托人”,而總數就應該是將所有記錄的值加起來。這一設計的宗旨是每個設備有它自己的“委托人”,并且只有設備自己可以把硬幣放到它的“委托人”當中。 字典的結構是:(A:a, B:b, C:c, ...),其中a代表了“委托人”A所擁有的硬幣,b是“委托人”B所擁有的硬幣,以此類推。 這樣的話,新的沖突解決策略算法將如下所示: - **本地數據**:(A:a, B:b, C:c, ...) - **云端數據**:(A:a', B:b', C:c', ...) - **解決后的數據**:(A:max(a,a'), B:max(b,b'), C:max(c,c'), ...) 例如,如果本地數據是(A:20, B:4, C:7)并且云端數據是(B:10, C:2, D:14),那么解決沖突后的數據將會是(A:20, B:10, C:7, D:14)。當然,應用的沖突解決邏輯可以根據具體的需求而有所差異。比如,有一些應用你可能希望挑選最小的值。 為了測試新的算法,將它應用于任何一個之前提到過的場景。你將會發現它都能取得正確地結果。 表4闡述了這一點,它使用了表3中所提到的場景。注意下面所列出的關鍵點: 在初始狀態,玩家有20枚硬幣。該數據準確體現在了所有設備和云端中,我們用字典:(X:20)來代表它,其中X我們不用太多關心,初始化的數據是哪兒來對該問題沒有影響。 當玩家在設備A上收集了100枚硬幣,這一變化會作為一個字典保存到云端。字典的值是100是因為這就是玩家在設備A上收集的硬幣數量。在這一過程中,沒有要執行數據的計算(設備A僅僅是將玩家所收集的數據匯報給了云端)。 每一個新的硬幣提交會打包成一個與設備關聯的字典并保存到云端。例如,假設玩家又在設備A上收集了100枚硬幣,那么對應字典的值被更新為110。 最終的結果就是,應用知道了玩家在每個設備上收集硬幣的總數。這樣它就能輕易地計算出實際的總數了。 表4. 鍵值對策略的成功應用案例 | 事件 | 設備A的數據 | 設備B的數據 | 云端的數據 | 實際的總數 | |-----|-----|-----|-----|-----| | 開始階段 | (X:20, x) | (X:20, x) | (X:20, x) | 20 | | 玩家在A設備上收集了100個硬幣 | (X:20, A:100) | (X:20) | (X:20) | 120 | | 設備A將狀態存儲到云端 | (X:20, A:100) | (X:20) | (X:20, A:100) | 120 | | 玩家在A設備上又收集了10個硬幣 | (X:20, A:110) | (X:20) | (X:20, A:100) | 130 | | 玩家在B設備上收集了1個硬幣 | (X:20, A:110) | (X:20, B:1) | (X:20, A:100) | 131 | | 設備B嘗試向云端存儲數據,**發生沖突** | (X:20, A:110) | (X:20, B:1) | (X:20, A:100) | 131 | | 設備B解決沖突 | (X:20, A:110) | (X:20, A:100, B:1) | (X:20, A:100, B:1) | 131 | | 設備A嘗試將數據存儲至云端,**發生沖突** | (X:20, A:110) | (X:20, A:100, B:1) | (X:20, A:100, B:1) | 131 | | 設備A解決沖突 | (X:20, A:110, B:1) | (X:20, A:100, B:1) | (X:20, A:110, B:1),**total 131** | 131 | ### 清除你的數據 在云端允許存儲數據的大小是有限制的,所以在后續的論述中,我們將會關注如何避免創建過大的詞典。一開始,看上去每個設備只會有一條詞典記錄,即使是非常激進的用戶也不太會擁有上千種不同的設備(對應上千條字典記錄)。然而, 獲取設備ID的方法很難,并且我們認為這是一種不好的實踐方式,所以你應該使用一個安裝ID,這更容易獲取也更可靠。這樣的話就意味著,每一次用戶在每臺設備安裝一次就會產生一個ID。假設每個鍵值對占據32字節,由于一個個人云存儲緩存最多可以有128K的大小,那么你最多可以存儲4096條記錄。 在現實場景中,你的數據可能更加復雜。在這種情況下,存儲數據的記錄條數也會進一步受到限制。具體而言則需要取決于實現,比如可能需要添加時間戳來指明每條記錄是何時修改的。當你檢測到有一條記錄在過去幾個禮拜或者幾個月的時間內都沒有被修改,那么就可以安全地將金幣數據轉移到另一條記錄中并刪除老的記錄。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看