Android高手筆記-D8, R8編譯優化

  • 在之前的文章Android高手筆記-包體積優化中提到過通過編譯優化包體積,涉及到了ProGuard,D8,R8,其中關于ProGuard及包體積優化方案已經進行了詳細介紹,那么今天我們來說說D8和R8;

D8

  • D8是一款用于取代 DX、更快的 Dex 編譯器,可以生成更小的 APK;
開啟D8的好處
  1. 編譯更快、時間更短
  2. 編譯時占用內存更小
  3. .dex文件更小
  4. .dex 文件擁有更好的運行時性能
  5. 支持在代碼中使用 Java 8 語言
開啟與關閉
  • Android Studio 3.0 需要主動在gradle.properties文件中新增:android.enableD8=true
  • D8作為DX的一個替代方案,Android Studio 3.1版本開始,將D8作為默認的Dex編譯器。
  • 想關閉D8 ,可以在gradle.properties里添加如下配置:
android.enableD8=false //關閉D8恢復到DX-
android.enableD8.desugaring=false //恢復到以前的行為,讓脫糖發生在Java編譯之后,.class字節碼仍遵循Java 7格式
執行增量構建
  • 為了在開發過程中提高構建速度(例如提高持續集成 build 的速度),可以指示 d8 僅編譯項目的部分 Java 字節碼;
  • 例如,如果啟用了按類 dexing 處理,則只需重新編譯自上次構建以來修改過的類(d8 無法自動檢測哪些字節碼文件已被修改過,因此您需要手動指定類列表):
//執行幾個類的增量構建,并啟用按類 dexing 處理,并為增量構建指定輸出目錄
d8 MainActivity.class R.class --intermediate --file-per-class --output ~/build/intermediate/dex
  • 可以使用 --main-dex-list 指定想讓 d8 編譯到主 DEX 文件中的類
d8 ~/build/intermediate/dex --release --main-dex-list ~/build/classes.txt --output ~/build/release/dex
支持Java8
  • 通過一個叫做“脫糖”的編譯過程,將這些實用的語言功能轉換為可以在 Android 平臺上運行的字節碼,D8脫糖就不會在transforms目錄下生成desugar目錄。
  • Android Studio 和 Android Gradle 插件包含了 d8 啟用脫糖所需的類路徑資源。
  • 從命令行使用 d8 時,需要手動添加一些資源:
  1. --lib:標記目標 Android SDK 中的 android.jar路徑
  2. --classpath:標記項目的部分已編譯的 Java 字節碼,目前不打算將這部分字節碼編譯為 DEX 字節碼,但在將其他類編譯為 DEX 字節碼時需要用到這些字節碼。例如,如果代碼使用默認和靜態接口方法(一種 Java 8 語言功能),則需要使用此標記來指定您項目的所有 Java 字節碼的路徑,即使您不打算將所有 Java 字節碼都編譯為 DEX 字節碼也是如此。這是因為 d8 需要根據這些信息來理解您項目的代碼并解析對接口方法的調用
  • 示例對一個訪問默認接口方法的類執行增量構建:
d8 MainActivity.class --intermediate --file-per-class --output ~/build/intermediate/dex
--lib android_sdk/platforms/api-level/android.jar
--classpath ~/build/javac/debug
Java8新特性:接口默認方法和靜態方法
  • JDK1.8以前,接口(interface)沒有提供任何具體的實現;
  • JDK1.8開始,接口允許定義默認方法和靜態方法

R8

  • R8之前采用D8+ProGuard的形式構建,R8則將ProGuard和D8工具進行整合,目的是加速構建時間和減少輸出apk的大小;
開啟R8的好處
  1. 代碼縮減(搖樹優化):使用靜態代碼分析來查找和刪除無法訪問的代碼和未實例化的類型,對規避 64k 引用限制非常有用;
  2. 資源縮減:移除不使用的資源,包括應用庫依賴項中不使用的資源。
  3. 混淆代碼:縮短類和成員的名稱,從而減小 DEX 文件的大小
  4. 優化代碼:檢查并重寫代碼,選擇性內聯,移除未使用的參數和類合并來優化代碼大小
  5. 減少調試信息 : 規范化調試信息并壓縮行號信息。
  • R8 會自動執行上述編譯時任務,也可以停用某些任務或通過 ProGuard 規則文件自定義 R8 的行為。
  • 使用某個第三方庫時,通常只使用其中很小一部分。若不壓縮,所有庫代碼都會保留在應用中。冗長的代碼有時可以提高可讀性和可維護性: 例如,使用有意義的變量名和建造者模式 來幫助其他人更容易檢查和理解代碼;但是這些模式會加大代碼量,通常我們自己編寫的代碼有很大的壓縮空間。
開啟與關閉
  • Android Studio 3.3 需在項目的 gradle.properties 里加上:android.enableR8=true
  • Android Studio 3.4 或 Android Gradle 插件 3.4.0 及更高版本時,R8 是默認編譯器(不再使用 ProGuard 執行編譯時代碼優化),用于將項目的 Java 字節碼轉換為在 Android 平臺上運行的 DEX 格式。
  • 不過創建新項目時,縮減、混淆處理和代碼優化功能默認處于停用狀態。因為這些編譯時優化功能會增加項目的構建時間,而且如果沒有充分自定義要保留的代碼,還可能會引入錯誤。
  1. 開啟代碼縮減,需要在應用的主 build.gradle 文件中將 minifyEnable 屬性設置為 true
  2. 開啟資源縮減:需要在應用的主 build.gradle 文件中將 shrinkResources 屬性設置為 true
  • 資源縮減只有在與代碼縮減配合使用時才能發揮作用。在代碼縮減器移除所有不使用的代碼后,資源縮減器便可確定應用仍要使用的資源,當添加包含資源的代碼庫時尤其如此。必須移除不使用的庫代碼,使庫資源變為未引用資源,因而可由資源縮減器移除。
  1. 創建新項目或模塊時,IDE 會創建一個 <module-dir>/proguard-rules.pro 文件,以便您添加自己的規則。
android {
    ...
    buildTypes {
        release {
            shrinkResources true //啟用 R8 的資源縮減功能
            minifyEnabled true //啟用 R8 的代碼縮減功能
             proguardFiles
                //1. Android Gradle 插件會生成 proguard-android-optimize.txt(其中包含了對大多數 Android 項目都有用的規則),并啟用 @Keep* 注解。
                getDefaultProguardFile('proguard-android-optimize.txt'),
                //2. 使用 Android Studio 創建新模塊時,Android Studio 會在該模塊的根目錄中創建 proguard-rules.pro 文件
                'proguard-rules.pro'

                //3. AAR 庫:<library-dir>/proguard.txt, JAR 庫:<library-dir>/META-INF/proguard/
                //由于 ProGuard 規則是累加的,因此 AAR 庫依賴項包含的某些規則無法移除,并且可能會影響對應用其他部分的編譯。
                //例如,如果某個庫包含停用代碼優化功能的規則,該規則會針對整個項目停用優化功能。

                //4. Android 資源打包工具 2 (AAPT2):
                //使用 minifyEnabled true 構建項目后,AAPT2 會根據對應用清單中的類、布局及其他應用資源的引用,生成保留規則。
                //文件路徑為:<module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt

                //5. 自定義配置文件:詳見下面的添加其他配置
        }
    }
}
添加其他配置
  • 可以通過在相應的 productFlavor 代碼塊中再添加一個 proguardFiles 屬性來添加每個構建變體專用的規則
android {
    ...
    buildTypes {
        release {
            ...
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}
  • flavor2 使用全部三個 ProGuard 規則,因為還應用了來自 release 代碼塊的規則。
關閉R8
  • 可以在gradle.properties里添加如下配置:
android.enableR8=false
開啟R8完全模式
  • R8 普通模式是兼容 ProGuard的,若原項目里已使用了ProGuard,直接啟用 R8 即可。同時,R8 也有完全模式,與ProGuard不直接兼容。
    可以在 gradle.properties 文件中另外設置以下內容:
android.enableR8.fullMode=true
  • 額外的優化功能會使 R8 的行為與 ProGuard 不同,因此可能會需要您添加額外的 ProGuard 規則,以避免運行時問題。
自定義要保留的代碼
  • 在某些情況下,R8 很難做出正確判斷,因而可能會移除應用實際上需要的代碼:
1. 當應用通過 Java 原生接口 (JNI) 調用方法時
2. 當您的應用在運行時查詢代碼時(如使用反射)
- 反射 (Reflection) 會導致 R8 在跟蹤代碼時無法識別到代碼的入口點
  • 如需修復錯誤并強制 R8 保留某些代碼,在 ProGuard 規則文件中添加 -keep 代碼行,如
-keep public class MyClass
  • 或者為要保留的代碼添加 @Keep 注解
1. 在類上添加 @Keep 可按原樣保留整個類
2. 在方法或字段上添加該注釋,將使該方法/字段(及其名稱)以及類名稱保持不變。
3. 只有在使用 AndroidX 注解庫且您添加 Android Gradle 插件隨附的 ProGuard 規則文件時,此注解才可用。
  • 如需輸出 R8 在構建項目時應用的所有規則的完整報告,請將以下代碼添加到模塊的 proguard-rules.pro 文件中:
// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt
自定義要保留的資源
  • 如果您有想要保留或舍棄的特定資源,請在項目中創建一個包含 <resources> 標記的 XML 文件,并在 tools:keep 屬性中指定每個要保留的資源,在 tools:discard 屬性中指定每個要舍棄的資源。這兩個屬性都接受以逗號分隔的資源名稱列表。您可以將星號字符用作通配符。
  • 將該文件保存在項目資源中,例如,保存在 res/raw/keep.xml 中。構建系統不會將此文件打包到應用中。
<?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" />
嚴格引用檢查
  • 通常資源縮減器可以準確地判斷是否使用了某個資源。不過如果代碼中調用了 Resources.getIdentifier()(或者引用的任何庫會執行此調用,例如 AppCompat 庫便會執行此調用),這意味著代碼將根據動態生成的字符串查詢資源名稱。資源縮減器在默認情況下(安全縮減模式)會采取保護行為,將所有具有匹配名稱格式的資源標記為可能已使用,無法移除。資源縮減器還會查看代碼中的所有字符串常量以及各種 res/raw/ 資源,以查找格式類似于 file:///android_res/drawable//ic_plus_anim_016.png 的資源網址。如果它找到與此類似的字符串,或找到其他看似可用來構建與此類似的網址的字符串,則不會將它們移除。
  • 例如,以下代碼會將所有帶 img_ 前綴的資源標記為已使用:
val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)
  • 啟用嚴格引用檢查: 將 keep.xml 文件中的 shrinkMode 設為 strict,此時如果通過動態生成的字符串引用資源,必須使用 tools:keep 屬性手動保留這些資源。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />
移除未使用的備用資源
  • Gradle 資源縮減器只會移除未由應用代碼引用的資源,這意味著,它不會移除用于不同設備配置的備用資源;
  • 例如使用的是包含語言資源的庫(如 AppCompat 或 Google Play 服務),那么應用中將包含這些庫中消息的所有已翻譯語言的字符串,可以使用 resConfigs 屬性移除應用不需要的備用資源文件,如設置只保留英語和法語的語言資源
android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}
合并重復資源
  • 默認情況下,Gradle 還會合并同名的資源(多個文件具有完全相同的資源名稱、類型和限定符時)。這一行為不受 shrinkResources 屬性控制,也無法停用,因為當多個資源與代碼查詢的名稱匹配時,有必要利用這一行為避免錯誤。
  • Gradle 會在重復項中選擇它認為最合適的文件(根據下述優先順序),并且只將這一個資源傳遞給 AAPT,以便在最終工件中分發
  • Gradle 會按以下級聯優先順序合并重復資源:庫項目依賴項 → 主資源 → 構建變種 → 構建類型, 如某個重復資源同時出現在主資源和構建變種中,Gradle 會選擇構建變種中的資源。
  • 如果完全相同的資源出現在同一源代碼集中,Gradle 無法合并它們,并且會發出資源合并錯誤,或者在 build.gradle 文件的 sourceSet 屬性中定義了多個源代碼集,src/main/res/ 和 src/main/res2/ 包含完全相同的資源也會報錯。

參考

我是今陽,如果想要進階和了解更多的干貨,歡迎關注微信公眾號 “今陽說” 接收我的最新文章

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容