From:郭霖
前言
目前更多開發(fā)者熱衷于應(yīng)用開發(fā),極少數(shù)的開發(fā)者才有機(jī)會從事SDK開發(fā)工作,而市面上關(guān)于SDK開發(fā)介紹的文章少之又少,以至于讓大家覺得SDK開發(fā)是相對比較難而且非常無聊的工作,今天我們就來簡單的聊聊SDK開發(fā)的哪點(diǎn)事届惋。
關(guān)于SDK的解釋
什么是SDK
在開始正文之前,首先來聊聊 SDK 是個啥玩意.
SDK 是 Software Development Kit 的縮寫,譯為”軟件開發(fā)工具包”,通常是為輔助開發(fā)某類軟件而編寫的特定軟件包,框架集合等,SDK 一般包含相關(guān)文檔,范例和工具.
SDK 可以分為 系統(tǒng)SDK 和 應(yīng)用SDK.所謂的 系統(tǒng)SDK 是為特定的軟件包,軟件框架,硬件平臺,操作系統(tǒng)等簡歷應(yīng)用時所使用的開發(fā)工具集合.而 應(yīng)用SDK 則是基于 系統(tǒng)SDK 開發(fā)的獨(dú)立于具體業(yè)務(wù)而具有特定功能的集合.
比如在進(jìn)行 Android 應(yīng)用開發(fā)時,我們使用 Google 提供的 系統(tǒng)SDK(Android SDK),而我們經(jīng)常使用的 友盟SDK,極光SDK 則是基于 系統(tǒng)SDK 開發(fā)的.
明確 SDK 的概念之后,再來聊一聊這三個概念:Library, API, Framework
什么是Library
Library 即我們所說的庫,通常是一組或者幾組類的集合,通常是應(yīng)用中某些功能的具體實(shí)現(xiàn)或者對系統(tǒng)已有功能的增強(qiáng)或補(bǔ)充.對 Android 開發(fā)者而言,最常見的莫過于是 Support Library,另外就是我們經(jīng)常使用各種網(wǎng)絡(luò)請求庫(OkHttp, Volley),數(shù)據(jù)庫操作,圖片加載庫(Glide, ImageLoader)等.
什么是Framework
Framework 即我們所說的框架,通常是系統(tǒng)或者應(yīng)用的骨架,很多時候,它表現(xiàn)為一組抽象的構(gòu)建及構(gòu)件實(shí)例間交互的方法.因此,可以認(rèn)為,Framework 規(guī)定了應(yīng)用的體系結(jié)構(gòu),闡明了整體設(shè)計(jì),寫作構(gòu)件之間的依賴關(guān)系以及控制流程.注意自處的 Framework 并不完全等同于你所熟知的 Android Framework 框架,可以認(rèn)為 Android Framework 中體現(xiàn)了 Framework 的思想,并進(jìn)行了實(shí)現(xiàn).
什么是API
API 是 Application Programming Interface,又稱為應(yīng)用編程接口痕届,是軟件系統(tǒng)不同組成部分銜接的約定。更加通俗的說就 API 就是我們常見和編寫的方法或函數(shù).
小結(jié)
明確了上面提到的概念之后,現(xiàn)在就可以來描述這四者之間的關(guān)聯(lián):
SDK 主要包含 Framework, API 及 Library 的三部分.Framework 定義了 SDK 整體的可重用設(shè)計(jì),規(guī)定了 SDK 各功能模塊的職責(zé)以及依賴關(guān)系.其中個功能模塊體現(xiàn)為 Library.模塊之間的內(nèi)部通信及 SDK 外部通信(SDK對外提供服務(wù)的接口)則通過 API 進(jìn)行.
另外完整的 SDK 還應(yīng)該包含大量的示例和其他工具.比如在 Android SDK 的 tools 目錄下提供了大量的輔助開發(fā)工具.
對我們而言,大部分情況下是為某種具體的業(yè)務(wù)需求開發(fā)對應(yīng)的 SDK,以便作為第三正提供給其他需求方使用.比如百度推送的 SDK 主要實(shí)現(xiàn)消息推送功能,需求方只需要集成百度推送的 SDK 便可以使自己應(yīng)用具備推送功能.
到現(xiàn)在已經(jīng)介紹了 SDK 的主要構(gòu)成,接下來我們重點(diǎn)來介紹 SDK的實(shí)現(xiàn)目標(biāo) 以及 在SDK架構(gòu)中的一些核心點(diǎn).
淺談SDK實(shí)現(xiàn)目標(biāo)
上面介紹了開發(fā)中常見的概念,現(xiàn)在來談?wù)?SDK 的實(shí)現(xiàn)目標(biāo).任何應(yīng)用都應(yīng)具備:簡潔易用,穩(wěn)定,高效,輕量,SDK 作為一種特定應(yīng)用當(dāng)然也不例外.
簡潔易用
按照”奧卡姆剃須刀”理論,一個好的產(chǎn)品對第三方使用者使用而言應(yīng)該是簡潔易用,不用改讓使用者花費(fèi)太長時間學(xué)習(xí)的.這對SDK同樣適用—SDK不應(yīng)該對宿主應(yīng)用有過多的代碼侵入,也不應(yīng)該有復(fù)雜頻繁的接入工作.比如當(dāng)開發(fā)者需要使用SDK的服務(wù)時,只需要在緣由的代碼中新增一行即可.常見的SDK初始化如下:
public class Ad {
@TargetApi(9)
public synchroized static void init(
Context context, SdkParams params) {
//省略
}
}
當(dāng)我們需要使用該 SD K的服務(wù)時,通過一行代碼便可啟用 Ad.init(this,params)
要保證較少的代碼侵入主要在對外提供服務(wù)時充分考慮到使用者的使用場景來設(shè)計(jì)出優(yōu)良的API.一個 優(yōu)良的API 在定義的時候應(yīng)該滿足絕大數(shù)開發(fā)者所預(yù)期的方式—語義上要求通俗易懂,使用上要求簡單可靠.
一個優(yōu)良的API首先是簡單可靠的.在正常使用的情況下體現(xiàn)為穩(wěn)定可靠的執(zhí)行,在異常情況下體現(xiàn)為及時的告知使用者使用錯誤.初次之外,遵循一致的明明規(guī)則,并是所有的API呈現(xiàn)出一致的風(fēng)格對開發(fā)而言無疑是個好消息.
穩(wěn)定
站在 SDK 使用者角度來看,我們期望第三方的 SDK 服務(wù)應(yīng)該是穩(wěn)定高效的,體現(xiàn)在提供穩(wěn)定可靠的服務(wù),在不影響宿主穩(wěn)定性的前提下足夠的高效,這就要求我們 SDK 設(shè)計(jì)者在設(shè)計(jì)并實(shí)現(xiàn) SDK 時要盡可能的做到以下幾點(diǎn):
對外提供穩(wěn)定的API.SDK 的API一旦確定,如無非常嚴(yán)重情況不可更改.作為提供服務(wù)方,發(fā)生 API 變更所帶來的變更成本非常大.
對外提供穩(wěn)定的業(yè)務(wù).在穩(wěn)定的 API 后,必須要有穩(wěn)定的業(yè)務(wù)來支撐.
SDK 運(yùn)行時的穩(wěn)定,作為服務(wù)提供方,我們必須確保 SDK 自身運(yùn)行的穩(wěn)定,并且保證接入方不會因?yàn)槲覀兊?SDK 產(chǎn)生不穩(wěn)定的情況.
版本穩(wěn)定更新.和面向普通用戶的應(yīng)用相比,SDK 版本的迭代是非常緩慢的.并且需要盡可能的對開發(fā)者屏蔽迭代過程,以免給開發(fā)者帶來不必要的適配開銷.
高效
無論是普通的應(yīng)用開發(fā)還是SDK開發(fā),都應(yīng)該考慮到性能問題,SDK設(shè)計(jì)者應(yīng)該著重考慮以下問題:
更少的內(nèi)存占用.在不使用多進(jìn)程的情況下,SDK 服務(wù)和宿主程序運(yùn)行在同一進(jìn)程中,這種情況下必須要求限制 SDK 內(nèi)存的占用,不能因?yàn)檎f因?yàn)槲覀?SDK 占用太多的內(nèi)存資源,導(dǎo)致應(yīng)用的存活時間變短.
更少的內(nèi)存抖動.在占用更少內(nèi)存的前提下,SDK 設(shè)計(jì)者必須刻意的減少反復(fù)GC造成的內(nèi)存抖動問題.
更少的電量消耗.盡管很多時候無法對電量消耗做一個很好的權(quán)衡,但是仍然有一些可以參考的做法,比如減少使用耗電模塊的時間.比如在使用定位服務(wù)時,不要求非常高的精度下優(yōu)先使用網(wǎng)絡(luò)定位而不是GPS定位.
更少的流量消耗.
SDK整體架構(gòu)設(shè)計(jì)
SDK 的架構(gòu)實(shí)現(xiàn)決定了 SDK 后續(xù)的維護(hù)難度,因此有必要在此對 SDK 整體架構(gòu)中的一些點(diǎn)做些簡單的說明.
模塊化開發(fā)
根據(jù)單一職責(zé)將系統(tǒng)拆分為不同的小模塊杆故,每個模塊保持相對獨(dú)立歌粥。
模塊之間通過協(xié)議或接口通信诈豌,以減少相互之間的依賴耦合.模塊內(nèi)部按照設(shè)計(jì)的幾大原則進(jìn)行實(shí)現(xiàn),以保證模塊本身可以靈活實(shí)現(xiàn)
對于現(xiàn)代開發(fā)而言,模塊化是常用的手段,從宏觀角度來看,模塊是系統(tǒng)最小的組成單元.
組件化開發(fā)
組件開發(fā)同樣是個老生常提的概念,但從我個人的感受來說,組件是對邏輯的封裝,并具備單個可移植性.比如可以把日志記錄做成一個組件,之后它可以被輕松在應(yīng)用在不同的項(xiàng)目中.對于 Android 開發(fā)者而言,Android 提供的每個UI 控件同樣也是組件,比如 Button,TextView等.
在明確了組件這一概念之后,組件化開發(fā)也就不難理解:所謂的組件化就是將整個項(xiàng)目劃分成多個模塊,幾個模塊或者單個模塊作為一個組件,開發(fā)過程中我們可以對每個組件進(jìn)行并行開發(fā),最后發(fā)布時通過依賴將組件合并成完整的應(yīng)用.
那為什么要使用組件化呢?隨著 Android 的逐漸成熟,現(xiàn)在的app業(yè)務(wù)越來越復(fù)雜,與此同時,Android工程也變得日益龐大,代碼行數(shù)十幾萬已經(jīng)是常態(tài),此時有幾個問題便會凸顯出來:
工程任何一點(diǎn)改動都會造成整個工程的重新編譯.記憶最深的就是早期在沒有進(jìn)行組件化的時候,龐大的工程動輒需要十幾分鐘的編譯時間,一杯茶的時間就出來了,很多時候,不得不眼巴巴的等著,盡管現(xiàn)在可以使用 facebook 出品的 buck 以及來自阿里的 feeline 來加速編譯過程,但是仍然不夠.
整個工程中充斥的大量重復(fù)或者冗余的子模塊,業(yè)務(wù)耦合度非常高,牽一發(fā)而動全身.這就造成了”老人不敢改,新人無法改”,因?yàn)檎l也不能預(yù)知在做修改之后,會產(chǎn)生什么影響.
協(xié)作開發(fā)基本上是不可能的,天知道彼此在做什么.代碼合并的的時候更是令人痛苦.
不方便測試.高度耦合的業(yè)務(wù)和模塊導(dǎo)致無法下手進(jìn)行測試,只能草草了事.
通過引入組件化,上面遇到的問題便可迎刃而解.在 SDK 當(dāng)中,根據(jù)實(shí)際情況對其進(jìn)行組件化,比如我們將分享功能組件化,可以輕松的支持多種渠道的分享,在需要更新分享功能時,可以對其進(jìn)行單獨(dú)的編譯和測試.
通過組件化,我們也可以輕松的實(shí)現(xiàn) SDK 的定制功能,通過編寫編譯腳本,我們可以決定哪些組件被依賴,最終合并到完整的應(yīng)用當(dāng)中.比如友盟中的提供的可定制分享組件(如下圖)的原理就是如此.
插件化開發(fā)
什么是插件化開發(fā)這里就不做介紹了,一方面插件化并不是個新概念,另外就是插件化到目前為止理論層次上已經(jīng)非常成熟,不想15念開始研究的時候資料相對較少.
在 SDK 中為什么使用插件化呢?SDK 不同于普通應(yīng)用,不能頻繁的進(jìn)行更新,以免讓開發(fā)者覺得 SDK 不穩(wěn)定或者讓開發(fā)者頻繁的集成.SDK 看起來變化較慢,實(shí)則變化頻繁.就以以前做的 廣告SDK 而言,有時候經(jīng)常需要對某類機(jī)型進(jìn)行數(shù)據(jù)采集或者及時更新反作弊模塊,在沒有使用插件化之前,解決該問題是非常麻煩的.但是在我們利用插件化之后,解決該問題就變得非常容易.
我們將 SDK整體 劃分為兩部分:宿主和插件.宿主只向開發(fā)者提供必要的服務(wù)接口,并提供了自定義插件加載器.而核心的邏輯則是存在于插件中.當(dāng)需要采集數(shù)據(jù)的時候,只需要由開發(fā)人員開發(fā)好數(shù)據(jù)采集插件并下發(fā)到指定設(shè)備即可;當(dāng)需要修復(fù)SDK缺陷時,同樣也只需要下發(fā)新的插件包即可.
通過在 SDK 使用插件化方案,可以有效的對開發(fā)者屏蔽手動更新的過程.宿主相對穩(wěn)定,一旦確定,一般不會變動,而后續(xù)的業(yè)務(wù)變化則只需要通過更新插件來支撐.
除了上面談到的利用插件化解決動態(tài)更新之外,通過將整個工程分為宿主和插件可以實(shí)現(xiàn)宿主的并行開發(fā)和分開編譯,并且能有效的解決方法數(shù)65535的限制.
SDK初始化
和應(yīng)用開發(fā)不同,很多情況下 SDK 沒有自身的上下文 Context,而必須要借助應(yīng)用提供.SDK 初始化的常見做法: Ad.init(Context context,AdParams params),我們往往推薦開發(fā)者在應(yīng)用 Application 組件中的 onCreate() 中去掉用該方法,這就意味著該初始化過程是同步的,假如 SDK 本身初始化時間較長,就會影響應(yīng)用的啟動速度.
在這種情況下,作為 SDK 的設(shè)計(jì)者必須著手解決該問題.通常將 SDK 服務(wù)進(jìn)一步劃分成核心服務(wù)和輔助服務(wù),之后通過并行初始化和延遲初始化的手段來減少 SDK 初始化耗時.曾經(jīng)在我所負(fù)責(zé)的廣告 SDK 中,有開發(fā)者反饋我們的 SDK 啟動較慢,通過對整個 SDK 啟動流程進(jìn)行分析后,我們將插件加載服務(wù)和云控服務(wù)并行初始化,而對于像日志服務(wù)則采用顏值初始化,通過該手段有效的減少了初始化耗時
云更新控制
云控服務(wù)作為一種服務(wù)端控制客戶端的手段在SDK中開發(fā)中非常重要,現(xiàn)在的SDK開發(fā)可以不支持插件化,但是必須要提供云控服務(wù),以便讓服務(wù)端能控制 SDK,比如在不需要進(jìn)行數(shù)據(jù)采集的時候,可以通過云控服務(wù)關(guān)閉 SDK 采集功能,在需要的時候在將其打開.
對本身是基于插件化開發(fā)的 SDK 而言,云控服務(wù)更是不可或缺.
從實(shí)現(xiàn)的角度而言,云控服務(wù)分為服務(wù)端主動和客戶端主動.服務(wù)端主動是指服務(wù)端會將最新的云控開關(guān)的信息推送到 SDK,而客戶端主動則是 SDK 在進(jìn)行操作之前會首先請求云控信息.對有推送開發(fā)經(jīng)驗(yàn)的同學(xué)而言,這非常容易理解,就是像是為了實(shí)現(xiàn)消息推送功能,我們可以通過客戶端輪訓(xùn)也可以通過服務(wù)端保持長連接進(jìn)行消息推送一樣.
安全
SDK自身安全
為了區(qū)分接入者并增加 SDK 自身安全性,我們通常會為開發(fā)者分配 api key 和 api secret,SDK 會讀取開發(fā)者配置的 api key 和 api secret,并用于隨后的網(wǎng)絡(luò)通信中.這是非常常見的做法,比如當(dāng)你集成極光推送 SDK 的時候,它也許需要你提供 api key 和 api secret,如果沒有則需要到官網(wǎng)進(jìn)行申請.
核心邏輯采用C/C++
為了安全起見,數(shù)據(jù)加密類,模塊算法類都都應(yīng)該采用NDK開發(fā),將其封裝在so文件當(dāng)中.有很多開發(fā)者不明白為什么這樣會增強(qiáng)安全性.這里我們簡單的做個說明.由于.so文件是通過C/Cpp編譯出的文件,相對于Java的反編譯文件來說,可讀性更差,另外大部分的Android開發(fā)者并不具備###較深的C/Cpp能力,因此一定程度上增加了被破解的能力.
通訊加密
針對實(shí)際情況對通訊協(xié)議進(jìn)行加密,具體是采用對稱加密還是非對稱加密,則需要根據(jù)實(shí)際情況做選擇.另外,請盡可能使用https來代替http.
設(shè)備安全
在很多情況下,比如廣告 SDK 中,有一些開發(fā)者會通過虛擬機(jī)來刷廣告,因此有必要針對此情況做判斷.一旦 SDK 檢測出非法請求后可以采取兩種方案,一種是 SDK 拒絕服務(wù),另外一種則是正常服務(wù),SDK 會將作弊信息上傳至服務(wù)器,以便后端服務(wù)定向排除數(shù)據(jù).
減少傳輸數(shù)據(jù)大小
在設(shè)計(jì) SDK 和服務(wù)端通訊之間的數(shù)據(jù)協(xié)議時,需要根據(jù)實(shí)際情況考慮,但有以下幾條建議值得我們接受:
如果對傳輸?shù)臄?shù)據(jù)大小有要求,建議對數(shù)據(jù)進(jìn)行壓縮.
可以采用 json/xml/Protobuf 等協(xié)議,如果它們?nèi)匀徊荒軡M足則可以考慮自定義二進(jìn)制協(xié)議.
選擇支持最低系統(tǒng)版本
作為 SDK 的設(shè)計(jì)者,面臨一個很大的問題是我們不得不考慮開發(fā)者應(yīng)用所支持的系統(tǒng)最小版本,但是在 SDK 發(fā)布之前,我們并不知道會什么樣的開發(fā)者使用我們提供的服務(wù),因此為了讓 SDK 支持更廣泛的設(shè)備,我們需要降低最低支持的系統(tǒng)版本.比如現(xiàn)在市面上主流的系統(tǒng)版本是Android 5.0,那么對 SDK 而言,起碼要支持到Android 4.0,甚至是Android 2.3.
降低最低支持版本看起來很容易,但是我們不得不做更多的工作來確保SDK能表現(xiàn)出一致的工作行為(通常,我們在SDK內(nèi)部檢測當(dāng)前系統(tǒng)版本來確定哪些方法可以被調(diào)用).更殘酷的真相是我們花費(fèi)了很大的精力去支持2.3,但來自2.3系統(tǒng)版本的請求量卻連1%都不到.
權(quán)限管理
Android中任何開發(fā)都避不開權(quán)限申請.作為 SDK 的設(shè)計(jì)者,對于權(quán)限遵循”如無必要,無需增加”,換句話說就是用不到的權(quán)限,就不要加上去,這也是我們所謂的最小權(quán)限原則,該原則同樣適用于普通應(yīng)用開發(fā).
在剛接觸 SDK 開發(fā)時,某些早期功能需要某些權(quán)限,但是后期該功能被砍掉了,但是權(quán)限卻忘記去掉,這就導(dǎo)致不必要權(quán)限仍然存在的情況.
另外過多的權(quán)限申請,會讓開發(fā)者懷疑你的目的.比如一個廣告SDK的你申請照相機(jī)權(quán)限是想干嘛?恩,我懷疑你在偷拍我….好吧,這里我只是開個玩笑.
另外,從Android 6.0以上,google改變了權(quán)限申請的策略,因此需要單獨(dú)對此做適配.
日志服務(wù)
無論系統(tǒng)大小,日志服務(wù)是基本的服務(wù).一個良好的日志服務(wù)能夠幫助我們快速的發(fā)現(xiàn)問題,定位缺陷,從而獲得問題的解決方案.
SDK 的日志服務(wù)和其他常見的日志服務(wù)并無太大的不同,但是要保證以下幾點(diǎn):
日志服務(wù)能夠記錄有效的信息,在SDK要關(guān)鍵位置進(jìn)行打點(diǎn).
日志服務(wù)上傳日志信息到服務(wù)器時,要保證最大的可靠性,不能發(fā)生上傳失敗后拋棄日志的情況.
日志服務(wù)不能影響對正常的操作流程有過多的性能影響.SDK 產(chǎn)生的日志信息往往是非常多的,因此必須考慮日志IO操作所帶來的開銷.
深究API設(shè)計(jì)
API 的設(shè)計(jì)在任何開發(fā)中都是非常重要的,很多時候軟件的質(zhì)量好不好在 API 的設(shè)計(jì)可以得到體現(xiàn).在普通的應(yīng)用開發(fā)中,API 只會在應(yīng)用開發(fā)人員間流通而不會暴露給非本應(yīng)用開發(fā)的其他人員,但是 SDK 作為一種服務(wù),需要向開發(fā)者暴露一部分 API.通常我們將內(nèi)部流通的 API 稱之為 內(nèi)部API,而開放給開發(fā)者的稱之為 SDK API.
兩者使用場景雖然不同,但是都遵循著一些通用的設(shè)計(jì)規(guī)則,這里無法細(xì)說,只列出我認(rèn)為需要重點(diǎn)關(guān)注的十一條原則:
方法名能夠表明其用途
方法名是理解方法含義的第一渠道.一個好的方法名首先是能夠向他人展示自身功能,這樣做的好處就是能夠減少不必要的溝通成本,對于開發(fā)者而言,還有什么比直接讀代碼更直觀呢.
參數(shù)的合法性檢驗(yàn)
對參數(shù)進(jìn)行合法性檢驗(yàn)是非常重要的,請不要想當(dāng)然的認(rèn)為可以用運(yùn)行時異常來代替.當(dāng)合法性校驗(yàn)不通過時,針對方法權(quán)限不同分別對應(yīng)不同不同的處理策略:
對于公開方法通過顯示檢查拋出異常的方式婆翔,并且使用javadoc的@throw來說明拋出異常的原因
對于私有方法通過斷言的方式來檢查參數(shù)的合法
檢查構(gòu)造方法的參數(shù)的合法性,以使對象處在統(tǒng)一狀態(tài).需要注意,如果檢查的代價太大爸业,需要綜合考量,比如如果接受的是一個很大的List河泳,此時檢查的代價可能很大
方法要明確其單一的功能
一個方法應(yīng)該具有單一的功能沃呢,盡可能做更少年栓,但是更專的事情.這也是我們常說的單一職責(zé)原則.另外一定要記住寧可提供小而美的方法也不要提供大而全的方法,經(jīng)驗(yàn)正面大而全的方法往往發(fā)生變動,產(chǎn)生風(fēng)險的可能性更高,因此不如提供更小的方法以便組合使用
方法異常問題
對于需要暴露給開發(fā)者的方法要及時的拋出可查異常來幫助開發(fā)者在編譯階段發(fā)現(xiàn)問題,另外,對于運(yùn)行時異常,SDK 設(shè)計(jì)者必須保證該類異常不會導(dǎo)致宿主程序出問題并且需要告知開發(fā)者.
方法權(quán)限控制
方法的權(quán)限也是需要著重考慮的,SDK 設(shè)計(jì)者必須同時從安全和業(yè)務(wù)的角度考慮哪些方法是可公開的,哪些是不可公開以及哪些是靜態(tài)的.
避免過長參數(shù)
過長的參數(shù)會造成記憶上困難,需要慎重對待.在無法避免過長參數(shù)的情況下,需要考慮其他的方法進(jìn)行解決:
a. 通過使用Builder模式來實(shí)現(xiàn)
b. 通過使用輔助類,通常采用靜態(tài)內(nèi)部類的方式,具體見靜態(tài)內(nèi)部類的使用
c. 通過將多個參數(shù)封裝成類對象
d. 通過將參數(shù)拆解成多個方法的參數(shù)
謹(jǐn)慎使用方法重載
重載不應(yīng)該讓使用者感到疑惑,即不應(yīng)該出現(xiàn)這種情況:同樣的參數(shù),但是開發(fā)者不能明確哪個方法會被執(zhí)行.換言之就是不要產(chǎn)生歧義性.
另外需要注意,不要存在參數(shù)類型經(jīng)過自動轉(zhuǎn)換就可以運(yùn)行在另外一個方法的情況,我曾經(jīng)在code review 中看到這樣的代碼:list中的 remove(Object) 和 remove(int),請務(wù)必保證自己不會犯類似的錯誤.
盡管在java當(dāng)中能夠使用重載,但是我不建議使用,尤其是不要重載變長參數(shù),在需要重載的時候?qū)幙墒褂貌煌椒麃泶嬉惨玫亩?關(guān)于這點(diǎn)java中提供的 ObjectOutputStream 類給我們做了很好的示范:它的write對于每個基本類型都有一個變形,比如寫出字符,寫出boolean等操作,我們發(fā)現(xiàn)設(shè)計(jì)者,并沒有使用重載將其設(shè)計(jì)成 write(Long l),write(Boolean b),而是將其設(shè)計(jì)為 writeLong(l),writeBoolean().
對于構(gòu)造函數(shù),則可以通過是用靜態(tài)工廠的方式來代替重載.
謹(jǐn)慎使用變長參數(shù)
多數(shù)情況下不需要使用變長參數(shù),一般方法的參數(shù)在5個以上的時候,才 建議使用變長參數(shù).在還有其他非變長參數(shù)的情況下,我覺得變長參數(shù)放在形參列表的最后.
避免方法直接返回NUll
對于需要返回?cái)?shù)組或這集合的方法,不要返回null.比如我們?nèi)ベI糕點(diǎn)店買面包,面包沒了是一種正常狀態(tài),就不應(yīng)該返回null,而是返回長度為0的數(shù)組或集合.
必要時進(jìn)行保護(hù)性拷貝
當(dāng)類接受來自客戶端的對象或者需要向客戶端返回對象,如果該類不能容忍進(jìn)來的對象再發(fā)生變化,那么有必要對對象進(jìn)行保護(hù)性拷貝.另外要注意參數(shù)的合法性檢驗(yàn)發(fā)生在保護(hù)性拷貝之后.
需要注意的是如果需要進(jìn)行保護(hù)性拷貝的對象非常大,比如list集合中存在十多萬個對象,需要權(quán)衡處理.
這十一條原則是我在團(tuán)隊(duì)中推廣并要求嚴(yán)格遵守的,下面,將對這十條原則分別進(jìn)行說明.
SDK開發(fā)流程
關(guān)于SDK開發(fā)流程,我會從以下三個方面寫:一時團(tuán)隊(duì)中如何協(xié)同開發(fā),二是SDK的持續(xù)集成,三是SDK多倉庫拆分和管理.
這三方面會再另外的篇章中展現(xiàn)(具體什么時候?qū)懲昴壳斑€未確定)
SDK版本管理策略
SDK 版本號命名及修改原則
SDK版本號命名和我們以往的命名規(guī)則并無太大不同,通由4部分組成,格式為:
V 主版本號 子版本號 階段版本號_日期版本號加希臘字母版本號.比如:V1_1_2_161209_beta.
希臘字母版本號說明
Alpha版:內(nèi)部測試版,此版本表示該軟件在該階段主要是以實(shí)現(xiàn)功能為主,Bug相對較多,需要繼續(xù)修改,通常只在內(nèi)部流通流通而不對外開放.
Beta版:外部測試版,該版本相對Alpha已經(jīng)有了很大的改進(jìn),不存在嚴(yán)重的Bug,但還是存在一些缺陷,需要進(jìn)一步的測試以檢查和消除Bug.
RC版:該版本已經(jīng)相當(dāng)成熟,不存在導(dǎo)致錯誤的Bug.與正式版相差無幾.
Release版:該版本意味著”最終版本”,是最終交付用戶或者公開發(fā)布的版本,也稱為標(biāo)準(zhǔn)版.需要注意的是,該版本在發(fā)布的時候回以符合R來代替Release單詞.
版本號修改規(guī)則
主版本號變化:當(dāng)功能模塊有較大的變化或者整體架構(gòu)發(fā)生變化
子版本號變化:當(dāng)功能有一定變化
階段版本號變化:一般是Bug修復(fù)或者較小的變動,根據(jù)反饋,需要經(jīng)常發(fā)布修訂版本.
日期版本號(161209):用于記錄修改項(xiàng)目的當(dāng)前日期,每天對項(xiàng)目的修改都要更改日期版本號.
希臘字母版本號:此版本?號用于標(biāo)注當(dāng)前軟件處于那個開發(fā)階段,當(dāng)軟件進(jìn)入到另一個階段是需要修改.
API版本管理
和普通應(yīng)用API版本管理不同,SDK 設(shè)計(jì)者需要著重關(guān)注SDK API的管理.原則上SDK API一旦公開發(fā)布后其狀態(tài)(簽名和具體實(shí)現(xiàn))應(yīng)為不可變.
對于特殊情況下API的變更,需要遵守”開閉原則”,即一個類,模塊,方法應(yīng)該對擴(kuò)展開發(fā),對修改關(guān)閉.這就要求我們做到以下幾點(diǎn):
在需要調(diào)整SDK API時,優(yōu)先選擇添加新方法,而不是在原方法上修改.對于實(shí)現(xiàn)相同功能的新方法,盡可能的要兼容原始方法.
在需要廢除某些方法時,需要在正式版發(fā)版前使用 @deprecated 標(biāo)識,并給出替代方案和廢棄的時間(通常是SDK版本號)
接入文檔和API文檔版本管理
接入文檔是用來告訴 SDK 使用者,如何使用 SDK,使用的詳細(xì)步驟和可能發(fā)生的問題,每個公司會有自己的一套規(guī)則,這個不需要做太多的解釋.
另外,接入文檔通常分為兩份:內(nèi)部版和公開版.內(nèi)部版通常用于內(nèi)部開發(fā)人員和測試人員,信息較為詳細(xì),而公開版則是面向開發(fā)者,相比內(nèi)部版會省略的一些信息.
API文檔其實(shí)就是對 SDK API 的更詳細(xì)說明,類似 java 中的 api doc,可以借助jdk的自帶javadoc直接生成,當(dāng)然在Android Studio也提供了便捷的生成方式.
無論是接入文檔還是api說明文檔,其變更一般發(fā)生在SDK版本發(fā)生變化時.當(dāng)SDK發(fā)生變更時,文檔必須隨之更新,不能出現(xiàn) SDK 更新后說明文檔不與之匹配的情況.
集成Demo版本管理
集成Demo通常是一個簡單的app,用來展示如何快速的接入SDK.其版本變更策略和SDK版本的變化保持一致.
總結(jié)
SDK開發(fā)中需要關(guān)注的點(diǎn)非常多,每個點(diǎn)都不能用三言兩語完成的,后面會在此基礎(chǔ)上慢慢的補(bǔ)充.
推薦
郭霖大神的微信公眾號