在開發(fā) Android App 時(shí),開發(fā)人員最容易出現(xiàn)困惑的地方之一應(yīng)該是 compileSdkVersion浮梢、minSdkVersion 和 targetSdkVersion 這些參數(shù)的用途绍坝。對(duì)此撕蔼,有 Google 內(nèi)部的 Android 開發(fā)人員就這個(gè)議題寫了一篇文章:Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion屯援。在這里會(huì)用不同的角度來說明這些參數(shù)的用途剂公,希望能對(duì)大家在理解上有所幫助吆视。
buildToolsVersion
首先典挑,這是在那篇文章中沒有說明到的另一個(gè)與版本有關(guān)的參數(shù)。這個(gè)參數(shù)主要是用來指定用哪一組編譯程序來將你所編寫的 Source Code 轉(zhuǎn)成操作系統(tǒng)可以載入的形式啦吧,也就是產(chǎn)出 Apk您觉。所以 Build Tools 在概念上可以看成是 Android Studio 的一部份或是其中一個(gè)套件,只是同時(shí)會(huì)有好幾組授滓。
譬如說要修理車輛琳水,每一組的 Build Tools 就像是有人把修車的工具,扳手般堆、起子在孝、套筒之類的放在一個(gè)包包里。只是隨著工具設(shè)計(jì)的翻新淮摔,包包里的工具可能會(huì)做調(diào)整私沮,像是起子原本是各尺寸一只,現(xiàn)在可能改成只有一只但可以換各種尺寸的頭和橙。同樣是拿來修車顾彰,工具也一樣,但是為了區(qū)別內(nèi)容物的不同所以給了不同的編號(hào)胃碾。
選用哪一組 Build Tools 原則上不會(huì)有太大的差異涨享,大概就是編譯的效能、產(chǎn)出的文件大小等等的細(xì)節(jié)仆百。但有時(shí)候還是可能會(huì)牽涉到 Apk 內(nèi)容或結(jié)構(gòu)的問題影響運(yùn)行厕隧,仍然要多注意改版的資訊。最保險(xiǎn)的做法還是讓 buildToolsVersion 的主版本編號(hào)與 compileSdkVersion 同步俄周。
compileSdkVersion
compileSdkVersion 則是 Build Tools 在進(jìn)行編譯時(shí)吁讨,檢查源代碼在呼叫 SDK 所使用的規(guī)格是否正確的基準(zhǔn),但所指定的 SDK 并不會(huì)被編入 Apk 中峦朗。Google 會(huì)在發(fā)行新版系統(tǒng)時(shí)對(duì) SDK 做調(diào)整建丧,為了保持相容性,在規(guī)格上不太可能采取激進(jìn)的方式波势,例如:同樣名稱的函式改為不同的參數(shù)規(guī)格翎朱。最常出現(xiàn)的大概就是在函式上標(biāo)注 @Deprecated橄维,等系統(tǒng)發(fā)行過幾個(gè)版本之后再正式廢止。
buildToolsVersion 及 compileSdkVersion 顧名思義就是在編譯拴曲、生成時(shí)期所使用的工具争舞,這句話的重點(diǎn)是編譯過了、順利產(chǎn)出 Apk澈灼,不代表運(yùn)行上就沒有問題竞川,而且所謂的運(yùn)行還要細(xì)分是在哪個(gè)版本上運(yùn)行。因?yàn)?SDK 不是包含在 Apk 里叁熔,是包含在 Android 的系統(tǒng)之中委乌。Apk 在哪一個(gè)版本的 Android 中運(yùn)行,調(diào)用?的就是對(duì)應(yīng)版本的 SDK荣回。所以運(yùn)行調(diào)用叫會(huì)不會(huì)報(bào)錯(cuò)福澡,跟編譯會(huì)不會(huì)成功是沒有直接的關(guān)連,除非運(yùn)行的環(huán)境和 compileSdkVersion 指定的版本相同驹马,但也僅限于規(guī)格的部份。
用旅游來做例子除秀,Build Tools 就好像旅行社糯累,把你對(duì)旅游的構(gòu)想轉(zhuǎn)成實(shí)際的行程。而 Compile SDK 則是旅行社在行前檢查所使用的檢查表册踩,用來確認(rèn)在行前是不是有什么細(xì)節(jié)遺漏了泳姐。選用哪一個(gè)旅行社對(duì)于規(guī)劃行程沒有什么太大的差別,頂多只是能夠提供服務(wù)的多寡或細(xì)致的程度暂吉。而行前檢查表如果用錯(cuò)了版本就有可能會(huì)出問題胖秒,像是要去的目的地簽證條件改變了,舊的版本沒有列上去慕的,在行前檢查時(shí)一切沒問題阎肝,到了當(dāng)?shù)夭虐l(fā)現(xiàn)沒有辦法入境,于是整個(gè)行程就報(bào)銷了肮街。但如果是用新的檢查表风题,就可以在行前發(fā)現(xiàn)問題,并且做出補(bǔ)救的措施嫉父,確保行程能夠如計(jì)劃進(jìn)行沛硅。
由于 Google Play 只限制 App 可運(yùn)行的 SDK 最低版本,并沒有限定最高的版本绕辖。也就是摇肌,使用者可以在硬體廠商有提供更新的情況下不斷地升級(jí)系統(tǒng)版本,而 App 則會(huì)一直保持在可安裝的狀態(tài)下仪际。如果一直不更動(dòng) compileSdkVersion围小,則有一些變動(dòng)在編譯時(shí)期不會(huì)被發(fā)現(xiàn)昵骤,前段提到的標(biāo)注 @Deprecated 就是一例。前幾次的系統(tǒng)版本更新在運(yùn)行 App 上可能不會(huì)有問題吩抓,但是一旦真的有函式被廢止了涉茧,則要到運(yùn)行時(shí)才能發(fā)現(xiàn),這時(shí)要偵錯(cuò)就不是件容易的事疹娶。但如果是使用新的 compileSdkVersion伴栓,則是在編譯時(shí)就會(huì)有對(duì)應(yīng)的訊息供作參考。
所以在開頭提到的文章中有一個(gè)很重要的關(guān)鍵點(diǎn)是:當(dāng)編譯的結(jié)果還存有警告時(shí)雨饺,一定要進(jìn)行對(duì)應(yīng)的處理钳垮,這些警告的設(shè)計(jì)是有其作用的。
targetSdkVersion
在推出新的 Android SDK 版本有函式的行為新舊版本不一致時(shí)额港,為保持相容性饺窿,舊的行為會(huì)被保留,并且會(huì)以 targetSdkVersion 的值來判斷要運(yùn)行哪一種行為移斩。以開頭提到的文章中舉例的 AlarmManager#set 來說肚医,依官方文件的說明,Android 4.4 (API 19)以后使用的是不精確的時(shí)間向瓷,以減少設(shè)備被喚醒的次數(shù)及降低電量的耗損肠套。官方文件中的另一段注解文字說到:targetSdkVersion 小于 API 19 的會(huì)繼續(xù)之前舊的行為,也就是 SDK 中會(huì)判斷 targetSdkVersion 的資訊來決定運(yùn)行的源代碼內(nèi)容猖任。
以前段提到旅游的例子來說你稚,當(dāng)實(shí)際出發(fā)到了當(dāng)?shù)氐暮jP(guān),targetSdkVersion 的規(guī)則就如同海關(guān)柜臺(tái)人員不是一體套用新的簽證規(guī)則朱躺,而是會(huì)好心地依照填寫的申請(qǐng)日期決定適用的簽證規(guī)則刁赖。如果填的是新規(guī)則公布之后的日子,代表填寫的人應(yīng)該知道新的簽證規(guī)則长搀,所以當(dāng)然就照新的簽證規(guī)則來審核宇弛。相對(duì)地,填寫的日期在新規(guī)則公布之前的日子源请,則會(huì)使用舊的簽證規(guī)則來審核涯肩。
Android 透過這種方式讓 App 在新的系統(tǒng)版本中不會(huì)有預(yù)期之外的行為出現(xiàn),即便 Google 打算在新版本中改變?cè)镜男袨槌驳觥K?targetSdkVersion 主要的用途是讓開發(fā)者告知 SDK病苗,開發(fā)者所認(rèn)知的函式行為應(yīng)該是哪一種,如果 targetSdkVersion 是設(shè)定成改變之前的值症汹,則 SDK 會(huì)運(yùn)行舊的版本硫朦,否則就運(yùn)行新的。對(duì)開發(fā)者來說背镇,修改 targetSdkVersion 后代表的意義是:開發(fā)者已經(jīng)知道了新的 SDK 行為咬展,并且確認(rèn)這個(gè)行為是 App 所需要的泽裳。所謂的確認(rèn)通常指的是要在參數(shù)指定之對(duì)應(yīng)版本的運(yùn)行環(huán)境中做過測(cè)試,而不光只是編譯成功破婆。
依照以上的說明涮总,如果在更換了 targetSdkVersion 內(nèi)容,卻還是使用萬年的模擬器設(shè)定來進(jìn)行測(cè)試祷舀,以致模擬器上系統(tǒng)的版本一直沒換瀑梗,這時(shí)就有必要重新檢討開發(fā)時(shí)的測(cè)試策略了。
minSdkVersion
最直接的用途就是要求 Google Play 設(shè)定 App 可被安裝的系統(tǒng)版本最底限裳扯,在這個(gè)版本之后的則不會(huì)被限制抛丽。
對(duì)開發(fā)者來說有另外一個(gè)要觀注的重點(diǎn)和 targetSdkVersion 一樣,minSdkVersion 也具有相同的意義饰豺。試想有一個(gè) App 是在 Android 4.4 (API 19)開發(fā)的亿鲜,預(yù)期的是 AlarmManager#set 的新行為,但是當(dāng) minSdkVersion 的值比 API 19 小時(shí)冤吨,程式運(yùn)作在 API 18 以下的環(huán)境中一定沒有 AlarmManager#set 的新行為蒿柳,這時(shí) App 會(huì)產(chǎn)生什么結(jié)果?
如果是上架很久的 App漩蟆,源代碼應(yīng)該也是對(duì)應(yīng)著系統(tǒng)版本演進(jìn)的歷程做修改垒探。所以源代碼中或多或少都有包含先前所提到和 SDK 類似用系統(tǒng)版本來判斷程式要運(yùn)行的新、舊邏輯區(qū)塊爆安,以適應(yīng)各種不同版本的系統(tǒng)。在這種情況之下仔引,minSdkVersion 理所當(dāng)然的可以是很早期的版本數(shù)值扔仓。
如果是新開發(fā)的 App,因?yàn)闆]有經(jīng)歷過這些過程咖耘,所以 targetSdkVersion 和 minSdkVersion 在開發(fā)初始的階段里意義上是一樣的翘簇,這時(shí)對(duì) minSdkVersion 調(diào)整最保守的做法應(yīng)該是逐次逐版地往前調(diào)。當(dāng) minSdkVersion 的數(shù)值逐版向前調(diào)整時(shí)儿倒,也應(yīng)逐版在對(duì)應(yīng)的環(huán)境中做過測(cè)試版保,確保程式在舊行為下所呈現(xiàn)的結(jié)果也符合預(yù)期,并且在必要時(shí)要加上對(duì)新舊版本的判斷式夫否、補(bǔ)上合于舊版環(huán)境的源代碼彻犁。
就像開頭提到的文章中所指出,即使 14 億臺(tái)設(shè)備中的 0.7% 也是逼進(jìn)一千萬臺(tái)可觀的數(shù)字凰慈,如果不想放棄這樣的商機(jī)汞幢,在砸了自己的招牌之前,還是乖乖地每一個(gè)版本確實(shí)地做好測(cè)試微谓,否則寧可不要輕易地下調(diào) minSdkVersion森篷。