2016/7/17

解析 Android 開發時的 SDK 版本參數設定

在開發 Android App 時,開發人員最容易出現困惑的地方之一應該是 compileSdkVersion、minSdkVersion 和 targetSdkVersion 這些參數的用途。對此,有 Google 內部的 Android 開發人員就這個議題寫了一篇文章:Picking your compileSdkVersion, minSdkVersion, and targetSdkVersion。在這裡會用不同的角度來說明這些參數的用途,希望能對大家在應用上有所幫助。


buildToolsVersion

首先,這是在那篇文章中沒有說明到的另一個與版本有關的參數。這個參數主要是用來指定用哪一組編譯程式來將你所撰寫的 Source Code 轉成作業系統可以載入的形式,也就是產出 Apk。所以 Build Tools 在概念上可以看成是 Android Studio 的一部份或是其中一個套件,只是同時會有好幾組。

譬如說要修理車輛,每一組的 Build Tools 就像是有人把修車的工具,扳手、起子、套筒之類的放在一個包包裡。只是隨著工具設計的翻新,包包裡的工具可能會做調整,像是起子原本是各尺寸一隻,現在可能改成只有一隻但可以換各種尺寸的頭。同樣是拿來修車,工具也一樣,但是為了區別內容物的不同所以給了不同的編號。

選用哪一組 Build Tools 原則上不會有太大的差異,大概就是編譯的效能、產出的檔案大小等等的細節。但有時候還是可能會牽涉到 Apk  內容或結構的問題影響執行,仍然要多注意改版的資訊,最保險的做法還是讓 buildToolsVersion 的主版本編號與 compileSdkVersion 同步。


compileSdkVersion

compileSdkVersion 則是 Build Tools 在進行編譯時,檢查程式碼在呼叫 SDK 所使用的規格是否正確的基準,但所指定的 SDK 並不會被編入 Apk 中。Google 會在發行新版系統時對 SDK 做調整,為了保持相容性,在規格上不太可能採取激進的方式,例如:同樣名稱的函式改為不同的參數規格。最常出現的大概就是在函式上標註 @Deprecated,等系統發行過幾個版本之後再正式廢止。

buildToolsVersion 及 compileSdkVersion 顧名思義就是在編譯、建置時期所使用的工具,這句話的重點是編譯過了、順利產出 Apk,不代表執行上就沒有問題,而且所謂的執行還要細分是在哪個版本上執行。因為 SDK 不是包含在 Apk 裡,是包含在 Android 的系統之中。Apk 在哪一個版本的 Android 中執行,呼叫的就是對應版本的 SDK。所以執行時呼叫會不會出錯,跟編譯會不會成功是沒有直接的關連,除非執行的環境和 compileSdkVersion 指定的版本相同,但也僅限於規格的部份。

用旅遊來做例子,Build Tools 就好像旅行社,把你對旅遊的構想轉成實際的行程。而 Compile SDK 則是旅行社在行前檢查所使用的檢查表,用來確認在行前是不是有什麼細節遺漏了。選用哪一個旅行社對於規劃行程沒有什麼太大的差別,頂多只是能夠提供服務的多寡或細緻的程度。而行前檢查表如果用錯了版本就有可能會出問題,像是要去的目的地簽證條件改變了,舊的版本沒有列上去,在行前檢查時一切沒問題,到了當地才發現沒有辦法入境,於是整個行程就報銷了。但如果是用新的檢查表,就可以在行前發現問題,並且做出補救的措施,確保行程能夠如計畫進行。

由於 Google Play 只限制 App 可執行的 SDK 最低版本,並沒有限定最高的版本。也就是,使用者可以在硬體廠商有提供更新的情況下不斷地昇級系統版本,而 App 則會一直保持在可安裝的狀態下。如果一直不更動 compileSdkVersion,則有一些變動在編譯時期不會被發現,前段提到的標註 @Deprecated 就是一例。前幾次的系統版本更新在執行 App 上可能不會有問題,但是一旦真的有函式被廢止了,則要到執行時才能發現,這時要偵錯就不是件容易的事。但如果是使用新的 compileSdkVersion,則是在編譯時就會有對應的訊息供作參考。

所以在開頭提到的文章中有一個很重要的關鍵點是:當編譯的結果還存有警告時,一定要進行對應的處理,這些警告的設計是有其作用的。


targetSdkVersion

在推出新的 Android SDK 版本有函式的行為新舊版本不一致時,為保持相容性,舊的行為會被保留,並且會以 targetSdkVersion 的值來判斷要執行哪一種行為。以開頭提到的文章中舉例的 AlarmManager#set 來說,依官方文件的說明,Android 4.4 (API 19)以後使用的是不精確的時間,以減少設備被喚醒的次數及降低電量的耗損。官方文件中的另一段註解文字說到:targetSdkVersion 小於 API 19 的會繼續之前舊的行為,也就是 SDK 中會判斷 targetSdkVersion 的資訊來決定執行的程式碼內容。

以前段提到旅遊的例子來說,當實際出發到了當地的海關,targetSdkVersion 的規則就如同海關櫃檯人員不是一體套用新的簽證規則,而是會好心地依照填寫的申請日期決定適用的簽證規則。如果填的是新規則公布之後的日子,代表填寫的人應該知道新的簽證規則,所以當然就照新的簽證規則來審核。相對地,填寫的日期在新規則公布之前的日子,則會使用舊的簽證規則來審核。

Android 透過這種方式讓 App 在新的系統版本中不會有預期之外的行為出現,即便 Google 打算在新版本中改變原本的行為。所以 targetSdkVersion 主要的用途是讓開發者告知 SDK,開發者所認知的函式行為應該是哪一種,如果 targetSdkVersion 是設定成改變之前的值,則 SDK 會執行舊的版本,否則就執行新的。對開發者來說,修改 targetSdkVersion 後代表的意義是:開發者已經知道了新的 SDK 行為,並且確認這個行為是 App 所需要的。所謂的確認通常指的是要在參數指定之對應版本的執行環境中做過測試,而不光只是編譯成功。

依照以上的說明,如果在更換了 targetSdkVersion 內容,卻還是使用萬年的模擬器設定來進行測試,以致模擬器上系統的版本一直沒換,這時就有必要重新檢討開發時的測試策略了。


minSdkVersion

最直接的用途就是要求 Google Play 設定 App 可被安裝的系統版本最底限,在這個版本之後的則不會被限制。

對開發者來說有另外一個要觀注的重點和 targetSdkVersion 一樣,minSdkVersion 也具有相同的意義。試想有一個 App 是在 Android 4.4 (API 19)開發的,預期的是 AlarmManager#set 的新行為,但是當 minSdkVersion 的值比 API 19 小時,程式運作在 API 18 以下的環境中一定沒有 AlarmManager#set 的新行為,這時 App 會產生什麼結果?

如果是上架很久的 App,程式碼應該也是對應著系統版本演進的歷程做修改。所以程式碼中或多或少都有包含先前所提到和 SDK 類似用系統版本來判斷程式要執行的新、舊邏輯區塊,以適應各種不同版本的系統。在這種情況之下,minSdkVersion 理所當然的可以是很早期的版本數值。

如果是新開發的 App,因為沒有經歷過這些過程,所以 targetSdkVersion 和 minSdkVersion 在開發初始的階段裡意義上是一樣的,這時對 minSdkVersion 調整最保守的做法應該是逐次逐版地往前調。當 minSdkVersion 的數值逐版向前調整時,也應逐版在對應的環境中做過測試,確保程式在舊行為下所呈現的結果也符合預期,並且在必要時要加上對新舊版本的判斷式、補上合於舊版環境的程式碼。

就像開頭提到的文章中所指出,即使 14 億台設備中的 0.7% 也是逼進一千萬台可觀的數字,如果不想放棄這樣的商機,在砸了自己的招牌之前,還是乖乖地每一個版本確實地做好測試,否則寧可不要輕易地下調 minSdkVersion。







0 意見:

張貼留言