APP架構(gòu)經(jīng)驗總結(jié)_第1頁
APP架構(gòu)經(jīng)驗總結(jié)_第2頁
APP架構(gòu)經(jīng)驗總結(jié)_第3頁
APP架構(gòu)經(jīng)驗總結(jié)_第4頁
APP架構(gòu)經(jīng)驗總結(jié)_第5頁
全文預(yù)覽已結(jié)束

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、-從API開場一個App,最核心的東西,其實就是數(shù)據(jù),而數(shù)據(jù)的主要來源,就是API。我之前負責(zé)的工程,因為API的坑已經(jīng)受過了不少苦,因此,之后對App工程的架構(gòu)設(shè)計我都會先從API開場。制定平安機制設(shè)計API第一個需要考慮的是API的平安機制。我負責(zé)的上一個工程,因為API的平安問題,就被人攻擊了兩次。之后經(jīng)過分析,主要存在兩個漏洞:一是因為缺少對調(diào)用者進展平安驗證的方式,二是因為數(shù)據(jù)傳輸不夠平安。則,制定API的平安機制,主要就是為了解決這兩個問題: 保證API的調(diào)用者是經(jīng)過自己授權(quán)的App; 保證數(shù)據(jù)傳輸?shù)钠桨?。第一個問題的解決方案,我主要采用設(shè)計簽名的方式。對每個客戶端,Android

2、、iOS、WeChat,分別分配一個AppKey和AppSecret。需要調(diào)用API時,將AppKey參加請求參數(shù)列表,并將AppSecret和所有參數(shù)一起,根據(jù)*種簽名算法生成一個簽名字符串,然后調(diào)用API時把該簽名字符串也一起帶上。效勞端收到請求之后,根據(jù)請求中的AppKey查詢相應(yīng)的AppSecret,按照同樣的簽名算法,也生成一個簽名字符串,當(dāng)效勞端生成的簽名和請求帶過來的簽名一致的時候,那就表示這個請求的調(diào)用者是經(jīng)過自己授權(quán)的,證明這個請平安的。而且,每個端都有一個Key,也方便不同端的標(biāo)識和統(tǒng)計。為了防止AppSecret被別人獲取,這個AppSecret一般寫死在代碼里面。另外,

3、簽名算法也需要有一定的復(fù)雜度,不能輕易被別人破解,最好是采用自己規(guī)定的一套簽名算法,而不是采用外部公開的簽名算法。另外,在參數(shù)列表中再參加一個時間戳,還可以防止局部重放攻擊。第二個問題的解決方案,主要就是采用HTTPS了。HTTPS因為添加了SSL平安協(xié)議,自動對請求數(shù)據(jù)進展了壓縮加密,在一定程序可以防止監(jiān)聽、防止劫持、防止重發(fā),主要就是防止中間人攻擊。蘋果從iOS9開場,默認就采用HTTPS了。而關(guān)于在Android中如何使用HTTPS,Google官方也給出了很多平安建議。不過,大局部App并沒有按照平安建議去實現(xiàn),主要就是沒有對SSL證書進展平安性檢查,這就成為了一個很大的漏洞,中間人利

4、用此漏洞用假證書就可以通過檢查,從而可以劫持到所有數(shù)據(jù)了。因此,為了平安考慮,建議對SSL證書進展強校驗,包括簽名CA是否合法、域名是否匹配、是不是自簽名證書、證書是否過期等。接口協(xié)議標(biāo)準(zhǔn)化API返回的數(shù)據(jù),一般都是采用JSON格式進展傳輸。然而,JSON的值只有六種數(shù)據(jù)類型: Number:整數(shù)或浮點數(shù) String:字符串 Boolean:true 或 false Array:數(shù)組包含在方括號中 Object:對象包含在大括號中 Null:空類型我遇到過的,關(guān)于API的坑有大局部就是因為JSON數(shù)據(jù)和實體對象轉(zhuǎn)化時出錯導(dǎo)致的,而且是各種各樣的錯誤都有,其中不乏有一些很奇葩的錯誤。最麻煩的就

5、是處理Date類型,因為JSON本身沒有Date類型,因此,JSON庫將Date類型的數(shù)據(jù)序列化時會轉(zhuǎn)為String。這時,不同環(huán)境,不同平臺,以及用不同的JSON解析庫,轉(zhuǎn)換后的結(jié)果經(jīng)常會不同。比方,你在開發(fā)機上可能得到的結(jié)果是2016-1-1 17:11:11,但放到效勞器后結(jié)果卻變成了Jan 1,2016 5:11:11 PM,客戶端進展反序列化時無疑會失敗。后來,我取消了所有Date類型,統(tǒng)一采用時間戳表示,就再沒有轉(zhuǎn)化的煩惱了。另外,接口的開發(fā)人員有時候會將一些數(shù)據(jù)錯誤地轉(zhuǎn)換為了String,導(dǎo)致客戶端使用時因類型錯誤而異常。例如,本來是數(shù)字的1,被轉(zhuǎn)成了1,客戶端做運算時就會出錯,

6、或用switch判斷時也會出錯,或其他無法轉(zhuǎn)換的情況發(fā)生時;例如,為空時JSON正確地表示應(yīng)該是null,但如果轉(zhuǎn)為了String就變成了null,那問題就來了,我遇到的因為這個錯誤的轉(zhuǎn)換導(dǎo)致的程序奔潰已經(jīng)好幾次了,第一次的時候,查了一整天才定位到問題所在。還有,因為接口的開發(fā)人員不同,很多時候還會出現(xiàn)不同接口同一個意思的參數(shù)名稱卻不同。比方,對于有分頁數(shù)據(jù)的接口,一般都有當(dāng)前頁的參數(shù),A開發(fā)人員可能將參數(shù)命名為currentPage,第一頁是從0開場;B開發(fā)人員在另一個接口則命名為currPage,第一頁卻從1開場;C開發(fā)人員在另一個接口又命名為presentPage,第一頁又是從0開場???/p>

7、戶端的開發(fā)人員看到也是醉了。每個技術(shù)團隊一般都會有一份接口協(xié)議文檔,主要容包括每個接口的描述、入?yún)ⅰ⑤敵鼋Y(jié)果等,但一般并不嚴(yán)謹,很多地方?jīng)]有統(tǒng)一標(biāo)準(zhǔn),從而容易出現(xiàn)很多坑。因此,有一份統(tǒng)一標(biāo)準(zhǔn)且嚴(yán)格執(zhí)行的接口協(xié)議非常重要。協(xié)議的容除了規(guī)定每個接口,包括接口中每個數(shù)據(jù)具體的數(shù)據(jù)類型,還需要規(guī)定一套共用的數(shù)據(jù)字典,以及其他需要統(tǒng)一定義的信息,比方簽名算法等。一旦有了這份統(tǒng)一標(biāo)準(zhǔn)且嚴(yán)格執(zhí)行的接口協(xié)議,很多問題都將迎刃而解。接口版本控制我們已經(jīng)不止一次因為接口發(fā)生變動而導(dǎo)致舊版本的App出錯的問題,而且變動不一定是修改了接口本身,有可能是底層增加了一種新的數(shù)據(jù)構(gòu)造,接口把新數(shù)據(jù)也返回給客戶端了,但客戶端

8、舊版本是解析不了的,從而就導(dǎo)致出錯了。為了解決接口的兼容性問題,需要做好接口版本控制。實現(xiàn)上,一般有兩種做法:每個接口有各自的版本,一般為接口添加個version的參數(shù);整個接口系統(tǒng)有統(tǒng)一的版本,一般在URL中添加版本號,比方api.domain./v2。平時小版本的更新,就采用第一種方式,我們的做法是根據(jù)不同版本號做不同分支處理。大版本的更新,則用第二種方式,這時候,根本就是一套全新的接口系統(tǒng)了,跟舊版本是相對獨立的。當(dāng)版本越來越多時,維護就會成為一個大問題,我們沒則多精力去維護所有版本,因此,太舊的版本一般就不會再維護了。這時候,如果有用戶還在使用即將廢棄的舊版本,需要提醒用戶升級到新版本

9、。架構(gòu)分層API的設(shè)計完成之后,接下來我就會考慮App工程的整體架構(gòu)了。整體如何架構(gòu),我也曾經(jīng)做過不少嘗試。早期的時候,Android就是將所有操作都放在Activity里完成,包括界面數(shù)據(jù)處理、業(yè)務(wù)邏輯處理、調(diào)用API。后來發(fā)現(xiàn)Activity越來越臃腫,代碼越來越復(fù)雜,很難維護。于是就開場思考如何拆分,如何才能做到松耦合高聚。前面也說過,一個App的核心就是數(shù)據(jù),則,從App對數(shù)據(jù)處理的角色劃分出發(fā),最簡單的劃分就是:數(shù)據(jù)管理、數(shù)據(jù)加工、數(shù)據(jù)展示。相應(yīng)的也就有了三層架構(gòu):數(shù)據(jù)層、業(yè)務(wù)層、展示層。它們之間的關(guān)系如下列圖,數(shù)據(jù)層是三層中的最底層,往下,它接入API;往上,它向業(yè)務(wù)層交付數(shù)據(jù)。業(yè)

10、務(wù)層夾在三層中間,屬于數(shù)據(jù)的加工廠,將數(shù)據(jù)層提供上來的數(shù)據(jù)加工成展示層需要展示的數(shù)據(jù)。展示層處于三層中的最上層,主要就是將從業(yè)務(wù)層取得的數(shù)據(jù)展示到界面上。數(shù)據(jù)層數(shù)據(jù)層是數(shù)據(jù)管理者,主要任務(wù)就是封裝API,并將數(shù)據(jù)結(jié)果交付給上層,中間會再加個數(shù)據(jù)緩存。整個主流程如下列圖: 業(yè)務(wù)層向數(shù)據(jù)層請求數(shù)據(jù); 數(shù)據(jù)層檢查緩存中有沒有請求需要的數(shù)據(jù); 如果有緩存數(shù)據(jù),則直接返回緩存數(shù)據(jù); 如果沒有緩存數(shù)據(jù),則從網(wǎng)絡(luò)API獲取數(shù)據(jù),并將數(shù)據(jù)參加緩存,然后返回數(shù)據(jù)。調(diào)用網(wǎng)絡(luò)API時,還要判斷網(wǎng)絡(luò)狀態(tài),根據(jù)不同狀態(tài)做不同處理。如果網(wǎng)絡(luò)不可用,就無需發(fā)起請求了。網(wǎng)絡(luò)可用時,也要區(qū)分是連接WIFI還是連接移動網(wǎng)絡(luò)。連接

11、移動網(wǎng)絡(luò)時,一般需要限制調(diào)用比擬耗流量的請求。曾經(jīng),我們沒有對移動網(wǎng)絡(luò)狀態(tài)下的請求進展限制,結(jié)果,測試時流量DuangDuangDuang地一下子就不見了十幾M。連接WIFI時,則無需設(shè)置這種限制,而且還可以預(yù)先請求一些接口,比方請求當(dāng)前分頁數(shù)據(jù)時,可以將下一頁的數(shù)據(jù)也預(yù)先請求。緩存也需要緩存策略,不同的接口需要做不同的緩存處理。首先,緩存只適用于獲取數(shù)據(jù)的接口,對于修改數(shù)據(jù)的接口則不適用。其次,不同接口緩存時間一般也不同,對于很少變動的數(shù)據(jù)緩存時間可以設(shè)置長一些,而頻繁變動的數(shù)據(jù)緩存時間則比擬短,甚至不進展緩存。最后,緩存數(shù)據(jù)因為比擬多,我們一般保存在數(shù)據(jù)庫,而對于調(diào)用頻率高、最新的數(shù)據(jù),還

12、會在存中也擁有一份緩存,不過緩存時間比擬短。請求緩存數(shù)據(jù)時,會先檢查存緩存中有沒有,有則直接將緩存的數(shù)據(jù)返回,沒有才從數(shù)據(jù)庫獲取。則,如何將數(shù)據(jù)交付給業(yè)務(wù)層呢.這是整個數(shù)據(jù)層模塊與外部交互的局部,當(dāng)與外部交互的時候,一般都要符合面向接口編程的原則,因此只要提供開放的數(shù)據(jù)接口就可以了。對于接口的參數(shù)需要說明一下,上面提到的參數(shù)有appKey、version、currentPage這幾個,還有簽名sign、時間戳time,其實可以分為兩類:系統(tǒng)參數(shù)和業(yè)務(wù)參數(shù)。像appKey、version、sign、time這些屬于系統(tǒng)參數(shù),而currentPage,或username之類的則屬于業(yè)務(wù)參數(shù)。數(shù)據(jù)層

13、開放的數(shù)據(jù)接口的參數(shù)只需要包含業(yè)務(wù)參數(shù)就可以了,業(yè)務(wù)層并不需要關(guān)心系統(tǒng)參數(shù)是什么,系統(tǒng)參數(shù)在數(shù)據(jù)層部封裝API時指定就可以了。業(yè)務(wù)層業(yè)務(wù)層是數(shù)據(jù)加工者,主要就是從數(shù)據(jù)層獲取數(shù)據(jù),然后經(jīng)過業(yè)務(wù)邏輯處理后轉(zhuǎn)化成展示層需要的數(shù)據(jù)。業(yè)務(wù)層因為夾在數(shù)據(jù)層和展示層中間,起著承上啟下的作用。也因此,業(yè)務(wù)層很容易淪落為只是一個數(shù)據(jù)的中轉(zhuǎn)站,主要就是因為對業(yè)務(wù)層具體的作用和職責(zé)沒有理解清楚。這里用一個例子來說明業(yè)務(wù)層具體的工作吧,就舉個用戶注冊的例子。用戶注冊時,界面上需要用戶提供手機號、短信驗證碼、密碼、確認密碼。則,最簡單的操作就是,帶上這些參數(shù)調(diào)用數(shù)據(jù)層的注冊接口。好了,問題來了,注冊接口并沒有提供確認密

14、碼的參數(shù)。那好,調(diào)用注冊接口之前先判斷下密碼和確認密碼是否一致,不一致則返回錯誤提示給用戶,一致了才調(diào)用注冊接口。好了,第二個問題來了,用戶等網(wǎng)絡(luò)請求等了一段時間后,請求結(jié)果返回說手機號少了一位。下一次,又等了一段時間,這次又返回說手機號多了一位。就因為一個小錯誤要讓用戶等則久,用戶肯定有意見。后臺也有意見,各種非法的請求都發(fā)過來,是嫌效勞器壓力不夠大啊。那好,調(diào)用接口之前對這些參數(shù)做有效性檢查吧,手機號要規(guī),短信驗證碼只能為六位數(shù)字,密碼不能少于六位。終于注冊成功了,第三個問題又來了,注冊接口是沒有返回用戶的accessToken的,只有登錄接口才會返回。讓用戶手動再登錄一下.這用戶體驗不太

15、好啊。正確的姿勢應(yīng)該是注冊成功后再自動調(diào)用一次登錄接口,如果因為網(wǎng)絡(luò)問題第一次登錄失敗,后面還需要再自動調(diào)用多一次,如果還是調(diào)用失敗,才讓用戶手動登錄。上面的例子中,對參數(shù)的有效性檢查,注冊成功后的自動登錄,都屬于業(yè)務(wù)邏輯的處理,也就是說都是業(yè)務(wù)層的工作。業(yè)務(wù)層交付給展示層的數(shù)據(jù)也是通過接口的方式,不過,和數(shù)據(jù)層交付給業(yè)務(wù)層時不同的是:交付給展示層的數(shù)據(jù)應(yīng)該是通過異步回調(diào)返回的。因為獲取數(shù)據(jù)是一個比擬耗時的任務(wù),通過異步回調(diào)才不會阻塞UI主線程。展示層展示層作為數(shù)據(jù)展示者,它只要關(guān)心數(shù)據(jù)如何展示就可以了。不過,數(shù)據(jù)如何展示卻不是則簡單。展示層是三層架構(gòu)中最復(fù)雜的一層了,要考慮的東西遠遠多于其他

16、兩層,涉及的東西包括但不限于界面布局、屏幕適配、圖片資源、文本資源、顏色資源等等。在開發(fā)一段時間后,展示層出現(xiàn)代碼混亂是最常見的。因此,做好展示層,就需要保持高質(zhì)量的代碼。要保持高質(zhì)量代碼,我覺得至少應(yīng)該遵循幾條根本的原則: 保持規(guī)性:定義好開發(fā)規(guī),包括書寫規(guī)、命名規(guī)、注釋規(guī)等,并按照規(guī)嚴(yán)格執(zhí)行; 保持單一性:布局就只做布局,容就只做容,各自別離好,每個方法、每個類,也只做一件事情; 保持簡潔性:保持代碼和構(gòu)造的簡潔,每個方法,每個類,每個包,每個文件,都不要塞太多代碼或資源,感覺多了就應(yīng)該拆分。所謂無規(guī)矩不成方圓,展示層的設(shè)計,要從開發(fā)規(guī)開場。一份好的開發(fā)規(guī),是保證代碼有較高的可讀性的根底。

17、iOS方面,蘋果已經(jīng)有一套Coding Guidelines,主要屬于命名方面的規(guī)。當(dāng)我們制定自己的開發(fā)規(guī)時,首先就要遵守蘋果的這份規(guī),在此根底上再加上自己的規(guī)。Android方面,我也在我的博客中分享過一套Android技術(shù)積累:開發(fā)規(guī),主要分為書寫規(guī)、命名規(guī)、注釋規(guī)三局部。最重要的不是開發(fā)規(guī)的制定,而是開發(fā)規(guī)的執(zhí)行。如果沒有按照開發(fā)規(guī)去執(zhí)行,那開發(fā)規(guī)就等于形同虛設(shè),那代碼混亂的問題依然得不到解決。說到單一性,面向?qū)ο笤O(shè)計中,有一個根本原則就是單一職責(zé)原則,它規(guī)定一個類應(yīng)該只有一個發(fā)生變化的原因。保持單一性是減低耦合度的關(guān)鍵標(biāo)準(zhǔn),其目的就是各方面的解耦。而我這里說的單一性不只是規(guī)定類的單一,

18、也包括界面的單一、方法的單一、資源文件的單一等。界面的單一,首先是界面的布局和界面的數(shù)據(jù)應(yīng)該別離。另外,界面數(shù)據(jù)的獲取和展示也應(yīng)該別離。一句話,保持界面的單一性就是要保持界面上每個維度都做好別離,從界面的布局,到數(shù)據(jù)的獲取,數(shù)據(jù)的檢查,數(shù)據(jù)的展示。方法的單一,則表現(xiàn)為一個方法是對一個行為的封裝。行為又可以拆分為多個步驟,每個步驟其實也是更細化的行為。因此,方法嵌套方法是一種常態(tài)。則,保持方法的單一性,關(guān)鍵不在于怎么定義這個方法的行為,而在于這個行為要怎么拆分成更細的行為。舉個例子,通常在Activity的onCreate方法,做初始化操作,細分出來就分為了:控件的初始化、邏輯變量的初始化、數(shù)據(jù)

19、的初始化。數(shù)據(jù)的初始化又可以再細分:數(shù)據(jù)的獲取、數(shù)據(jù)的展示。每個細化的行為都應(yīng)該封裝為一個獨立的方法,這樣,才真正符合方法的單一性。資源文件的單一,主要是指Android的各類資源文件,包括存放字符串的strings.*ml,存放字符串?dāng)?shù)組的arrays.*ml,存放顏色值的colors.*ml,存放尺寸值的dimens.*ml,等等。資源文件的單一,是說所有相關(guān)的資源信息要在資源文件里定義并引用到代碼或布局文件里,而不是在代碼或布局文件里直接定義。這樣做,可以很方便地做各種適配和修改,比方支持國際化,比方不同分辨率的屏幕用不同尺寸值。iOS則沒有提供和Android一樣的資源文件別離的機制,

20、但可以參考Android的做法自己去實現(xiàn)。環(huán)境別離每個App工程,至少都會有兩個環(huán)境:測試環(huán)境和生產(chǎn)環(huán)境。多的甚至有四個環(huán)境:開發(fā)環(huán)境、測試環(huán)境、預(yù)生產(chǎn)環(huán)境和生產(chǎn)環(huán)境。開發(fā)人員經(jīng)常需要在環(huán)境之間切換,測試人員也同樣。經(jīng)常出現(xiàn)測試人員今天需要測試環(huán)境的最新版本,叫App開發(fā)人員打包一個給她,明天需要切換到生產(chǎn)版本,再叫App開發(fā)人員打包一個生產(chǎn)環(huán)境的給她。我們知道,一個App,在一臺手機上要么只能是測試環(huán)境的,要么只能是生產(chǎn)環(huán)境的。測試人員要測試兩個環(huán)境,只能不斷替換不同環(huán)境的同個App,這實在太麻煩了。為了解決此問題,最好的方案就是環(huán)境別離,不同環(huán)境有不同的App。一個App的唯一標(biāo)識,Android是用包名,iOS是用Bundle Identify。則,在一個系統(tǒng)想安裝不同環(huán)境的App,只要每個環(huán)境App的包名和Bundle Identify不同即可。比方,生產(chǎn)版的包名和Bundle Identify命名為.mydomain.myapp,測試版的包名和Bundle Identify則命名為,這樣,Android和iOS都會識別為兩個不同的App了。不過,只改包名和Bundle Identify是不夠的,應(yīng)用圖標(biāo)和應(yīng)用名稱也要修改,不然安裝之后很難區(qū)分哪個App是哪個環(huán)境的。一般做法就是,非生產(chǎn)環(huán)境的App圖標(biāo)就是在生產(chǎn)圖標(biāo)的根底上添加一個環(huán)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論