縮減、模糊處理及最佳化應用程式

如果想盡可能縮小應用程式的大小和執行速度,建議您對應用程式進行最佳化調整及壓縮 透過 isMinifyEnabled = true 取得發布子版本。

這樣做會啟用「縮減」,藉此移除未使用的程式碼和資源。 模糊處理:縮短應用程式類別和成員的名稱。和 最佳化,套用更積極的策略來進一步降低 以及提升應用程式效能本頁說明 R8 如何替專案執行這些編譯時間工作,以及如何自訂 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件

使用 GCP 建構專案時 Android Gradle 外掛程式 3.4.0 以上版本, 外掛程式不再使用 ProGuard 執行編譯時間程式碼最佳化作業。 不過,外掛程式會與 R8 編譯器搭配運作,以處理下列作業 編譯時間的工作:

  • 程式碼縮減 (或搖樹):偵測並安全地移除未使用的 應用程式及其程式庫中的類別、欄位、方法和屬性 (因此很適合用來處理 64,000 份參照上限)。例如,如果您只使用程式庫依附元件中的幾個 API,則縮減可以識別應用程式使用的程式庫程式碼,並僅從應用程式中刪除該程式碼。詳情請參閱如何縮減程式碼
  • 資源縮減:從封裝應用程式中移除未使用的資源。 在應用程式的程式庫依附元件中納入未使用的資源。使用中 再與程式碼縮減 這樣未使用的程式碼就會移除 任何不再參照的資源都可以安全移除。詳情請參閱如何縮減資源一節。
  • 最佳化:檢查及重新編寫程式碼,改善執行階段 並進一步縮減應用程式的 DEX 檔案大小。這個 提升高達 30% 程式碼的執行階段效能,大幅改善 以及影格時間例如,如果 R8 偵測到特定 if/else 陳述式的 else {} 分支從未被擷取,R8 就會移除 else {} 分支版本的程式碼。詳情請參閱程式碼最佳化一節。
  • 模糊處理 (或 ID 壓縮):縮短類別名稱 進而縮減 DEX 檔案大小。詳情請造訪 ,瞭解如何對程式碼進行模糊處理

建構應用程式的發布版本時,您可以將 R8 設定為執行 執行上述的編譯時間工作你也可以停用 工作,或是透過 ProGuard 規則檔案自訂 R8 的行為。 事實上,R8 適用於所有現有的 ProGuard 規則檔案,因此更新 Android Gradle 外掛程式並不需要變更現有規則。

啟用縮減、模糊處理和最佳化功能

使用 Android Studio 3.4 或 Android Gradle 外掛程式 3.4.0 以上版本時,R8 會是 將專案的 Java 位元碼轉換為 DEX 的預設編譯器 Android 平台專用的格式不過,建立新的專案時 縮減、模糊化和程式碼最佳化等功能都無法達到此效果 預設啟用的功能這是因為這些編譯時間的最佳化機制能提高 建構時間,如果不夠充分,可能會發生錯誤 自訂要保留的程式碼

因此,建議您在建構最終的應用程式版本時,啟用這些編譯時間工作,再進行發布。如要啟用縮減、模糊處理 並在專案層級建構指令碼中加入以下內容。

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}

Groovy

android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

R8 設定檔

R8 會使用 ProGuard 規則檔案修改預設行為,並進一步瞭解應用程式的結構,例如做為應用程式程式碼進入點的類別。雖然您可以修改部分規則檔案,但部分規則可能是由編譯時間工具 (例如 AAPT2) 或從應用程式的程式庫依附元件沿用而來。下表說明 R8 使用的 ProGuard 規則檔案來源。

來源 位置 說明
Android Studio <module-dir>/proguard-rules.pro 使用 Android Studio 建立新模組時,IDE 會在該模組的根目錄中建立 proguard-rules.pro 檔案。

根據預設,這個檔案不會套用任何規則。因此,請在此處加入自己的 ProGuard 規則,例如自訂保留規則

Android Gradle 外掛程式 在編譯期間由 Android Gradle 外掛程式產生。 Android Gradle 外掛程式會產生 proguard-android-optimize.txt,其中包含大多數 Android 專案都適用的規則,並啟用 @Keep* 註解

根據預設,使用 Android Studio 建立新模組時,模組層級 建構指令碼會在發布子版本中加入這個規則檔案 不必確保憑證管理是否適當 因為 Google Cloud 會為您管理安全性

注意:Android Gradle 外掛程式包含額外的預先定義的 ProGuard 規則檔案,但建議使用 proguard-android-optimize.txt

資料庫依附元件

在 AAR 程式庫中:
proguard.txt

在 JAR 程式庫中:
META-INF/proguard/<ProGuard-rules-file>

除了這些位置之外,Android Gradle 外掛程式 3.6 以上版本也 支援指定縮減規則

如果 AAR 或 JAR 程式庫是以自己的規則檔案發布,而 只要將該程式庫納入編譯時間依附元件,R8 就會自動 並在編譯專案時套用這些規則

除了傳統的 ProGuard 規則外,Android Gradle 外掛程式 3.6 以上版本也支援 指定縮減規則。這些是規則 指定特定縮減器 (R8 或 ProGuard) 及 縮減器版本

如果確定,使用與程式庫一起封裝的規則檔案 所有規則都必須符合這些規則,程式庫才能正常運作 開發人員已為您進行故障排除步驟。

但請注意,由於規則屬於附加性質, 程式庫依附元件包含的特定規則無法移除 可能會影響應用程式其他部分的編譯結果。舉例來說 程式庫內含停用程式碼最佳化功能的規則,該規則會停用 以獲得最佳專案效益

Android 資產封裝工具 2 (AAPT2) 使用 minifyEnabled true 建立專案後:<module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt AAPT2 會根據應用程式資訊清單、版面配置和其他應用程式資源中的類別,產生保留規則。例如,AAPT2 會為您在應用程式資訊清單中註冊的每個活動建立資料進入點。
自訂設定檔 根據預設,當您使用 Android Studio 建立新模組時,IDE 會建立 <module-dir>/proguard-rules.pro 以新增您自己的規則。 您可以新增其他設定,R8 會在編譯期間套用。

minifyEnabled 屬性設為 true 時,R8 會結合上述所有可用來源的規則。請務必記住 使用 R8 進行疑難排解,因為其他編譯時間依附元件。 應用程式 (例如程式庫依附元件) 可能會變更 R8 行為 所學

如要輸出 R8 建構您的專案適用的所有規則,請在模組的 proguard-rules.pro 檔案中加入下列內容:

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

指定縮減規則

Android Gradle 外掛程式 3.6 以上版本支援程式庫 特定縮減器 (R8 或 ProGuard),以及特定縮減器版本。這個 讓程式庫開發人員能夠自訂規則,在專案中發揮最佳效能 ,同時允許現有規則 在具有舊版縮減器版本的專案中使用。

如要指定指定目標縮減規則,程式庫開發人員需要納入這些規則 放在 AAR 或 JAR 程式庫內的特定位置,如下所述。

In an AAR library:
    proguard.txt (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

也就是說,指定的縮減規則會儲存在 META-INF/com.android.tools 中 ,或是位於META-INF/com.android.tools 自動套用最佳化建議的 classes.jar

該目錄之下可以多個名稱形式的目錄, r8-from-<X>-upto-<Y>proguard-from-<X>-upto-<Y>,以表示哪些 要編寫目錄中規則的縮減版本。 請注意,-from-<X>-upto-<Y> 部分為選用部分,<Y> 版本 為專屬值,且版本範圍必須是連續的。

例如 r8-upto-8.0.0r8-from-8.0.0-upto-8.2.0r8-from-8.2.0 形成一組有效的指定縮減規則。「規則」中的規則 自 8.0.0 版起,R8 會使用 r8-from-8.0.0-upto-8.2.0 目錄 但「不包含」8.2.0 版。

根據這項資訊,Android Gradle 外掛程式 3.6 以上版本將選取 相符 R8 目錄中的規則如果程式庫未指定 縮減規則,Android Gradle 外掛程式就會從舊版 Cloud Build 地點 (採用自動套用建議功能須proguard.txt,或是 META-INF/proguard/<ProGuard-rules-file> 用於 JAR)。

程式庫開發人員可以選擇納入指定縮減規則或舊版 程式庫中的 ProGuard 規則;如要維護規則,則同時採用這兩種規則 與 Android Gradle 外掛程式 3.6 以下版本或其他工具相容。

新增其他設定

使用 Android Studio 建立新專案或模組時,IDE 會建立 <module-dir>/proguard-rules.pro 檔案,以納入自己的規則。個人中心 您也可以把其他檔案的額外規則新增到 proguardFiles 屬性。

例如,您可以在對應的 productFlavor 區塊中加入另一個 proguardFiles 屬性,以新增特定於每個建構變化版本的規則。下列 Gradle 檔案會新增 flavor2-rules.proflavor2 變種版本。現在 flavor2 會使用三個 ProGuard 規則,因為系統會套用 release 區塊中的規則。

此外,您還可以新增 testProguardFiles 屬性,指定 只有測試 APK 中包含的 ProGuard 檔案清單:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

縮減程式碼

設定 minifyEnabled 時,系統預設會啟用使用 R8 的程式碼縮減 設為 true

程式碼縮減 (也稱為搖樹) 是指移除執行階段不需要 R8 判定的程式碼的程序。例如,如果您的應用程式含有許多程式庫依附元件,但僅使用了一小部分功能,那麼這項程序可以大幅縮減應用程式的大小。

如要縮減應用程式的程式碼,R8 會先判定應用程式的所有進入點 程式碼整合設定檔。 這些進入點包括 Android 平台用於開啟應用程式活動或服務的所有類別。從每個進入點開始,R8 會檢查 應用程式的程式碼,建立所有方法、成員變數和其他項目的圖表 應用程式可能會在執行階段存取的類別。未連接至該圖表的程式碼會被視為無法連上,且可能會從應用程式中移除。

圖 1 顯示執行階段程式庫依附元件的應用程式。在檢查應用程式的程式碼時,R8 判定可從 MainActivity.class 進入點連線至 foo()faz()bar() 方法。不過,應用程式從未在執行階段使用 OkayApi.class 類別或 baz() 方法,而且在縮減應用程式時,R8 則會移除該程式碼。

圖 1. 在編譯期間,R8 會建構 根據專案的合併保留規則,判斷無法存取的程式碼。

R8 會在專案的 R8 設定檔中,透過 -keep 規則決定進入點。也就是說,保留規則指定 R8 在縮減應用程式時不應捨棄的類別,而 R8 也會將 盡可能將這些類別放在應用程式中Android Gradle 外掛程式 AAPT2 會自動產生大多數應用程式所需的保留規則 專案,例如應用程式的活動、檢視和服務。不過,如果您需要用額外的保留規則來自訂此預設行為,請參閱自訂要保留的程式碼一節。

如果您只是想縮減應用程式資源的大小 跳至如何縮減資源一節。

請注意,如果程式庫專案遭到縮減,那麼應用程式依附於該程式庫 包括縮減程式庫類別在下列情況中,您可能需要調整程式庫保留規則: 資料庫 APK 中缺少類別。分為多個平台 AAR 格式的程式庫,程式庫依附的本機 JAR 檔案 縮減。

自訂要保留的程式碼

在大多數情況下,預設的 ProGuard 規則檔案 (proguard-android-optimize.txt) R8 就夠了,只要移除未使用的程式碼即可。不過 R8 有時難以正確分析,而且可能會移除 應用程式實際需要的程式碼以下列舉一些錯誤移除程式碼的範例:

  • 應用程式從 Java Native Interface (JNI) 呼叫方法時
  • 應用程式在執行階段查詢程式碼 (例如使用反射)

測試應用程式應能找出任何因不當移除而造成的錯誤 但您也可以檢查已移除哪些程式碼 產生已移除的程式碼報表

如要修正錯誤並強制 R8 保留特定程式碼,請在 ProGuard 規則檔案中加入 -keep 程式碼行。例如:

-keep public class MyClass

或者,您也可以在想保留的程式碼中加入 @Keep 註解。在類別中新增 @Keep 後,整個類別維持不變。 在方法或欄位中新增屬性時,方法/欄位 (及其名稱) 也會一併保留 保持不變請注意,只有使用 AndroidX 註解程式庫,且加入已隨附於 Android Gradle 外掛程式的 ProGuard 規則檔案時,您才能使用該註解,例如相關說明請參閱啟用縮減功能一節。

使用 -keep 選項時,您需要考量許多因素;如要進一步瞭解如何自訂規則檔案,請參閱 ProGuard 手冊疑難排解 這一節概述了您的程式碼取得 移除。

刪除原生資料庫

根據預設,系統會在應用程式的發布子版本中去除原生程式碼庫。 這項移除作業包括移除符號表和偵錯資訊 包含在應用程式所使用的任何原生資料庫中。去除原生程式碼 程式庫可大幅節省大小;但是要診斷出來 Google Play 管理中心因缺少資訊 (例如 類別和函式名稱)。

原生程式碼支援

Google Play 管理中心會報告 Android Vitals 下方的原生程式碼發生錯誤事件。只有幾個會 步驟,即可為應用程式產生並上傳原生偵錯符號檔案。 這個檔案支援符號化的原生當機堆疊追蹤,包括類別和 函式名稱),協助您在實際工作環境中對應用程式進行偵錯。 這些步驟取決於專案中使用的 Android Gradle 外掛程式版本,以及專案的建構輸出內容。

Android Gradle 外掛程式 4.1 以上版本

如果您在專案中建構 Android App Bundle,可以自動加入原生偵錯符號檔。如要在發布版本中加入這個檔案,請將以下內容新增至應用程式的 build.gradle.kts 檔案:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

請從下列清單中選取偵錯符號級別:

  • 使用 SYMBOL_TABLE 取得 Play 管理中心符號化堆疊追蹤中的函式名稱。 這個級別支援「空值標記」
  • 使用 FULL 在 Play 管理中心的符號化堆疊追蹤中取得函式名稱、檔案和行數。
,瞭解如何調查及移除這項存取權。

如果您的專案會建構 APK,請使用畫面上顯示的 build.gradle.kts 建構設定 ,分別產生原生偵錯符號檔案。手動將原生偵錯符號檔案上傳至 Google Play 管理中心。在建構程序中,Android Gradle 外掛程式會在下列專案位置輸出這個檔案:

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

Android Gradle 外掛程式 4.0 以下版本 (以及其他建構系統)

在建構程序中,Android Gradle 外掛程式會在專案目錄中保留未移除的程式庫副本。這個目錄結構與下列內容相似:

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. 壓縮以下目錄的內容:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. 手動上傳 symbols.zip 檔案至 Google Play 管理中心。

縮減資源

資源縮減功能僅適用於程式碼縮減。在 程式碼縮減器會移除所有未使用的程式碼,資源縮減器就能找出 應用程式仍會使用的資源如果新增程式碼 包含資源的程式庫,您必須移除未使用的程式庫程式碼, 程式庫資源沒有參照,因此可由資源移除 縮減。

如要啟用資源縮減功能,請設定 shrinkResources 屬性 將容器新增至建構指令碼中的 true (以及 minifyEnabled 用於程式碼縮減)。例如:

Kotlin

android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

Groovy

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

如果您尚未使用 minifyEnabled 建構應用程式, 請先嘗試程式碼縮減功能,再啟用 shrinkResources 因為您可能需要編輯 proguard-rules.pro 檔案 會保留先前以動態方式建立或叫用的類別或方法 即可開始移除資源

自訂要保留的資源

如要保留或捨棄特定資源,請在專案中使用建立帶有 <resources> 標記的 XML 檔案,並在 tools:keep 屬性中指定要保留的每個資源,並在 tools:discard 屬性中指定要捨棄的每個資源。這兩個屬性都接受以半形逗號分隔的資源名稱清單。您可以使用星號字元做為 萬用字元

例如:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

請將這個檔案儲存在專案資源中,例如在 res/raw/my.package.keep.xml 中。建構作業不會將這個檔案封裝至應用程式中。

注意:請務必為 keep 檔案指定專屬名稱,時間 分別連結不同的程式庫和保留規則 否則可能會導致已忽略規則或不需要保留的問題 再複習一下,機構節點 是所有 Google Cloud Platform 資源的根節點

指定應捨棄的資源似乎毫無用處 但這在建構變數時很實用。適用對象 例如將所有資源放入通用專案目錄 則為每個項目建立不同的 my.package.build.variant.keep.xml 檔案 如果您知道程式碼中有使用特定資源,就會建構變化版本 (因此不會遭到縮減工具移除),但實際上 指定的建構變數。或者,建構工具也可能是 之所以能夠視需要正確地識別資源 編譯器會以內嵌方式新增資源 ID,這樣資源分析器可能就不會 瞭解真正參照的資源與整數值之間的差異 程式碼中的位置值相同

啟用嚴格參考資料檢查功能

一般而言,資源縮減器能夠準確判斷資源是否被使用。不過,如果您的程式碼會呼叫 Resources.getIdentifier() (或是您的任何程式庫,則使用 AppCompat 程式庫),代表您的程式碼是根據動態產生的字串查詢資源名稱。執行這項操作時,資源縮減器會運作 並依預設,將所有具有相符名稱格式的資源標示為 可能可以使用且無法移除

例如,下列程式碼會讓所有具有 img_ 前置字串的資源標示為使用中。

Kotlin

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

Java

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

資源縮減器也會檢查 程式碼,以及各種 res/raw/ 資源 網址格式範例: file:///android_res/drawable//ic_plus_anim_016.png。如果我們發現這類字串可用來建立這類網址,就不會將其移除。

以下為預設啟用的安全縮減模式。 但是,您可以關閉這項「防患於未然」控制代碼,並且指定資源縮減器僅保留特定資源。方法是在 keep.xml 檔案中將 shrinkMode 設為 strict,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

如果您啟用嚴格縮減模式,且程式碼也會參照 含有動態產生字串的資源 (如上所示),您必須 使用 tools:keep 屬性手動保留這些資源。

移除未使用的替代資源

Gradle 資源縮減器只會移除未參照的資源 您的應用程式程式碼,也就是說應用程式程式碼不會移除 不同裝置設定的替代資源。如有需要 您可以使用 Android Gradle 外掛程式的 resConfigs 屬性進行以下操作: 移除應用程式不需要的額外資源檔案。

例如,如果您使用的程式庫包含語言資源 (例如 AppCompat 或 Google Play 服務),則您的應用程式會包含這些程式庫中訊息的所有翻譯語言字串,無論您的應用程序的其餘部分是否被翻譯成相同的語言。如果您只想保留應用程式官方支援的語言,可以使用 resConfig 屬性指定。系統會移除任何未指定語言的資源。

下列程式碼片段說明如何將語言資源限制為英文和法文:

Kotlin

android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}

Groovy

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

使用 Android App Bundle 格式發布應用程式時 (根據預設,只有這些格式) 應用程式會下載使用者裝置上設定的語言。 同樣地,只有符合裝置的螢幕密度和原生程式的資源 下載時會包含符合裝置 ABI 的程式庫。詳情請參閱 Android App Bundle 設定的相關說明。

如果舊版應用程式使用 APK 發布 (2021 年 8 月前建立),您可以透過建構多個 APK (每個 APK 針對不同的裝置設定),以自訂要包含在 APK 中的螢幕密度或 ABI 資源。

合併重複的資源

根據預設,Gradle 也會合併名稱相同的資源,例如 名稱相同的可繪項目,可能位於不同資源資料夾中。這個 行為不受 shrinkResources 屬性控制,且 無法停用,因為如要避免在多個 含有與程式碼查詢名稱相符的資源。

只有在兩個以上的檔案共用相同的資源名稱、類型和限定詞時,系統才會合併資源。Gradle 會選取要處理的檔案 是重複項目的最佳選擇 (根據描述的優先順序 並且只將一項資源傳送給 AAPT 進行在 最終構件

Gradle 會在下列位置尋找重複的資源:

  • 與主要來源集相關聯的主要資源 位於src/main/res/
  • 子類疊加層,來自建構類型和建構版本。
  • 程式庫專案依附元件。

Gradle 會根據下列優先順序合併重複的資源:

依附元件 → 主要 → 版本版本 → 建構類型

例如,如果主要資源和建構版本出現在重複的資源,Gradle 會選取建構版本中的資源。

如果相同的資源出現在相同的來源集中,Gradle 無法合併 並發出資源合併錯誤如果在 build.gradle.kts 檔案的 sourceSet 屬性中定義了多個來源集,例如 src/main/res/src/main/res2/ 都含有相同的資源,就會發生這種情況。

模糊處理程式碼

模糊處理的目的是縮短應用程式名稱 應用程式的類別、方法和欄位。以下是使用 R8 進行模糊處理的範例:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

雖然模糊處理不會移除應用程式中的程式碼,但大幅縮減應用程式大小 可以顯示在含有索引許多類別、方法和欄位的 DEX 檔案的應用程式中。 不過,由於程式碼會重新命名程式碼的不同部分,因此某些工作 (例如檢查堆疊追蹤) 需要額外工具。為了瞭解您的 請參閱一節,瞭解如何 解碼模糊化的堆疊追蹤

此外,如果您的程式碼必須使用可預測的應用程式方法命名 和類別—例如使用反射方法時 做為進入點,並指定其保留規則,詳細說明請參閱 請參閱這篇文章,瞭解如何自訂要保留的程式碼。這些保留規則會指示 R8 將程式碼保留在應用程式的最終 DEX 中,同時保留原本的命名。

解碼模糊化的堆疊追蹤

在 R8 模糊處理程式碼後,由於類別和方法的名稱可能已變更,因此瞭解堆疊追蹤並不容易。如要取得原始堆疊追蹤, 重新追蹤堆疊追蹤

程式碼最佳化

為了進一步最佳化應用程式,R8 會進一步檢查程式碼 移除未使用的程式碼,或視情況重新編寫程式碼,以 較不冗長以下是這類最佳化的幾個範例:

  • 如果您的程式碼從未取得特定 if/else 陳述式的 else {} 分支版本,R8 可能會移除 else {} 分支版本的程式碼。
  • 如果程式碼只在幾處呼叫方法,R8 可能會移除方法 並內嵌於幾個呼叫網站中
  • 如果 R8 判定類別只有一個專屬子類別,以及該類別 本身並未執行個體化 (例如,僅用於 一個具體的實作類別),則 R8 可以結合這兩個類別 並從應用程式中移除類別
  • 詳情請參閱 Jake Wharton 撰寫的 R8 最佳化網誌文章

R8 不允許停用或啟用離散最佳化,或是修改最佳化的行為。事實上,R8 會忽略所有嘗試 即可修改預設最佳化設定,例如 -optimizations-optimizationpasses。這項限制非常重要,因為 R8 會繼續 但維護標準的標準行為有助 Android Studio 團隊可以輕鬆排解問題,並解決您可能遇到的問題。

請注意,啟用最佳化功能會變更 應用程式。舉例來說,內嵌會移除堆疊框架。詳情請參閱 反向瞭解如何取得原始堆疊追蹤。

對執行階段效能的影響

如果啟用縮減、模糊處理和最佳化功能,R8 就會改善 程式碼的執行階段效能 (包括 UI 執行緒的啟動和影格時間) 最高減少 30%如果停用任一個連結,最佳化組合就會大幅減少 R8 的用途。

若已啟用 R8 建立啟動設定檔 以提升啟動效能

啟用更積極的最佳化功能

R8 包含一組額外的最佳化設定 (稱為「完整模式」), 因此行為與 ProGuard 不同。這些最佳化功能是由 預設自 Android Gradle 外掛程式 8.0.0 版

如要停用這些額外最佳化功能,請在 專案的 gradle.properties 檔案:

android.enableR8.fullMode=false

由於額外的最佳化作業讓 R8 與 ProGuard 的運作方式不同 您可能需要加入額外的 ProGuard 規則,以免執行階段 使用專為 ProGuard 設計的規則時會發生問題。舉例來說 透過 Java Reflection API 參照類別「不」使用時 「完整模式」R8 假設您打算檢查及操控 存取該類別的程式碼 (即使程式碼實際上不存在), 會保留該類別及其靜態初始化器。

然而,使用「完整模式」時,R8 不會假設;如果 R8 斷言程式碼絕對不會在執行階段使用類別,系統就會移除 新增到應用程式最終 DEX 的類別。也就是說,如要保留類別及其靜態初始化器,您必須在規則檔案中加入 Keep 規則,以完成此動作。

如果您在使用 R8 的「完整模式」時遇到任何問題,請參閱 R8 常見問題頁面 瞭解可行的解決方案如果無法解決問題,請報告錯誤

反向堆疊追蹤

R8 處理的程式碼變更了造成堆疊追蹤的各種方式 較難理解,因為堆疊追蹤與原始碼不會完全一致。這個 執行偵錯資訊時, 都不會保存。原因可能出在內嵌和大綱等最佳化做法。 最大的貢獻者就是模糊化,即使是類別和方法 變更名稱

如要復原原始堆疊追蹤,R8 會提供 retrace 指令列工具,也就是 指令列工具套件中的一部分。

如要支援應用程式堆疊追蹤的反向追蹤功能,請務必確認 只要新增下列指令,即可保留足夠的資訊以供回溯使用 新增至模組 proguard-rules.pro 檔案的規則:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

LineNumberTable 屬性會保留位置資訊 以用於讓這些位置顯示在堆疊追蹤中。SourceFile 屬性 即可確保所有潛在執行階段實際輸出位置資訊。 -renamesourcefileattribute 指令會在堆疊中設定來源檔案名稱 追蹤記錄於 SourceFile。原始來源檔案名稱不是 ,因為對應檔案含有原始來源檔案,因此必須在反向修訂時需要。

R8 會在每次執行時建立 mapping.txt 檔案, 包含將堆疊追蹤對應回原始版本所需的資訊 堆疊追蹤Android Studio 會將檔案儲存在 <module-name>/build/outputs/mapping/<build-type>/ 目錄。

在 Google Play 發布應用程式時,您可以上傳 mapping.txt 檔案 指定每個應用程式版本使用 Android App Bundle 發布應用程式時 檔案就會自動納入應用程式套件內容。則 Google。 Google Play 會針對使用者回報的問題,重新追蹤傳入的堆疊追蹤,讓您 請前往 Play 管理中心審查這些聲明詳情請參閱如何針對當機時的堆疊追蹤去模糊化相關說明中心文章。

使用 R8 進行疑難排解

本節說明使用 R8 啟用縮減、模糊處理和最佳化時,疑難排解的一些策略。如果下方未列出您的問題,另請參閱 R8 常見問題頁面ProGuard 的疑難排解指南

產生已移除 (或保留) 程式碼的報表

為了協助您排解某些 R8 問題,建議您查看 R8 從應用程式中移除的所有程式碼針對每個要選取的模組 要產生報表,請將 -printusage <output-dir>/usage.txt 加進自訂報表 規則檔案。當您啟用 R8 並建構應用程式時,R8 會使用您指定的路徑和檔案名稱輸出報表。程式碼已移除的報表 類似如下:

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

但如果您想查看 R8 根據專案的保留規則決定的進入點報表,請在自訂規則檔案中加入 -printseeds <output-dir>/seeds.txt。當您啟用 R8 並建構應用程式時,R8 會使用您指定的路徑和檔案名稱輸出報表。保留進入點的報表看起來會像這樣:

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

資源縮減疑難排解

當您縮減資源時,會將建構 視窗會顯示 從應用程式中移除的資源。(您必須先按一下 [切換檢視] 顯示 Gradle 的詳細文字輸出內容)。例如:

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

Gradle 也會在 <module-name>/build/outputs/mapping/release/ 中建立名為 resources.txt 的診斷檔案 (與 ProGuard 的輸出檔案位於同一個資料夾)。這個檔案內含哪些資源會參照其他資源,以及如何運用或移除資源。

例如,如要找出 @drawable/ic_plus_anim_016 仍在應用程式中的原因,請開啟 resources.txt 檔案並搜尋檔案名稱。您可能會發現,它從其他資源中進行了參照,如下所示:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

您現在必須瞭解 @drawable/add_schedule_fab_icon_anim 的原因 但如果您向上搜尋,就會看到資源 ,在「根可連線資源為:」下方。這表示 add_schedule_fab_icon_anim 有程式碼參照 (也就是說,可在可連接的程式碼中找到其 R.drawable ID)。

如果您並未使用嚴格檢查,系統會將資源 ID 標示為可連線 如果有字串常數可用於建構 動態載入資源的資源名稱。在這種情況下,假設您搜尋 資源名稱的建構輸出內容,您可能會看到如下所示的訊息:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

如果看到這類字串,且確定該字串不是 可用以動態方式載入指定資源 tools:discard 屬性通知建構系統將其移除。 ,詳情請參閱如何自訂要保留的資源一節。