Android編譯優化之混淆配置
背景
為了使用java8及后續java新版本的特性,Google增加了一步編譯過程—脫糖(desugaring),但這一步會導致更長的編譯時間,這也是為什么Google會推出D8和R8編譯器來優化編譯速度。
什么是脫糖?
脫糖即在編譯階段將在語法層面一些底層字節碼不支持的特性轉換為基礎的字節碼結構,(比如 List 上的泛型脫糖后在字節碼層面實際為 Object); Android 工具鏈對 Java8 語法特性脫糖的過程可謂豐富多彩,當然他們的最終目的是一致的:使新的語法可以在所有的設備上運行。
D8
D8的功能是將Java字節碼轉化成dex代碼,D8作為DX的一個替代方案。編譯流程如下圖所示:
D8
Android Studio 3.1版本開始,將D8作為默認的Dex編譯器。如果想關閉D8,可以在gradle.properties里添加如下配置:
android.enableD8=false android.enableD8.desugaring=false
開啟D8的好處
?編譯更快、時間更短
?DEX編譯時占用內容更小
?.dex文件更小
?D8編譯的.dex文件擁有相同或者更好的運行性能
如果你的工程已經使用Java 8盡可能開啟D8編譯,不然可能會出現編譯錯誤。
R8
R8作為原本Proguard 壓縮與優化(minification、shrinking、optimization)部分的替代品,依然使用與Proguard一樣的keep規則,是新一代的代碼壓縮工具。 R8之前采用D8+Proguard的形式構建,R8則將混淆和D8工具進行整合,目的是加速構建時間和減少輸出apk的大小。
R8
Gradle插件版本達到3.4.0及以上,默認會開始R8進行代碼優化。如果你不想開啟R8,可以在gradle.properties里添加如下配置:
android.enableR8=false
開啟R8的好處
?代碼縮減:規避64引用限制
?資源縮減:移除不使用的資源
?混淆代碼:減小DEX文件大小
?優化代碼:進一步減小DEX文件大小
相關評測報告
D8R8評測報告
AS多模塊混淆配置
AS多模塊下,我們可以采用各個模塊單獨配置方式,即proguard文件配置在各個模塊下,在各自的build.gradle文件中引入; 也可以采用下方集中配置方式,將各模塊的proguard文件集中配置到一個文件夾下,然后在app模塊中集中引入所依賴的模塊的proguard文件。 下面以cmcc_service這個app模塊為例加以說明,
buildTypes{ release{ minifyEnabledtrue//開啟混淆 shrinkResourcestrue//無用資源去除 zipAlignEnabledtrue proguardFilesgetDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro', '../proguardDefine/common-rules.pro','../proguardDefine/live_lib-rules.pro', '../proguardDefine/gs_api_adapter-rules.pro','../proguardDefine/cmcc_service.pro', '../proguardDefine/lib_voiceassistant-rules.pro','../proguardDefine/cmcc_softprobe-rules.pro' if(propertyHaveSigningConfigs) signingConfigsigningConfigs.release } }
其中,proguard-android-optimize.txt是android默認的一些通用混淆規則,proguard-rules.pro是app代碼的混淆規則,proguardDefine文件夾下所依賴模塊統一放置proguard規則的地方。
注意:引入的三方libs,一般都配有響應的proguard文件,所以不需要再重復配置,gradle編譯時會將相關文件 proguard文件合并到一個configuration.txt文件中
configuration位置
proguard-android-optimize.txt
#禁用一些代碼簡化和優化,以及字段和類合并 -optimizations!code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* #運行優化passes的數量為5,數值越高,混淆效果越好,但耗時也更長 -optimizationpasses5 #允許訪問和修改保護代碼 -allowaccessmodification #不允許使用大小寫混合的類名 -dontusemixedcaseclassnames #不跳過非公共庫類 -dontskipnonpubliclibraryclasses #詳細輸出 -verbose #保留一些反射所需的屬性 -keepattributes*Annotation*,Signature,InnerClasses,EnclosingMethod #保留以下三個類及其公共成員 -keeppublicclasscom.google.vending.licensing.ILicensingService -keeppublicclasscom.android.vending.licensing.ILicensingService -keeppublicclasscom.google.android.vending.licensing.ILicensingService #忽略以下類,不輸出note信息 -dontnotecom.android.vending.licensing.ILicensingService -dontnotecom.google.vending.licensing.ILicensingService -dontnotecom.google.android.vending.licensing.ILicensingService #對于本地方法,參見http://proguard.sourceforge.net/manual/examples.html#native #保留包含native方法的類和方法 -keepclasseswithmembernames,includedescriptorclassesclass*{ native; } #保留公共的View子類的set和get方法 #以便于使用屬性動畫 -keepclassmemberspublicclass*extendsandroid.view.View{ voidset*(***); ***get*(); } #保留Activity中可用于XML屬性onClick中的方法 -keepclassmembersclass*extendsandroid.app.Activity{ publicvoid*(android.view.View); } #對于枚舉類,參見http://proguard.sourceforge.net/manual/examples.html#enumerations #保留枚舉類成員 -keepclassmembersenum*{ publicstatic**[]values(); publicstatic**valueOf(java.lang.String); } #保留實現Parcelable接口的類的CREATOR靜態成員 -keepclassmembersclass*implementsandroid.os.Parcelable{ publicstaticfinal**CREATOR; } #保留JavaScript接口方法上的注解 -keepclassmembersclass*{ @android.webkit.JavascriptInterface ; } #支持庫包含對新平臺版本的引用 #在應用鏈接舊版平臺版本時,不要發出警告,因為它們是安全的 -dontnoteandroid.support.** -dontnoteandroidx.** -dontwarnandroid.support.** -dontwarnandroidx.** #該類已棄用,但仍然保留用于向后兼容 -dontwarnandroid.util.FloatMath #這段混淆規則用于保護使用了@Keep注解的類和成員不被混淆,以及忽略特定的冗余類。 -keepclassandroid.support.annotation.Keep -keepclassandroidx.annotation.Keep #保留使用了@Keep注解的類和接口的所有成員 -keep@android.support.annotation.Keepclass*{;} -keep@androidx.annotation.Keepclass*{;} #保留使用了@Keep注解的類的方法 -keepclasseswithmembersclass*{ @android.support.annotation.Keep ; } -keepclasseswithmembersclass*{ @androidx.annotation.Keep ; } #保留使用了@Keep注解的類的字段 -keepclasseswithmembersclass*{ @android.support.annotation.Keep ; } -keepclasseswithmembersclass*{ @androidx.annotation.Keep ; } #保留使用了@Keep注解的類的構造方法 -keepclasseswithmembersclass*{ @android.support.annotation.Keep (...); } -keepclasseswithmembersclass*{ @androidx.annotation.Keep (...); } #忽略特定的冗余類 #android.jar和org.apache.http.legacy.jar中的類重復 -dontnoteorg.apache.http.** -dontnoteandroid.net.http.** #android.jar和core-lambda-stubs.jar中的類重復 -dontnotejava.lang.invoke.**
一些三方自帶混淆規則
這些規則同樣合并到了configuration.txt文件中retrofit2混淆規則
#Theproguardconfigurationfileforthefollowingsectionis/home/cl/.gradle/caches/transforms-3/29b6aa006718d6829551a18646bf70bb/transformed/rules/lib/META-INF/proguard/retrofit2.pro #Retrofitdoesreflectionongenericparameters.InnerClassesisrequiredtouseSignatureand #EnclosingMethodisrequiredtouseInnerClasses. -keepattributesSignature,InnerClasses,EnclosingMethod #Retainservicemethodparameterswhenoptimizing. -keepclassmembers,allowshrinking,allowobfuscationinterface*{ @retrofit2.http.*; } #Ignoreannotationusedforbuildtooling. -dontwarnorg.codehaus.mojo.animal_sniffer.IgnoreJRERequirement #IgnoreJSR305annotationsforembeddingnullabilityinformation. -dontwarnjavax.annotation.** #GuardedbyaNoClassDefFoundErrortry/catchandonlyusedwhenontheclasspath. -dontwarnkotlin.Unit #Top-levelfunctionsthatcanonlybeusedbyKotlin. -dontwarnretrofit2.-KotlinExtensions #Endofcontentfrom/home/cl/.gradle/caches/transforms-3/29b6aa006718d6829551a18646bf70bb/transformed/rules/lib/META-INF/proguard/retrofit2.pro
RxJava2RxAndroid混淆規則
-dontwarnjava.util.concurrent.Flow*
Okhttp3混淆規則
#Theproguardconfigurationfileforthefollowingsectionis/home/cl/.gradle/caches/transforms-3/af3ecb4c3ae4accf6423845d738f047d/transformed/rules/lib/META-INF/proguard/okhttp3.pro #JSR305annotationsareforembeddingnullabilityinformation. -dontwarnjavax.annotation.** #Aresourceisloadedwitharelativepathsothepackageofthisclassmustbepreserved. -keepnamesclassokhttp3.internal.publicsuffix.PublicSuffixDatabase #AnimalSniffercompileOnlydependencytoensureAPIsarecompatiblewitholderversionsofJava. -dontwarnorg.codehaus.mojo.animal_sniffer.* #OkHttpplatformusedonlyonJVMandwhenConscryptdependencyisavailable. -dontwarnokhttp3.internal.platform.ConscryptPlatform #Endofcontentfrom/home/cl/.gradle/caches/transforms-3/af3ecb4c3ae4accf6423845d738f047d/transformed/rules/lib/META-INF/proguard/okhttp3.pro
更多可能用到的混淆配置
#指定不去忽略非公共庫的類 -dontskipnonpubliclibraryclasses #指定不去忽略非公共庫的成員 -dontskipnonpubliclibraryclassmembers #混淆時不做預校驗 -dontpreverify #忽略警告 -ignorewarnings #保留代碼行號,方便異常信息的追蹤 -keepattributesSourceFile,LineNumberTable #appcompat庫不做混淆 -keepclassandroidx.appcompat.** #保留AndroidManifest.xml文件:防止刪除AndroidManifest.xml文件中定義的組件 -keeppublicclass*extendsandroid.app.Fragment -keeppublicclass*extendsandroid.app.Activity -keeppublicclass*extendsandroid.app.Application -keeppublicclass*extendsandroid.app.Service -keeppublicclass*extendsandroid.content.BroadcastReceiver -keeppublicclass*extendsandroid.content.ContentProvider -keeppublicclass*extendsandroid.preference.Preference -keeppublicclass*extendsandroid.view.View #保留序列化,例如Serializable接口 -keepclassmembersclass*implementsjava.io.Serializable{ staticfinallongserialVersionUID; privatestaticfinaljava.io.ObjectStreamField[]serialPersistentFields; privatevoidwriteObject(java.io.ObjectOutputStream); privatevoidreadObject(java.io.ObjectInputStream); java.lang.ObjectwriteReplace(); java.lang.ObjectreadResolve(); } #帶有Context、View、AttributeSet類型參數的初始化方法 -keepclasseswithmembersclass*{ public(android.content.Context); } -keepclasseswithmembersclass*{ public (android.content.Context,android.util.AttributeSet); } -keepclasseswithmembersclass*{ public (android.content.Context,android.util.AttributeSet,int); } -keepclassmembersclass*extendsandroid.app.Activity{ publicvoid*(android.view.View); } #保留資源R類 -keepclass**.R$*{*;} #避免回調函數onXXEvent混淆 -keepclassmembersclass*{ void*(**On*Event); void*(**On*Listener); void*(**on*Changed); } #業務實體不做混淆,避免gson解析錯誤 -dontwarncom.grandstream.convergentconference.entity.** -keepclasscom.grandstream.convergentconference.entity.**{*;} #Rxjava、RxAndroid,官方ReadMe文檔中說明無需特殊配置 -dontwarnjava.util.concurrent.Flow* #okhttp3、okio、retrofit,jar包中已包含相關proguard規則,無需配置 #其他一些配置
Android.bp混淆配置
android_app{ name:"MyApp", package_name:"com.example.myapp", srcs:["java/**/*.java"], manifest:"AndroidManifest.xml", dex_preopt:{ enabled:true, }, apk_cert_permissions:[ "android.permission.ACCESS_FINE_LOCATION", "android.permission.RECORD_AUDIO", "android.permission.READ_CONTACTS", ], certificates:["platform"], resource_files:["res/**/*"], enable_proguard:true, proguard_flags:["proguard-android.txt"], dex_preopt_image_dir:"target/product/${TARGET_PRODUCT}/obj/dex_preopt_image", }
其中,enable_proguard 設置為 true 表示啟用代碼和資源混淆,proguard_flags 指定了 Proguard 混淆規則文件,可以在文件中指定代碼和資源混淆規則。 resource_files 指定了應用程序的資源文件,包括 AndroidManifest.xml 文件。
android.mk混淆配置
LOCAL_PROGUARD_ENABLED:=obfuscationoptimization LOCAL_PROGUARD_FLAG_FILES:=proguard.flags ifeq(eng,$(TARGET_BUILD_VARIANT)) LOCAL_PROGUARD_FLAG_FILES+=proguard-test.flags else LOCAL_PROGUARD_FLAG_FILES+=proguard-release.flags endif
LOCAL_PROGUARD_ENABLED有以下幾種配置:
1.LOCAL_PROGUARD_ENABLED := disabled:禁用混淆。
2.LOCAL_PROGUARD_ENABLED := obfuscate:只開啟代碼混淆,不進行優化。
3.LOCAL_PROGUARD_ENABLED := optimize:只開啟優化,不進行代碼混淆。
4.LOCAL_PROGUARD_ENABLED := obfuscate optimize:同時開啟代碼混淆和優化。
編譯系統默認應該會加載一個android通用的混淆文件,LOCAL_PROGUARD_FLAG_FILES用于指定app特定的proguard文件。 如果我們不太會配置這些選項,可以去查看原生應用的混淆配置,這對你會有很大啟發。
混淆堆棧還原
使用 android 自帶的混淆堆棧還原工具 proguardgui 解決
Android/Sdk/tools/proguard/bin$ls proguard.shproguardgui.shretrace.sh /Android/Sdk/tools/proguard/bin$./proguardgui.sh
腳本運行后會打開 ProGuard 調試界面
proguard gui tools
填寫混淆前后的映射文件 mapping.txt 和需要還原的 logcat 異常日志,點擊 Retrace 按鈕就能還原混淆前的異常信息
結論
總之,一些通用混淆規則Android已經幫我寫好了,我們關心的可能就是我們自己開發的那部分代碼的混淆規則,混淆工作做的好有很多好處: 代碼更安全、包體積更小、運行速度更快,但是混淆也會帶來意想不到的運行異常,希望你做好測試,希望你盡快用起來。
審核編輯:湯梓紅
-
Android
+關注
關注
12文章
3935瀏覽量
127339 -
JAVA
+關注
關注
19文章
2966瀏覽量
104702 -
代碼
+關注
關注
30文章
4779瀏覽量
68524 -
編譯
+關注
關注
0文章
657瀏覽量
32852
原文標題:Android編譯優化之混淆配置
文章出處:【微信號:哆啦安全,微信公眾號:哆啦安全】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論