最近在做 Flutter 專案,當我在 build release 版本的 Android 時,遇到一些瓶頸。以下是我遇到的問題以及如何解決的過程。

問題描述

當我使用 flutter build apk --release 建置專案時,出現了很多錯誤

主要問題有以下兩類:

第一類:SDK 與 AGP 版本問題

  • SDK 版本不匹配:某些套件需要 Android SDK 35,但我的配置是 34。
  • 當我升級 SDK 為 35 後,又出現 AGP 版本必須 ≥ 8.1.0 的問題
  • 我透過 Android Studio 的 AGP Upgrade Assistant 來嘗試更新 AGP,卻沒看到 8.0.0 以上的版本

第二類:build release 會缺少一些必要 class

  • 缺少必要的類:BouncyCastle 和 FindBugs 註解類缺失,導致建置失敗。

解決方法

為了解決這些問題,我做了以下幾步:

  1. 我發現 Android Studio 因為版本過舊,所以才找不到 AGP8.0.0 以上的版本可以安裝,因此至官方下載最新版本 Android Studio 後,就能透過 AGP Upgrade Assistant 來幫你更新最新的 AGP version。
  2. AGP 更新為 8.1.0 後,將 build.gradle 中的 compileSdktargetSdkVersion 設置為 35,以滿足套件的需求。
  3. 添加 BouncyCastle 和 FindBugs 的依賴,以確保所有必要的類都存在。(下方詳細說明)
  4. proguard-rules.pro 中添加規則,保留這些類,避免被 ProGuard 移除。(下方詳細說明)

透過 Android Studio 查看專案版本

  1. 在專案中對 android 右鍵,選擇 Open in Android Studio

  2. Android Studio 首次打開專案需要一點時間等他跑完,你可以先去滑個手機等等他

  3. 打開 File → Project Structure 就能看到相關版本資訊 SDK, AGP, Gradle plugin version 等等

添加 BouncyCastle 和 FindBugs 的依賴

(以下步驟為如果在 build release 版本時有遇到關於 BouncyCastle 的問題才需要做。)

android/app/build.gradle 中 dependencies 的部分要加入以下依賴

1
2
3
4
5
6
dependencies {
implementation platform('com.google.firebase:firebase-bom:33.12.0')
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
implementation 'com.google.code.findbugs:jsr305:3.0.2'
}

稍微解釋一下這幾個依賴的用途

BouncyCastle 相關依賴:

1
2
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
  • BouncyCastle 是一個加密庫
  • bcprov  提供核心加密功能
  • bcpkix  提供 PKI/X.509 相關功能,
  • 這些是必須的,因為 MSAL 認證和 JWT 處理都需要這些加密功能

FindBugs 依賴:

1
implementation 'com.google.code.findbugs:jsr305:3.0.2'
  • 提供  @NonNull、@Nullable  等註解
  • MSAL 庫使用這些註解來標記參數的可空性
  • 這個也是必須的,因為 MSAL 的程式碼依賴這些註解

proguard-rules.pro 添加規則

關於 proguard-rules.pro,這個文件是 Android 專案的 ProGuard 配置文件,在 build release 版本時非常重要,特別是當我們啟用程式碼混淆(minification)的功能。

android/app/build.gradle 中,我們有寫到

1
2
3
4
5
6
7
8
9
10
11
buildTypes {
release {
signingConfig signingConfigs.release
// 使用程式碼混淆
minifyEnabled true
// 刪除未使用資源
shrinkResources true
// 添加proguard規則
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

從裡面可以看到,我們啟用 minifyEnabled 後,ProGuard 會幫我們刪除未使用的程式碼並進行混淆,用處是讓打包後的 APK 變得更小以及更安全,但這麼做有個缺點,因為有些類是通過反射或動態載入的,ProGuard 可能會誤判為未使用而移除,所以我們要添加 proguard-rules.pro 來告訴 ProGuard 哪些類必須保留,不能被移除或混淆。

android/app 底下加入 proguard-rules.pro

1
2
3
4
5
6
7
8
9
10
11
# Keep BouncyCastle classes
-keep class org.bouncycastle.** { *; }
-dontwarn org.bouncycastle.**

# Keep FindBugs annotations
-keep class edu.umd.cs.findbugs.** { *; }
-dontwarn edu.umd.cs.findbugs.**

# Keep MSAL classes
-keep class com.microsoft.identity.** { *; }
-dontwarn com.microsoft.identity.**

那如果不啓用程式碼混淆是不是就不用加入 proguard-rules.pro?

如果不起用 minifyEnabled,也不添加 proguard-rules.pro 的話,直接執行 flutter build apk --release ,還是會看到一堆可怕的訊息,如下圖:

這是因為,就算不啓用 minifyEnabled 和 shrinkResources,R8 也一樣會在 release 建置中運行,R8 是 ProGuard 的替代品,負責壓縮和優化程式碼。它會自動嘗試移除未使用的類和方法。

所以就算不起用 minifyEnabled,程式碼也是會受到 R8 自動刪除的影響,因此加入 proguard-rules.pro 是必要的

加入後,執行 flutter build apk —release 就成功嘍!

結果

經過這些修改後,release build 成功了!APK 大小減少,安全性提高,所有功能正常運行。

心得

這次經驗讓我學到,release build 需要特別注意依賴和 ProGuard 規則的配置,這樣才能避免不必要的問題。