男怕入錯行,女怕嫁錯郎。html
肥肥工做六年有餘,其中四年的時間致力於 SDK
開發。細細想來,唏噓不已,感觸頗多。android
SDK
開發是一份不被理解而又枯燥無趣的工做。據說,每一個 SDK 開發者
上輩子都是折翼的天使。因此,程序猿與程序媛們,若是你身邊有 SDK 開發者
,請愛護他們。git
這些年來,肥肥都假設,使用個人 SDK
的開發者都是一羣肥肥打不過又跑不贏的暴力狂,最關鍵的是還知道我住哪裏。github
六年來一路荊棘,一路坎坷,一事無成。數據庫
揚雄亦慕仲尼之教者,以著書立言爲事,得自易哉。編程
目前來講,並無統一的、官方的文檔定義 Android
應用開發中常見的 Library
、API
、SDK
以及 Framework
這些概念。咱們基於其字面意思以及平常使用習慣作以下的解釋:安全
Library
是一組或幾組類的集合,能夠直接調用,使得開發更高效。Library
每每是對系統已有功能的加強或是對應用程序架構中功能模塊的具體實現。好比 Android SDK
中提供的Support Library
,著名的開源項目如Volley
、Picasso
、Android-Universal-Image-Loader
。Application Programming Interface
,也就是軟件系統不一樣組成部分(模塊)銜接的約定。因爲軟件的規模日趨龐大,經常須要把複雜的系統劃分紅小的組成部分,API
的設計就顯示尤其更要。良好的程序設計實踐中,API
的設計首先要使軟件系統的職責獲得合理的劃分,下降系統各組成部分的相互依賴,並提高組成單元的內聚性,從而提升系統的可維護性以及擴展性。Software Development Kit
,普遍意義上的 SDK
通常都是爲特定的軟件包、軟件框架、硬件平臺、操做系統等創建應用程序時所使用的開發工具的集合(系統 SDK
)。而狹義上的 SDK
(應用 SDK
) 則是基於系統 SDK
進行開發的新的、獨立於具體業務且完成特定功能的一組工具的集合。例如友盟統計 SDK
、極光推送 SDK
、多盟廣告 SDK
。Framework
是整個或部分系統的可重用設計,表現爲一組抽象構件及構件實例間交互的方法。另外一種定義認爲,Framework
是被應用開發者定製的應用骨架。能夠說,一個 Framework
是一個可複用的設計構件,它規定了應用的體系結構,闡明瞭整個設計、協做構件之間的依賴關係、責任分配和控制流程,表現爲一組抽象類(或接口)以及其實例之間的協做方法,它爲構件複用提供了上下文(Context
)關係。通常來講,SDK
是 Framework
、API
以及 Library
的集合。Framework
定義了 SDK
總體的可重用設計,規定了 SDK
各功能模塊的職責以及依賴關係。SDK
中功能模塊的具體實現則是 Library
的主要職責。各模塊之間的通訊以及 SDK
所能提供的服務則經過 API
體現出來。網絡
一般狀況下,SDK
在應用程序中是做爲特定功能提供者的角色出現的。例如推送功能的 SDK
、統計功能的 SDK
、廣告功能的 SDK
、性能監測功能的 SDK
以及分享功能的 SDK
等等。架構
前文中說到 SDK
是做爲應用程序中特定功能的提供者而存在的。一般狀況下,SDK
是做爲第三方服務而被引入到應用程序中的,SDK
的品質可以影響到應用程序的品質。框架
肥肥認爲,好的 SDK
產品應該是易於使用的。咱們想要創造一種簡單的模式,讓 SDK
的使用者在他們的應用中方便的使用 SDK
,那麼這種模式應該是不須要侵入太多的代碼或者不須要繁瑣的集成工做的。
若是一個 API
的調用方式正好是開發者所預期的方式,那麼咱們認爲該 API
的調用方式是易用的表現。多數狀況下,API
的品質直接決定了 SDK
的品質。SDK
的易用性體如今 API
的易用性上,那麼,好的 API
設計也就顯得尤其重要。
一般狀況下,API 難以被誤用
也是易用性的一種,這樣能夠有效地避免一些錯誤的發生。好比,對參數的校驗、對邊界的嚴格檢查以及詳細的說明文檔,都將使得開發者在使用 SDK
的時候,可以有效地避免一些錯誤的。
從 SDK 使用者
角度來講,在 SDK
使用過程當中,咱們假設 SDK
自己是可靠的,不會影響到程序自己的穩定性。那麼,從 SDK 設計者
的角度來講,SDK
做爲第三方服務,其穩定性是尤其重要的。這種穩定性體如今以下四個方面:
API
的穩定性,SDK
對外的 API
一旦肯定,其變動的成本(即便僅僅在某個 API
上增長或減小一個參數)是很是高昂的。API
穩定性的補充。底層的 SDK 業務
一般決定了上層 API
的形態。SDK 運行時
的穩定性,做爲第三方服務提供者,自身的穩定性很是重要。SDK 版本迭代
的穩定性,相對於應用程序的 Release 版本
迭代速度,SDK
的 Release 版本
的迭代速度是相對緩慢的。頻繁的 SDK
升級會給應用程序開發者帶來額外的升級成本,並給應用程序開發者留下 SDK
不穩定的印象。一般狀況下,SDK
開發者並不能像應用開發者那樣擁有更多的選擇權。咱們不能選擇設備,系統版本,甚至是目標客戶。相應的,咱們須要最大化支持設備,提供高度靈活的 API
設計,以知足不一樣客戶羣的須要。
可讓開發者選擇不一樣的依賴管理器或者構建工具來集成 SDK
,是靈活性的一大致現。面對形形色色的應用程序開發團隊,咱們也要儘量的去迎合這些團隊所使用的開發環境,提供一些主要的開發工具插件的支持,包括 Gradle
、Maven
以及 Ant
等。
靈活性設計的關鍵是瞭解你的 SDK 用戶
的需求,而後作出須要支持的最低系統版本的決定。咱們很但願咱們的 SDK
可以支持儘量多的系統設備,對於這一點,下降支持最低操做系統版本是頗有必要盡力去作的。
但從另外一方面來看,兼容低版本也是要付出代價的。並無什麼直接的法則可以告訴咱們如何才能在繁瑣度和更好的兼容性上權衡。支持舊的操做系統版本,一般意味着不能使用操做系統的新特性,同時還要面對一些舊版本存在的問題。除此以外,咱們還要花費更多精力去測試代碼的正確性以及兼容性。
相對於 PC
來講,移動設備的硬件資源顯得尤其珍貴。SDK
應儘量的下降以下幾種系統資源開銷:
CPU
,SDK
在儘量下降內存佔用的狀況下,也應該儘可能保證內存佔用的穩定性(避免內存抖動
)。SDK
的電量開銷很難有統一的標準來衡量。手機的各個硬件模塊的耗電量是不同的,有些模塊很是的耗電,而有的模塊耗電量則相對顯得很小。可是,儘量的爲用戶省電是值得推薦的作法。CPU
的開銷來講,應用程序的使用者對於電量以及網絡流量消耗更爲敏感。Android
設備的存儲路徑大體可以分爲兩類:/data/data/package_name/
和/storage/sdcard0/Android/data/package_name/
目錄)SD
卡下非應用程序目錄存儲對於 SDK
來講,若是沒有徹底的必要性(好比使用 SharedPreference
),選擇 SD
卡目錄存儲數據是一種不錯的選擇。
這樣作的好處在於,一方面能夠減小/data/data/package_name/
和/storage/sdcard0/Android/data/package_name/
目錄的存儲壓力,另外一方面則方便多個應用程序間共享文件。
固然,不管使用何種存儲目錄,爲 SDK
建立獨立的文件夾是很是有必要的(好比 SDK
使用/data/data/package_name/sdk_cache
目錄),這也是爲了方便將 SDK
文件與應用程序文件區分開來。
相較於目前動輒16G 起步的存儲空間來講,應用程序的使用者對於電量和網絡流量的消耗顯得尤其敏感。究其緣由,多是電量和網絡流量是應用程序使用者可以直接接觸到的一些指標。在即使是網絡流量白菜價的年達,也會有很大一部分用戶由於摳門亦或是運營商等緣由,仍舊使用着每個月幾十兆流量的套餐。而對於電量的敏感,可能就是由於現代人都有的 低電量恐懼症
這樣時髦的毛病了。
內存以及 CPU
的過分使用,一方面帶來了過分的電量開銷,另外一方面則可能形成應用程序卡頓甚至 ANR
等問題。
這些問題都可以或直接或間接的影響到應用程序使用者對一款應用程序的評價。
衆所周知,Android
系統中主線程
又被稱爲 UI 線程
,理想狀況下,主線程
只負責向 UI 組件分發事件(觸屏事件
、渲染事件
等)。
系統並不會爲每一個組件建立單獨線程,在同一個進程中的 UI 組件
都會在 UI 線程
中實例化,系統對每個組件的調用都從 UI 線程
分發出去。那麼由此引起的問題就是,響應系統回調的方法(組件生命週期
、觸屏事件
等)都是在 UI 線程
中執行的。
若是全部的工做都是在 UI 線程
中執行,特別是作一些耗時的操做(Http 請求
、數據庫查詢以及文件讀寫等),都會阻塞 UI 線程
,致使事件的分發中止。從用戶的角度來看,是應用程序卡頓甚至卡住了。更爲糟糕的狀況是,若是 UI 線程
阻塞的時間過長(UI 線程
中大約5秒,BroadcastReceiver
中大概10秒),系統就會彈出 ANR
(Application Not Response
)對話框。
從另外一個方面來講,Android
的 UI 組件
並不是是線程安全
的,也就意味着不能從非 UI 線程
操做 UI 組件
。因此,SDK
的線程模型有四條重要的設計原則:
UI 線程
;UI 線程
以外操做 UI 組件
(SurfaceView
不受該原則限制)。SDK
必須,不然不能使用應用程序主線程。若是必須使用主線程,那麼不能長時間佔用。SDK
應該有一個專門的線程來處理 SDK
的相關業務。Android 應用程序權限機制
限制應用程序訪問特定的資源,如照相機、網絡、存儲系統以及查詢用戶數據以及以及某些 API
的調用。
通常來講,系統會在應用程序安裝過程當中提醒用戶該應用程序所申請的權限,若是所申請的權限太高(Root 權限
)則會在應用程序申請該權限時彈出窗口進行通知。
而自 Android 6.0
開始則使用了全新的權限控制系統,除了以上權限控制的機制以外,還會在應用程序訪問特定系統功能時(好比使用藍牙模塊),也會經過彈出窗口的形式的進行通知。
相對應的,在 SDK
開發過程當中,咱們應該爲 Android 6.0
及以上版本單獨作權限方面的適配工做。
那麼,做爲第三方服務的 SDK
必定要遵循的一個原則就是:最小權限原則
。最小權限原則
指的是,SDK
儘量不要申請非必要的權限,儘量的不要給使用 SDK
的應用程序帶來額外的權限申請。
舉例來講,若是 SDK
中並無使用到撥打電話的功能,可是卻要求應用程序開發者在AndroidManifest.xml
文件中聲明 android.permission.CALL_PHONE
權限,那麼就是違反了最小權限原則
。
違反最小權限原則
並不會對 SDK
自己的業務帶來任何影響,可是這會使得應用程序向系統申請沒必要要的權限而形成的額外的權限開支。由此帶來的後果是用戶對於應用程序的不良印象。
SDK
做爲服務的提供者,定義清晰且嚴謹的生命週期模型顯得尤其重要。一種簡單的作法就是 SDK
的生命週期託管給當前 Activity
的生命週期管理。由此帶來的好處就是,SDK
能夠在恰當的時機作恰當的事情。好比咱們能夠在 onCreate()
的生命週期中完成一些初始化的工做,而在onDestroy()
的生命週期中完成對象的銷燬工做以及在應用程序的onPause()
狀態暫定一些後臺的操做以節省資源。
本文中,咱們假定 API
設計只涉及以下兩方面:
SDK
對外提供服務的 API 設計
,後文簡稱 SDK API
或者公共 API
;SDK
內部各模塊間的 API 設計
,後文簡稱模塊 API
。之因此將這兩方面拆分出來,是由於肥肥以爲這是兩種不一樣的 API
設計理念。首先是面向的用戶羣體不一樣,SDK API
面向的是 SDK 使用者
,也就是商業用戶,而模塊 API
則是面向 SDK
團隊中的其餘開發人員。其次,SDK API
是由具體的使用場景而決定的,而模塊 API
則是由具體的功能而決定的。
從公司的角度來講,API
的通用商業價值是能夠進行評估的。從數據的角度來看,API
應該算是公司資產的一種,由於設計優良的 API
實現了數據的可訪問性、準確性、可應用性以及安全性。每個公共 API
都在某些程度上提供了特定數據的可訪問性,而設計優良的公共 API
則很大程度上保證了數據的準確性以及安全性。對於每個開發人員來講,只要參與到編程的過程當中,那麼你就是一名 API
的設計者——由於好的代碼便是模塊,每個模塊就是一個 API
(雖然這並不適用於 SDK API
的開發)。
與 SDK
內部模塊 API
的設計相比,SDK API
的設計難度要更大一些。 咱們下文中的討論圍繞 SDK API
的設計展開,固然其也適用於模塊 API
的設計。
好的 API
設計來自於迭代過程。
在開始設計你的 API
以前,你應該先了解設計這個 API
的目的,這也就意味着咱們要設計出一種接口,讓它的使用方式符合 API
自己的設計目的。做爲 SDK 開發者
,咱們對 API 設計
所作的任何一個決策都會影響到 SDK
產品的質量。在咱們可以作出一個正確的決策以前,極可能會先作出一個錯誤的決策,並從中吸收教訓。實際上,在經歷了屢次的錯誤決策以後纔可能接近正確的決策。
這正是 API 設計
中迭代的意義。在實際的操做過程當中,咱們所面臨的一項挑戰在於,在某個 API
發佈以後,再進行變動的成本變得很是高昂,並伴隨着很是大的風險。
咱們力求在 API
變動的成本變得高昂以前,就消除易用性與設計方面的問題。這須要強有力的對於產品需求的把控、全面的測試以及深厚的 API 設計
功力來保證。
設計良好的 API
應該具有以下幾個特色:
單一職責原則說的是在類或方法的設計中,應該保證有且僅有一個引發類或方法變化的緣由。通俗來講就是一個類或方法只負責一項職責。若是有兩個比較接近的功能,可是使用一個接口實現有點繁瑣,那麼就應該使用兩個接口。不要爲了減小接口的數量而生硬的把兩個接口合併爲一個。
接口調用中應儘量少的要求調用中傳遞參數。若是 SDK
能本身獲取的參數就不須要讓開發者傳遞。
在同一個接口中使用大量的相同類型的參數也是不推薦的。若是沒法避免,建議將參數封裝成對象。
參數合法性校驗應該是接口要作的第一件事情。全部的參數必須校驗其合法性,並視具體業務對不合法參數進行處理。通常狀況下,除了必要參數,對於其餘參數可使用默認值或者區間值(超過最大、最小值使用最大、最小值)的方式來確保業務的正常流程。若是必要參數不合法,能夠考慮使用拋出運行時異常的方式通知開發者。
開發者常常容易不耐煩,因此對於一些錯誤或異常,應該儘量早的拋出。好比一些可以在編譯期間就能拋出,終歸好於在運行期間拋出。也就是說,SDK 開發者
應該儘量早的把一些能夠預期的異常拋出,以便讓開發者儘快處理這些異常。
正式發佈的 SDK
的接口應該是穩定的,這其中包括其參數類型、返回值類型、異常類型。
咱們假設正式發佈的 SDK
中的任何一個接口,都有機會被調用。那麼,這樣也就要求咱們在後續的版本迭代中保證接口的參數類型、返回值類型以及異常類型是統一的。
若是須要變動接口功能,建議增長新的接口而不是改變現有接口。
在 SDK
的升級、維護策略中,版本管理是一個很是重要的組成部分:
SDK 版本
的特定信息,以及已使用的 SDK
的升級版本的可用狀況;SDK 開發者
須要使用版本號來定位 SDK
使用過程當中所存在的問題,並創建 SDK
升級的依據。版本號的命名及管理並無統一的標準,不一樣的團隊每每使用不一樣的命名風格。
可是不管使用哪一種版本命名風格,給出詳盡的版本變動記錄是一種不錯的選擇。
按照軟件版本的發佈階段來看,一款成熟穩定的 SDK
產品的版本迭代每每會經歷以下階段:
SDK 產品
在此階段主要是以實現功能爲主,一般只在開發團隊內部交流使用。通常來講,該版本的 SDK 產品
存在的 Bug
較多,須要經歷多個 alpha 版本
的迭代才能進入 beta 版
。alpha 版
已經有了很大的改進,修復了嚴重的 Bug
,可是還存在一些已知或是未知的 Bug
,一般狀況下只在開發團隊以及測試團隊之間交流使用,須要經歷多個 beta 版本
的迭代才能進入 rc 版
。SDK
趨於成熟,基本上不會出現致使錯誤的 Bug
,原則上再也不增長新的功能,與正式發佈的正式版沒有太大的差別。一般狀況下該版本用於進行小規模灰度測試,原則上不會提供給應用程序開發者使用。一個比較合理的版本號命名規範由以下四部分組成:
V1_0_2_201511171733_beta
SDK
總體架構發生變化時,由需求決定是否修改。Bug
以及小規模調整時,須要常常發佈修訂版,此時可由項目經理決定是否修改。SDK
發佈時的時間以及當前的迭代狀態。原則上,當項目處於 alpha
、beta
以及 rc 版
時,該版本號須要體現每一次的修改時間以及狀態。當項目處於 release 版
時,該版本號用於記錄該版本的發版時間。API
的版本受到 SDK 版本迭代狀態
的約束,可是不受 SDK 版本號
修改原則的限制。
只有處於 release
(或 rc
) 狀態的 API
才能是對外提供服務的,不然該 API
應該是對應用程序開發人員不可見的。換句話說就是,堅定不發佈處於 alpha
和 beta
狀態的 API
。
API
一旦對外發布,其內部實現以及方法簽名原則上處於不可變動狀態:
API
的內部實現,在保證方法簽名不變的狀況下,API
必須經過測試用例的邊界及功能測試,並儘量的給出原 API
實現的備份——使用oldMethodName
前綴標識原 API
;API
不變的狀況下,使用方法重載實現新的 API
。API
,應在 SDK release 版本
迭代的前 N 個版本使用 @deprecated
標識須要廢棄的 API
,並給出該 API
的替代方案以及具體的 API
移除時間(或是 SDK
版本)。SDK
一旦發佈,你將沒法強制要求應用程序開發者跟隨你的 SDK 版本
迭代而更新他們的代碼。在必定週期內,將會有多個 SDK 版本
在提供服務,除了建議開發者升級 SDK
以外,服務端將不得不爲多個 SDK
版本提供支持。
從另外一方面來看,隨着需求的變動,API
會相應的增長或聲明廢棄。與之相對應的,Http 接口
每每也會發生相應的變化。
一種比較合理的作法是,文檔以及 SDK
對應的 Demo
受 SDK 版本
的管理。更爲簡便的作法就是文檔以及 Demo
採用 SDK 版本號
進行統一管理。廣泛的作法是,即使是 SDK
接口的輕微改變,也要及時的體如今對應的文檔上,並更新對應的 Demo
。在 SDK
上線初期,其迭代頻率相對較高,那麼就會出現多個版本 SDK
共存的狀況。合理的文檔、SDK
以及 Demo
間的版本關係,也就顯得尤其重要。
SDK
開發是一個很大的範疇,相較於應用程序的開發,有類似之處,也有不一樣之處。從面相的客戶全體來講,應用程序開發者面向的是普通用戶,而 SDK
開發者則面向應用程序開發人員。從服務的角度來講,應用程序開發人員在設計應用的時候,每每要考慮性能、兼容性、用戶體驗、渠道以及版本迭代。而 SDK
開發人員不只要全面考慮上面這些因素,還要近乎於苛刻的將性能、兼容性提高到極致。對於某項需求的驗證,應用程序開發人員會選擇在部分灰度版本中進行驗證,而 SDK
開發人員則沒有這樣的幸運,只能依賴對業務的高度抽象進行驗證。固然,目前廣泛的作法是基於本身的 SDK
開發相應的應用程序,一方面可以進行一些需求的驗證,另外一方面,本身成爲本身的客戶,也何嘗不是一件壞事。
肥肥不才,文章先後修改數次,歷經四月,終於寫完《Android SDK 開發》的第一部分。這期間肥肥仔細拜讀了 參考文獻 中各位前輩的文章,受益頗多。肥肥在文章的有些章節內容中,直接參考了一些前輩的觀點,甚至存在一些直接複製的行爲。在此向各位前輩致以最高的敬意,併爲肥肥的剽竊行爲做出道歉。
剩餘的內容將會圍繞 SDK
的測試、安全性、業務配置以及數據運營展開討論。