為移動(dòng)應(yīng)用提供離線支持
對(duì)移動(dòng)應(yīng)用的離線支持可以理解為應(yīng)用在網(wǎng)絡(luò)連接不穩(wěn)定的情況下能夠做出優(yōu)雅的反應(yīng)的能力。在移動(dòng)設(shè)備這一相對(duì)較新的技術(shù)背景中,新的問(wèn)題也隨之產(chǎn)生,例如網(wǎng)絡(luò)連接的正?;虍惓?、高延遲以及低帶寬等情況。這些問(wèn)題出現(xiàn)的時(shí)間并不算長(zhǎng),剛剛上手進(jìn)行移動(dòng)開(kāi)發(fā)的工程師對(duì)此并不十分了解。除此之外,創(chuàng)建一個(gè)能夠適應(yīng)不同網(wǎng)絡(luò)情況的移動(dòng)應(yīng)用可能還包括以下需求:
- 在網(wǎng)絡(luò)調(diào)用失敗的情況下顯示適當(dāng)?shù)腻e(cuò)誤信息。
- 允許在“訪客模式”下使用該應(yīng)用,而某些特性只在用戶登錄之后才可以使用。
- 在UI上明確地顯示出網(wǎng)絡(luò)連接斷開(kāi)的信息(連接模式/離線模式)。
- 在網(wǎng)絡(luò)連接斷開(kāi)的情況下禁用某些控件。
- 在沒(méi)有網(wǎng)絡(luò)連接的情況下也允許用戶進(jìn)行數(shù)據(jù)查詢與操作(離線數(shù)據(jù)訪問(wèn))。
- 在不同的網(wǎng)絡(luò)連接條件下對(duì)應(yīng)用進(jìn)行測(cè)試!
雖然以上這幾點(diǎn)從使用性的角度來(lái)看都是非常重要的,但其中某一點(diǎn)的復(fù)雜性尤為突出,即“離線數(shù)據(jù)訪問(wèn)”。應(yīng)用程序或許需要支持多種不同的離線數(shù)據(jù)訪問(wèn)場(chǎng)景或是級(jí)別,在下文中我將一一進(jìn)行講解。
本地緩存
應(yīng)用程序在沒(méi)有網(wǎng)絡(luò)連接的情況下依然能夠顯示信息,而在連上網(wǎng)絡(luò)的情況下需要刷新數(shù)據(jù)。要實(shí)現(xiàn)這一點(diǎn),需要在移動(dòng)設(shè)備上對(duì)數(shù)據(jù)進(jìn)行一定程度的持久化,并且通常需要保留一段時(shí)間。

對(duì)緩存中的數(shù)據(jù)進(jìn)行刷新有三種不同的“策略”,接下來(lái)我將逐一進(jìn)行分析。
網(wǎng)絡(luò)優(yōu)先
總是嘗試從服務(wù)器上獲取數(shù)據(jù),如果無(wú)法從服務(wù)器上獲取,再轉(zhuǎn)而從本地緩存中獲取數(shù)據(jù)。如果你特別希望總是顯示最近的、經(jīng)過(guò)更新的信息,那么這種策略對(duì)你非常有幫助。
本地優(yōu)先
在一段指定的時(shí)間之內(nèi)完全不會(huì)從網(wǎng)絡(luò)上獲取數(shù)據(jù),而是通過(guò)本地緩存進(jìn)行獲取。如果你的應(yīng)用能夠接受顯示被緩存的數(shù)據(jù)而不存在什么風(fēng)險(xiǎn),那么這種方式非常適合你。另一方面,這種方式的用戶體驗(yàn)更好,因?yàn)橥ǔ?lái)說(shuō)不會(huì)產(chǎn)生任何延遲。
混合 / 智能
這種方式在從服務(wù)端獲取數(shù)據(jù)之前先從本地緩存中返回結(jié)果。可以選擇等待服務(wù)端的通知,或是在后臺(tái)對(duì)服務(wù)進(jìn)行輪詢的方式,對(duì)本地的緩存數(shù)據(jù)進(jìn)行刷新。這種機(jī)制能夠在性能與用戶體驗(yàn)之間取得一種良好的平衡,而它仍然會(huì)對(duì)本地緩存進(jìn)行定期刷新,避免了為用戶顯示“過(guò)期”數(shù)據(jù)的風(fēng)險(xiǎn)。
此外,通過(guò)某種服務(wù)端緩存的支持,可以有效地彌補(bǔ)本地緩存的不足之處。正如HTTP緩存一樣,當(dāng)需要從服務(wù)器獲取數(shù)據(jù)的時(shí)候,客戶端可以通過(guò)發(fā)送一個(gè)“修訂號(hào)”以確認(rèn)該數(shù)據(jù)是否已經(jīng)被更新了。服務(wù)端將檢查客戶端所發(fā)送的修訂號(hào)是否與服務(wù)器上的數(shù)據(jù)一致,根據(jù)結(jié)果不同,或者通知客戶端不必更新數(shù)據(jù),或者將***的數(shù)據(jù)返回。
示例場(chǎng)景
對(duì)于性能與用戶體驗(yàn)的改進(jìn)使得本地緩存在許多場(chǎng)景中非常實(shí)用,這種實(shí)用性的關(guān)鍵條件在于數(shù)據(jù)無(wú)需進(jìn)行實(shí)時(shí)顯示。能夠?qū)?shù)據(jù)在本地緩存中保留的時(shí)間越長(zhǎng),這種方式的優(yōu)點(diǎn)就越突出。
這方面的例子包括用戶所感興趣的地區(qū)或聯(lián)系人,這種信息具有很高的實(shí)用性,同時(shí)又不太會(huì)非常頻繁地更新,因此是使用本地緩存理想的應(yīng)用場(chǎng)景。
本地隊(duì)列
一旦應(yīng)用程序失去了網(wǎng)絡(luò)連接,可以將服務(wù)器請(qǐng)求放入本地隊(duì)列,以便之后的處理。這種方式讓用戶能夠發(fā)起即發(fā)即棄(fire and forget)的操作,等到這些操作由服務(wù)器成功處理之后(如果確實(shí)經(jīng)過(guò)處理的話)收到操作完成的通知信息。

在本地隊(duì)列處理多個(gè)操作時(shí),你需要考慮以下問(wèn)題:
- 用戶應(yīng)當(dāng)收到該操作已加入隊(duì)列的通知。
- 用戶很可能想要了解隊(duì)列的實(shí)際狀態(tài),哪些操作已經(jīng)完成了,還有哪些操作還在等待中?
- 對(duì)于仍處于隊(duì)列中的操作來(lái)說(shuō),手動(dòng)撤消或重試的能力或許是十分重要的。
- 一旦這些操作被發(fā)送到服務(wù)器,用戶希望了解最終的處理結(jié)果(成功或失?。?。
- 當(dāng)操作進(jìn)入隊(duì)列之后,用戶有可能需要重新啟動(dòng)整個(gè)流程,因而需要中斷這個(gè)操作。
在使用者進(jìn)行審核或測(cè)量等現(xiàn)場(chǎng)工作,以及發(fā)送報(bào)告時(shí),本地隊(duì)列是一個(gè)很好的主意。如果這些操作不需要更新記錄,只是插入新記錄而已,那么整個(gè)實(shí)現(xiàn)會(huì)變得更加簡(jiǎn)單,無(wú)需進(jìn)行并發(fā)管理或沖突處理。
示例場(chǎng)景
本地隊(duì)列能夠幫助你在進(jìn)行操作時(shí)無(wú)需等待其結(jié)果,這對(duì)于庫(kù)存檢查或?qū)徍说炔僮鱽?lái)說(shuō)是非常重要的,讓使用者無(wú)需等待網(wǎng)絡(luò)連接也能夠使用該應(yīng)用,或發(fā)送報(bào)告 。
數(shù)據(jù)同步
通過(guò)使用本地緩存與隊(duì)列,你就能夠保持設(shè)備與服務(wù)器數(shù)據(jù)的更新,這就是我們熟知的“同步”。有以下幾種方式可以實(shí)現(xiàn)數(shù)據(jù)同步。

保持移動(dòng)端數(shù)據(jù)的更新
在這種場(chǎng)景下,你需要讓你的移動(dòng)應(yīng)用數(shù)據(jù)保持同步。有兩種方式可以實(shí)現(xiàn)這一需求:如上文所描述的方式一樣使用本地緩存、或者向服務(wù)器請(qǐng)求***的變更。這些***的變更也被稱為“增量”,能夠讓移動(dòng)應(yīng)用對(duì)服務(wù)器的當(dāng)前狀態(tài)進(jìn)行重建。為了能夠?qū)?**的更新進(jìn)行查詢,你需要使用一些審核字段,例如‘UpdatedOn’、 ‘CreatedOn’和‘DeletedOn’。
在第二種場(chǎng)景中,數(shù)據(jù)在設(shè)備中并未修改,因此也無(wú)需處理任何沖突,因此服務(wù)器上的數(shù)據(jù)總是正確的。
保持服務(wù)器數(shù)據(jù)的更新
這一點(diǎn)可以通過(guò)使用本地隊(duì)列實(shí)現(xiàn),但僅僅使用隊(duì)列本身是不夠的。如果在我的請(qǐng)求發(fā)送到服務(wù)器時(shí),服務(wù)器上的數(shù)據(jù)狀態(tài)與我嘗試進(jìn)行修改的時(shí)候已經(jīng)有所變化,這時(shí)該如何處理?如果請(qǐng)求的執(zhí)行被延遲,例如出現(xiàn)網(wǎng)絡(luò)連接斷開(kāi)的情況,就有可能造成并發(fā)沖突的增長(zhǎng)。在這種情況下,開(kāi)發(fā)者(或用戶)必須決定如何對(duì)服務(wù)器與應(yīng)用中的變更進(jìn)行“合并”。對(duì)于每一次數(shù)據(jù)沖突來(lái)說(shuō),有以下幾種合并方式:
- 保留設(shè)備上的版本
- 保留服務(wù)器上的版本
- 同時(shí)保留兩個(gè)版本
合并記錄的操作往往是由移動(dòng)開(kāi)發(fā)者通過(guò)自動(dòng)化實(shí)現(xiàn)的。至于具體使用哪一種算法,要由應(yīng)用程序的業(yè)務(wù)規(guī)則來(lái)確定。一旦出現(xiàn)無(wú)法完全自動(dòng)化的情況,將提示用戶做出決定。
同時(shí)保持移動(dòng)端與服務(wù)器的數(shù)據(jù)更新
這種方式也被稱為雙向同步。你很可能已經(jīng)猜到了,這種策略是對(duì)以上兩種技術(shù)的一個(gè)結(jié)合,它是目前所描述的場(chǎng)景中最完整,也是***大的。但請(qǐng)注意,雖然創(chuàng)建一種可以支持雙向同步的應(yīng)用很有誘惑性,但它也是目前為止最為復(fù)雜的一種場(chǎng)景。而且問(wèn)題不僅僅在于它的復(fù)雜性,正如我在本文中已經(jīng)提到的,雙向同步并不總是必要的。
示例場(chǎng)景
雙向同步為移動(dòng)應(yīng)用帶來(lái)了一個(gè)全新級(jí)別的用戶體驗(yàn),而必須使用雙向同步方式的關(guān)鍵條件之一是讓一個(gè)團(tuán)隊(duì)或小組里的所有用戶都能夠及時(shí)了解其它人的活動(dòng)情況。這方面的一個(gè)例子是協(xié)作應(yīng)用,需要顯示更新、注釋或狀態(tài)變更的***狀態(tài)??梢韵胂笕绻幸粋€(gè)協(xié)作式的地址簿,那么團(tuán)隊(duì)中的每位成員都能夠在任何時(shí)間更新其中的聯(lián)系人信息。
思考
為你創(chuàng)建的移動(dòng)應(yīng)用加入對(duì)離線場(chǎng)景的支持能夠極大地提高用戶體驗(yàn),但如何選擇合適的支持級(jí)別,并隨后實(shí)現(xiàn)這一功能,這并不是一件簡(jiǎn)單的任務(wù)。我在下文中列舉了一些當(dāng)你計(jì)劃為應(yīng)用加入離線功能時(shí)應(yīng)當(dāng)考慮的問(wèn)題。
數(shù)據(jù)大小
在進(jìn)行數(shù)據(jù)本地緩存時(shí),對(duì)于存儲(chǔ)的數(shù)據(jù)大小要有所警覺(jué)。盡量爭(zhēng)取在緩存的數(shù)據(jù)量與得到的用戶體驗(yàn)改善之間達(dá)到一種良好的平衡,這一點(diǎn)十分重要。如果數(shù)據(jù)量十分龐大(例如保存一個(gè)完整的Sharepoint網(wǎng)站內(nèi)容),你可能必須考慮為用戶提供一個(gè)選項(xiàng),讓他選擇要將哪些數(shù)據(jù)進(jìn)行緩存,以用于離線閱讀。
數(shù)據(jù)存儲(chǔ)
一定要對(duì)于如何進(jìn)行數(shù)據(jù)存儲(chǔ),以及存儲(chǔ)在何處作出明智的抉擇。這些數(shù)據(jù)是否包含敏感信息?如果是的話,就需要在保存數(shù)據(jù)時(shí)進(jìn)行加密。而一旦你選擇了對(duì)數(shù)據(jù)進(jìn)行加密,請(qǐng)確保將解密數(shù)據(jù)所用的密鑰保存在一個(gè)安全的地方,可以考慮使用操作系統(tǒng)的功能實(shí)現(xiàn)這一點(diǎn)。還要注意一點(diǎn),在某些平臺(tái)上你的應(yīng)用程序代碼是可以被閱讀(或反射)的,因此可以考慮對(duì)代碼進(jìn)行混淆。***,但并非無(wú)關(guān)緊要的是,確保你能夠通過(guò)某種機(jī)制遠(yuǎn)程地消除設(shè)備上的數(shù)據(jù)。像移動(dòng)設(shè)備管理(MDM)平臺(tái)等工具能夠幫助你實(shí)現(xiàn)這一功能,但也可以由應(yīng)用程序本身完成這一點(diǎn)。
電池使用
如果你計(jì)劃使用輪詢機(jī)制或是后臺(tái)作業(yè)(job),請(qǐng)確保你仔細(xì)考慮電池用量的情況。某些操作和網(wǎng)絡(luò)功能會(huì)很快耗光你的電量,并損害你的用戶體驗(yàn)。你可以在開(kāi)始啟動(dòng)一個(gè)耗電量很大的操作之前先檢測(cè)電池電量的狀態(tài),以及該設(shè)備是否正在充電等等。
數(shù)據(jù)的應(yīng)用
你或許需要對(duì)數(shù)據(jù)進(jìn)行查詢與操作(新增、修改、刪除),這取決于你的應(yīng)用程序的需求。在具有一定復(fù)雜性的場(chǎng)景中,使用數(shù)據(jù)庫(kù)作為持久化機(jī)制是一個(gè)不錯(cuò)的選擇。要選擇一個(gè)合適的數(shù)據(jù)庫(kù),需要考慮以下一些問(wèn)題:
- 對(duì)平臺(tái)的支持:我能夠在應(yīng)用的每個(gè)版本上都使用這個(gè)數(shù)據(jù)庫(kù)嗎?? (iOS、Android、Web、混合應(yīng)用等等……)
- 選擇關(guān)系型數(shù)據(jù)庫(kù)還是NoSQL數(shù)據(jù)庫(kù)技術(shù)
- 通過(guò)ORM的支持,將對(duì)象模型方便地映射到數(shù)據(jù)庫(kù)中
- 數(shù)據(jù)大小
- 對(duì)于同步協(xié)議的支持(例如CouchDB)
下面我們將逐個(gè)分析一些類庫(kù)及數(shù)據(jù)庫(kù),這對(duì)于我們實(shí)現(xiàn)離線功能非常實(shí)用。
使用類庫(kù)及數(shù)據(jù)庫(kù)
SQLite
SQLite是一個(gè)開(kāi)源的關(guān)系型數(shù)據(jù)庫(kù),非常適合于在移動(dòng)設(shè)備上使用。它使用一個(gè)單一的文件用于保存數(shù)據(jù),因此對(duì)于持久化的管理非常簡(jiǎn)單。對(duì)于同步及解決沖突來(lái)說(shuō),它起不到太大的作用,但對(duì)于信息的緩存及隊(duì)列操作來(lái)說(shuō),它是一個(gè)簡(jiǎn)單易用的選擇。它能夠支持主要的移動(dòng)平臺(tái),例如iOS、Android、Xamarin和Windows Phone。
SQLCypher
正如上文所說(shuō),如果你所緩存或加入隊(duì)列中的數(shù)據(jù)十分敏感,那么在保存數(shù)據(jù)的同時(shí)需要對(duì)其加密。SQLCypher是一種對(duì)SQLite數(shù)據(jù)庫(kù)進(jìn)行加密的一種十分健壯的選擇。它支持每種主流的移動(dòng)平臺(tái),但這個(gè)類庫(kù)是需要付費(fèi)的。根據(jù)安全級(jí)別和支持功能的不同,它有多個(gè)版本可以提供。
Couchbase Mobile
Couchbase最初的名稱是Membase,它是一個(gè)開(kāi)源的分布式NoSQL數(shù)據(jù)庫(kù)。它特別適合用于離線應(yīng)用的場(chǎng)景,因?yàn)樗軌蛲ㄟ^(guò)Couchbase Mobile和一個(gè)額外的同步網(wǎng)關(guān)對(duì)數(shù)據(jù)進(jìn)行雙向同步。它支持主流的移動(dòng)平臺(tái),包括Xamarin和PhoneGap,并且提供了本地文件加密的功能。
Meteor
Meteor是一個(gè)用于創(chuàng)建web應(yīng)用的開(kāi)源平臺(tái),內(nèi)置了實(shí)時(shí)更新的能力。Meteor是基于開(kāi)源的Node.js平臺(tái)和MongoDB所創(chuàng)建的,它提供了一種發(fā)布-訂閱機(jī)制,能夠?qū)?shù)據(jù)的變化實(shí)時(shí)傳遞給每個(gè)連接中的客戶端。
它通過(guò)PhoneGap和Cordova等混合工具支持所有移動(dòng)平臺(tái)。
結(jié)語(yǔ)
一旦用戶開(kāi)始期望他們的個(gè)人應(yīng)用能夠?qū)崿F(xiàn)與企業(yè)級(jí)應(yīng)用同等級(jí)別的用戶體驗(yàn),就無(wú)法忽視對(duì)離線功能的支持。能夠?yàn)橛脩籼峁╇x線場(chǎng)景的良好支持,將極大地改善移動(dòng)應(yīng)用的用戶體驗(yàn),對(duì)于員工的生產(chǎn)力也是至關(guān)重要的。
請(qǐng)留意在設(shè)備上進(jìn)行數(shù)據(jù)本地存儲(chǔ)時(shí)的安全問(wèn)題,并且請(qǐng)盡量不要低估你的應(yīng)用對(duì)于用戶的電池電量消耗可能造成的影響。




















