# Android 混淆機制 ###### tags: `Tag(Android 技術相關)` Proguard : Android Studio 3.4以下默認默認開啟 R8 : Android Studio 3.4或Android Gradle插件3.4.0及更高版本時,R8是默認開啟 ### 兩種混淆機制的相同特性: * 4個功能特性: * 壓縮: - Java原始程式碼通常被編譯為位元組碼,雖然為元組碼比原始程式碼更簡潔,但他本身會包含很多無用的程式碼。 - 從應用及其庫依賴項中檢測並安全地移除未使用的類、字段、方法和屬性(這使其成為了一個對於規避[64k引用限制](https://developer.android.com/studio/build/multidex)非常有用的工具) 。例如,如果您僅使用某個庫依賴項的少數幾個API,縮減功能可以識別應用“未”使用的庫代碼並僅從應用中移除這部分代碼。如需了解詳情,請轉到介紹如何[縮減代碼](https://developer.android.com/studio/build/shrink-code#shrink-code)的部分。 * 最佳化 - 從封裝應用中移除不使用的資源,包括應用庫依賴項中的不使用的資源。此功能可與代碼縮減功能結合使用,這樣一來,移除不使用的代碼後,也可以安全地移除不再引用的所有資源。如需了解詳情,請轉到介紹如何[縮減資源](https://developer.android.com/studio/build/shrink-code#shrink-resources)的部分。 * 混淆 - 使用無意義的簡短字母組合對類別名稱、欄位名稱和方法名稱進行重新命名 - 縮短類和成員的名稱,從而減小DEX文件的大小。如需了解詳情,請轉到介紹如何[對代碼進行混淆處理](https://developer.android.com/studio/build/shrink-code#obfuscate)的部分。 * 優化 - 檢查並重寫代碼,以進一步減小應用的DEX文件的大小。例如,如果R8檢測到從未採用過給定if/else語句的else {}分支,則會移除else {}分支的代碼。如需了解詳情,請轉到介紹[代碼優化](https://developer.android.com/studio/build/shrink-code#optimization)的部分。 * build.gradle 相同設定 ``` android { buildTypes { debug { //關閉,僅啟用代碼收縮,混淆和優化 minifyEnabled false //關閉,最佳化啟用資源縮減,該縮減由 Android Gradle插件 shrinkResources false //默認ProGuard規則文件,配置檔 Android Gradle插件 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { minifyEnabled true //開啟,混淆 shrinkResources true //開啟,去除用到的資源 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } ``` ### 輸出文件 啓用R8或ProGuard構建項目後會在模塊下的build\outputs\mapping\release文件夾下輸出下列文件: dump.txt:說明 APK 中所有類文件的內部結構。 mapping.txt:提供原始與混淆過的類、方法和字段名稱之間的轉換。 seeds.txt:列出未進行混淆的類和成員。 usage.txt:列出從 APK 移除的代碼。 --- ## Proguard ### 啟動和設定 一般build.gradle檔案預設已經對Proguard 進行設定,只不過是還沒有啟動。 - proguard-android-optimize.txt 是Proguard 預設的混淆設定檔 - 根據APP的特殊性增加或減少相關設定 - 其中 proguard-android.txt 為預設的混淆規則,該文件在SDK目錄 /tools/proguard 中。 proguard-rules.pro 為自定義的混淆規則。 **==注意:==** **==使用 Proguard 時,不是只要 minifyEnabled 設為 true,就大公告成,因為 ProGuard 預設配置文件 (proguard-android.txt) ,會移除所有未使用的代碼。大部分情況下也會移除到真正需要的程式碼。這時我們就需要自定義哪些程式碼,需要特地保留,不要移除、不要混淆。==** ## 基本常用混淆語法 * 關鍵字 keep 就是要去保留什麼,不要去混淆什麼 * #為註解 * 保留類別和類別成員 | 保留 | 防止被刪除或重命名 | 防止被重命名 | | -------- | -------- | -------- | | 類別和類別成員 | -keep | -keepnames | | 僅類別成員 | -keepclassmembers |-keepclassmembernames| | 如果擁有某成員,保留類防止被重命名| -keepclasseswithmembers |-keepclasseswithmembernames * 常用符號 | 符號 | 作用 | | -------- | -------- | -------- | | * | 匹配任意長度字符,但不含包名分隔符(.) | | ** | 匹配任意長度字符,並且包含包名分隔符(.) | | *** | 匹配任意參數類型 | | … | 匹配任意長度的任意類型參數 | | % | 匹配任何原始類型 | | ? | 匹配類名中的任何單個字符 | * 常用 ProGuard 模板 ``` #壓縮比,預設5不修改 -optimizationpasses 5 #不使用大小寫混合,混淆後類名稱為小寫 -dontusemixedcaseclassnames #指定不去忽略公開的 publicli classes -dontskipnonpubliclibraryclasses #混淆後產生印射文件 -verbose #註解此行,可以自動上傳 mapping 檔到 Firebase #-printmapping mapping.txt #保留泛型 -keepattributes Signature # 不做預校驗,加速建置速度 -dontpreverify # 保留Annotation不混淆 -keepattributes *Annotation*,InnerClasses # 避免混淆泛型 -keepattributes Signature # 抛出異常時保留檔名與行數 -keepattributes SourceFile,LineNumberTable # 保留 android-support -dontwarn android.support.design.** -keep class android.support.design.** { *; } -keep interface android.support.design.** { *; } -keep public class android.support.design.R$* { *; } ``` 參考連結: http://tw-hkt.blogspot.com/2018/12/proguard.html --- ## R8 - R8會自動執行上述編譯時任務。不過,您也可以停用某些任務或通過ProGuard規則文件自定義R8的行為。 - ==R8支持所有現有ProGuard規則文件==,因此您在更新Android Gradle插件以使用R8時,無需更改現有規則。 ### 啟動和設定 - 使用Android Studio 3.4或Android Gradle插件3.4.0及更高版本時,**R8是默認編譯器**,用於將項目的Java字節碼轉換為在Android平台上運行的DEX格式。 ### R8 配置文件 #### R8 使用的ProGuard 規則文件可以使用的來源。 - **Android Studio <module-dir>/proguard-rules.pro** 當您使用Android Studio創建新模塊時,IDE會在該模塊的根目錄中創建文件。 proguard-rules.pro 默認情況下,此文件不會應用任何規則。因此,請在此處添加您自己的ProGuard規則,如您的[自定義保留規則](https://developer.android.com/studio/build/shrink-code#keep-code) - **Android Gradle 插件 由Android Gradle 插件在編譯時生成** Android Gradle插件會生成(其中包含了對大多數Android項目都有用的規則),並啟用註釋。 proguard-android-optimize.txt@Keep* 默認情況下,使用Android Studio創建新模塊時,模塊級build.gradle文件會將此規則文件納入到您的發布版本中。 **==注意: 雖然Android Gradle插件包含額外的預定義ProGuard規則文件,但建議您使用proguard-android-optimize.txt。==** - **庫依賴項 AAR 庫:<library-dir>/proguard.txt JAR 庫:<library-dir>/META-INF/proguard/** - **ndroid 資產打包工具2 (AAPT2)** 使用minifyEnabled true構建項目後: <module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt - **自定義配置文件** 默認情況下,當您使用Android Studio創建新模塊時,IDE會創建,以便您添加自己的規則。 <module-dir>/proguard-rules.pro --- ## R8 和 Proguard 相比 R8 可以更快地縮減代碼,同時改善輸出大小,R8 默認處於啓用狀態,你可將以下代碼添加到項目的 gradle.properties 文件以停用 R8: `android.enableR8=false` R8 普通模式是兼容 Proguard的,R8 完全模式與會啓用一些額外的優化,這個時候可能需要一些其它ProGuard規則以避免運行時問題,可以在 項目的gradle.properties 文件中設置以下內容啓用完全模式。 ``` android.enableR8.fullMode=true ``` --- ### 縮減代碼 如果將**minifyEnabled屬性設為true**,則默認會啟用R8代碼縮減功能 ### 自定義要保留的代碼 强制 R8 保留某些代码,请在 ProGuard 规则文件中添加 -keep 代码行。例如: `-keep public class MyClass` 或者,您也可以為您要保留的代碼添加[@Keep](https://developer.android.com/reference/androidx/annotation/Keep?hl=zh-cn)註釋。在類上==添加@Keep可按原樣保留整個類==。在方法或字段上添加該註釋,將使該方法/字段(及其名稱)以及類名稱保持不變。==請注意,只有在使用AndroidX註釋庫且您添加Android Gradle插件隨附的ProGuard規則文件時,此註釋才可用==。 --- ### 縮減資源 **資源縮減**只有在與**代碼縮減**配合使用時才能發揮作用 **shrinkResources ==true==** **minifyEnabled ==true==** ### 自定義要保留的資源 想要保留或捨棄的特定資源,請在項目中創建一個包含<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" /> ``` ### 啟用嚴格引用檢查 資源縮減器可以準確地==判斷是否使用了某個資源==。不過,如果您的代碼會調用 **Resources.getIdentifier()**(或者您的任何庫會進行此調用,例如**AppCompat**庫便會執行此調用),則==意味著您的代碼將根據動態生成的字符串查詢資源名稱==。 當您啟用嚴格引用檢查時,資源縮減器在默認情況下會採取保護性行為,將所有具有匹配名稱格式的資源標記為可能已使用,無法移除 例如,以下代碼會將所有帶img_前綴的資源標記為已使用。 ``` 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還會合併同名的資源,如可能位於不同資源文件夾中的同名可繪製對象。這一行為不受shrinkResources屬性控制,也無法停用,因為當多個資源與代碼查詢的名稱匹配時,有必要利用這一行為來避免錯誤。 只有在兩個或更多個文件具有完全相同的資源名稱、類型和限定符時,才會進行資源合併。Gradle 會在重複項中選擇它認為最合適的文件(根據下述優先順序),並且只將這一個資源傳遞給AAPT,以便在APK 文件中分發。 Gradle 會在以下位置查找重複資源: * 與主源集關聯的主資源,一般位於src/main/res/中。 * 來自版本類型和版本變種的變體疊加層。 * 庫項目依賴項 Gradle 會按以下級聯優先順序合併重複資源: 依賴項→ 主資源→ 版本變種→ 版本類型 --- 參考說明:https://www.twblogs.net/a/5d443e55bd9eee5327fb496b 官網說明:https://developer.android.com/studio/build/shrink-code?hl=zh-cn#enable #### R8常見問題 https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md #### Proguard 常見問題 https://www.guardsquare.com/en/products/proguard/manual/troubleshooting
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up