針對不同 API 級別建立多個 APK

如果您將應用程式發布到 Google Play,應建構並上傳 Android App Bundle。這樣一來,Google Play 會為每位使用者的裝置設定自動產生並提供最佳化 APK,使用者就能僅下載執行應用程式所需的程式碼和資源。如果您未將應用程式發布到 Google Play,發布多個 APK 是非常實用的做法,但請注意您必須自行建構、簽署及管理各個 APK。

當您開發 Android 應用程式以利用 Google Play 上的多個 APK 時,請務必從一開始就採用一些最佳做法,避免在開發過程中出現不必要的頭痛。本課程將說明如何為應用程式建立多個 APK,每個 APK 涵蓋的 API 級別略有不同。此外,您也會取得一些必要工具,盡可能輕鬆維護多個 APK 程式碼集。

確認需要使用多個 APK

當您嘗試建立可跨越多種 Android 平台版本的應用程式時,您自然會希望應用程式能在不犧牲回溯相容性的情況下,充分利用新裝置的新功能。乍看之下,多個 APK 支援也許是最好的解決方案,但通常並非如此。多個 APK 開發人員指南的「使用單一 APK 替代」一節包含一些實用資訊,可協助您瞭解如何透過單一 APK 提升使用體驗,包括使用我們的支援資料庫。您也可以瞭解如何在單一 APK 中編寫只會在特定 API 級別執行的程式碼,而不必採用 這篇文章中的反射成本等昂貴的技術。

如果您可以管理,將應用程式限制為單一 APK 有幾項優點,包括:

  • 發布與測試更容易
  • 只有一個程式碼集需要維護
  • 您的應用程式可根據裝置設定變更進行調整
  • 在所有裝置上還原應用程式的功能皆可正常運作
  • 您不必擔心市場偏好、從一個 APK 的「升級」到下一個 APK 的行為,以及哪個 APK 會與哪個裝置類別有關

本課程的其餘部分假設您已研究過主題,工作室確實吸收連結資源中的素材,並判定多個 APK 是應用程式的正確路徑。

繪製資格條件圖表

您可以先建立簡單的圖表,以便快速判斷您需要多少 APK,以及每個 APK 包含的 API 範圍。Android 開發人員網站的「平台版本」頁面提供搭載特定 Android 平台版本的使用中裝置相對數量資料,供您參考。此外,雖然一開始聽起來很容易,但要追蹤每個 APK 要指定的 API 級別組合並不容易,特別是在可能會重疊時 (通常是發生重疊) 的情況。幸好,您可以輕鬆快速地規劃需求,以便日後參考。

如要建立多個 APK 圖表,請從一列儲存格開始代表 Android 平台的各種 API 級別。在結尾處擲回一個額外儲存格,代表未來的 Android 版本。

3 4 5 6 7 8 9 10 11 12 13 +

現在只需為圖表中設定顏色,讓每種顏色代表一個 APK。以下舉例說明如何將每個 APK 套用至特定範圍的 API 級別。

3 4 5 6 7 8 9 10 11 12 13 +

圖表建立完成後,即可發布給團隊成員。比起詢問「API 級別 3 到 6 的 APK 如何」這類的問題,您知道的是 Android 1.x,不用再詢問「How’s the APK for the APK for API Level 3 to 6」(API 級別 3 到 6 的 APK) ,這樣團隊的團隊通訊功能就能更為簡單。怎麼會這樣?」只要說「藍色 APK 的推出方式為何?」

將所有通用程式碼和資源放在程式庫專案中

無論是修改現有的 Android 應用程式,還是從頭啟動應用程式,都必須執行程式碼集的首要作業,這也是最重要的。程式庫專案中的所有內容都只需更新一次 (例如本地化字串、色彩主題、修正共用程式碼中的錯誤),這有助於改善開發時間,並減少容易避免的錯誤。

注意:雖然本課程範圍並不涵蓋建立及納入程式庫專案的實作詳細資料,但如果想快速掌握相關知識,請參閱「建立 Android 程式庫」。

如果您要將現有應用程式轉換成使用多個 APK 支援功能,請為每個本地化字串檔案搜尋程式碼集、值清單、主題顏色、選單圖示和版面配置,但這些內容不會因為 APK 不同而改變,然後把這些內容放到程式庫專案中。不會大幅變動的程式碼 也必須納入程式庫專案您可能會發現自己擴充這些類別,從而在 APK 中新增一或兩個方法。

不過,如果是從頭開始建立應用程式,請「先」盡可能在程式庫專案中編寫程式碼,然後再視需要將其移至個別 APK。長期下來,比起將 blob 新增到程式庫區段,在初期測試更容易管理的做法,數個月後開始嘗試判斷這個 blob 是否可移往程式庫區段,而不必向上捲動。

建立新的 APK 專案

您要發布的每個 APK 都應該有獨立的 Android 專案。為方便整理,請將程式庫專案和所有相關 APK 專案放在同一個上層資料夾下。此外請注意,每個 APK 都必須使用相同的套件名稱,但不一定要與程式庫共用套件名稱。如果按照上述配置建立 3 個 APK,根目錄可能會如下所示:

alexlucas:~/code/multi-apks-root$ ls
foo-blue
foo-green
foo-lib
foo-red

建立專案後,請將程式庫專案做為對每個 APK 專案的參照。如果可行,請在程式庫專案中定義起始活動,然後在 APK 專案中擴充該活動。在程式庫專案中定義的起始活動可以把所有應用程式初始化作業放在同一個位置,這樣每個 APK 就不必重新實作「通用」工作,例如初始化 Analytics (分析)、執行授權檢查,以及其他不會大幅從 APK 變更為 APK 的任何其他初始化程序。

調整資訊清單

如果使用者透過 Google Play 下載使用多個 APK 的應用程式,系統會透過兩個簡單規則選擇要使用的正確 APK:

  • 資訊清單必須表明特定 APK 符合資格
  • 在符合資格的 APK 中,最高版本號碼勝出

舉例來說,我們以先前所述的多個 APK 組合,假設我們尚未為任何 APK 設定最高 API 級別。我們個別來看,每個 APK 的可能範圍如下所示:

3 4 5 6 7 8 9 10 11 12 13 +
3 4 5 6 7 8 9 10 11 12 13 +
3 4 5 6 7 8 9 10 11 12 13 +

依規定, minSdkVersion 版本的 APK 也必須有更高的版本代碼,因此若版本代碼的值為紅色 ≥ 綠色 ≥ 藍色,就表示該版本。因此,我們可以有效收合圖表,如下所示:

3 4 5 6 7 8 9 10 11 12 13 +

現在,讓我們進一步假設該紅色 APK 有某些要求,但是另外兩個 APK 無法這麼做。 Android 開發人員指南的「Google Play 篩選器」頁面提供完整的可能問題清單。例如,我們假設紅色需要使用前置鏡頭。事實上,紅色 APK 的全部重點,就是將前置鏡頭與 API 11 中新增的甜美新功能結合。不過,並不是所有支援 API 11 的裝置甚至具備前置鏡頭!真可怕!

幸好,當使用者透過這類裝置瀏覽 Google Play 時,Google Play 會查看資訊清單,系統會依照紅色列出前置鏡頭,並靜默忽略 (經判定紅色和該裝置並非數位天堂) 的要求。然後發現綠色不僅與具備 API 11 的裝置具有前瞻相容性 (因為尚未定義 maxSdkVersion),而且也不會考量是否有前置鏡頭!使用者仍可從 Google Play 下載應用程式,因為即使整個前置鏡頭無法正常運作,系統依然有 APK 支援該特定 API 級別。

如要將所有 APK 保存在獨立的「測試群組」上,請務必採用良好的版本代碼配置。請參閱開發人員指南的「版本代碼」區域,即可找到建議採用的代碼。由於一組 APK 範例僅處理 3 種可能維度的其中一種,因此請將每個 APK 除以 1000,將該特定 APK 的前幾位數設定為 minSdkVersion,然後以該數字遞增。看起來可能會像這樣:

藍色:03001、03002、03003、03004...
綠色:07001、07002、07003、07004...
紅色:11001、11002、11003、11004...

總而言之,Android 資訊清單看起來會像這樣:

藍色:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="03001" android:versionName="1.0" package="com.example.foo">
    <uses-sdk android:minSdkVersion="3" />
    ...

綠色:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="07001" android:versionName="1.0" package="com.example.foo">
    <uses-sdk android:minSdkVersion="7" />
    ...

紅色:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="11001" android:versionName="1.0" package="com.example.foo">
    <uses-sdk android:minSdkVersion="11" />
    ...

詳閱正式發布前檢查清單

將檔案上傳至 Google Play 前,請仔細檢查下列項目。提醒您,這些內容與多個 APK 明確相關,並未代表所有上傳至 Google Play 的應用程式完整清單。

  • 所有 APK 都必須使用相同的套件名稱
  • 所有 APK 都必須使用相同的憑證簽署
  • 如果平台版本中的 APK 與平台版本重疊,則 minSdkVersion 較高的 APK 必須採用更高的版本代碼
  • 請仔細檢查資訊清單篩選器是否含有衝突的資訊 (如果 APK 僅支援 XLARGE 螢幕的杯子蛋糕,就不會看到這個 APK)
  • 每個 APK 的資訊清單必須在至少一個支援的畫面、OpenGL 紋理或平台版本中皆不得重複
  • 請嘗試在至少一個裝置上測試每個 APK。不過,您在開發機器上可自訂一個市面上最可自訂的裝置模擬器。堅持下去!

另外,在推出應用程式之前先檢查已編譯的 APK,確保 Google Play 不會發生任何意外情況,導致應用程式遭到隱藏。使用「aapt」工具 這個做法其實相當簡單Aapt (Android 資產封裝工具) 是建立及封裝 Android 應用程式的建構程序的一部分,也是檢查應用程式時非常便利的工具。

>aapt dump badging
package: name='com.example.hello' versionCode='1' versionName='1.0'
sdkVersion:'11'
uses-permission:'android.permission.SEND_SMS'
application-label:'Hello'
application-icon-120:'res/drawable-ldpi/icon.png'
application-icon-160:'res/drawable-mdpi/icon.png'
application-icon-240:'res/drawable-hdpi/icon.png'
application: label='Hello' icon='res/drawable-mdpi/icon.png'
launchable-activity: name='com.example.hello.HelloActivity'  label='Hello' icon=''
uses-feature:'android.hardware.telephony'
uses-feature:'android.hardware.touchscreen'
main
supports-screens: 'small' 'normal' 'large' 'xlarge'
supports-any-density: 'true'
locales: '--_--'
densities: '120' '160' '240'

檢查 aapt 輸出內容時,請務必確認支援畫面和相容畫面的值沒有衝突,且您沒有因您在資訊清單中設定的權限而新增的非預期「uses-feature」值。在上述範例中,很多裝置都看不到 APK。

原因是什麼?藉由新增必要權限 SEND_SMS,系統已默示新增 android.hardware.telephony 的功能需求。由於 API 11 是 Honeycomb (專為平板電腦最佳化的 Android 版本),且沒有任何 Honeycomb 裝置內建電話硬體,因此 Google Play 一律會篩除這個 APK,直到日後的裝置在 API 級別獲得較高級別「且」擁有電話硬體為止。

不過,只要在資訊清單中新增以下內容,即可輕鬆修正這項問題:

<uses-feature android:name="android.hardware.telephony" android:required="false" />

android.hardware.touchscreen 要求也會以隱含方式加入。如要讓 APK 顯示在非觸控螢幕裝置的電視上,請在資訊清單中新增以下內容:

<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

完成正式發布前檢查清單後,請將 APK 上傳到 Google Play。瀏覽 Google Play 時,應用程式可能需要一點時間才會顯示,如果是之後,請執行最後檢查。將應用程式下載到所有測試裝置上,確保 APK 的指定目標裝置正確無誤。恭喜,大功告成!