Android編譯優(yōu)化之混淆配置
背景
為了使用java8及后續(xù)java新版本的特性,Google增加了一步編譯過程—脫糖(desugaring),但這一步會導(dǎo)致更長的編譯時(shí)間,這也是為什么Google會推出D8和R8編譯器來優(yōu)化編譯速度。
什么是脫糖?
脫糖即在編譯階段將在語法層面一些底層字節(jié)碼不支持的特性轉(zhuǎn)換為基礎(chǔ)的字節(jié)碼結(jié)構(gòu),(比如 List 上的泛型脫糖后在字節(jié)碼層面實(shí)際為 Object); Android 工具鏈對 Java8 語法特性脫糖的過程可謂豐富多彩,當(dāng)然他們的最終目的是一致的:使新的語法可以在所有的設(shè)備上運(yùn)行。
D8
D8的功能是將Java字節(jié)碼轉(zhuǎn)化成dex代碼,D8作為DX的一個替代方案。編譯流程如下圖所示:
D8
Android Studio 3.1版本開始,將D8作為默認(rèn)的Dex編譯器。如果想關(guān)閉D8,可以在gradle.properties里添加如下配置:
android.enableD8=false android.enableD8.desugaring=false
開啟D8的好處
?編譯更快、時(shí)間更短
?DEX編譯時(shí)占用內(nèi)容更小
?.dex文件更小
?D8編譯的.dex文件擁有相同或者更好的運(yùn)行性能
如果你的工程已經(jīng)使用Java 8盡可能開啟D8編譯,不然可能會出現(xiàn)編譯錯誤。
R8
R8作為原本Proguard 壓縮與優(yōu)化(minification、shrinking、optimization)部分的替代品,依然使用與Proguard一樣的keep規(guī)則,是新一代的代碼壓縮工具。 R8之前采用D8+Proguard的形式構(gòu)建,R8則將混淆和D8工具進(jìn)行整合,目的是加速構(gòu)建時(shí)間和減少輸出apk的大小。
R8
Gradle插件版本達(dá)到3.4.0及以上,默認(rèn)會開始R8進(jìn)行代碼優(yōu)化。如果你不想開啟R8,可以在gradle.properties里添加如下配置:
android.enableR8=false
開啟R8的好處
?代碼縮減:規(guī)避64引用限制
?資源縮減:移除不使用的資源
?混淆代碼:減小DEX文件大小
?優(yōu)化代碼:進(jìn)一步減小DEX文件大小
相關(guān)評測報(bào)告
D8R8評測報(bào)告
AS多模塊混淆配置
AS多模塊下,我們可以采用各個模塊單獨(dú)配置方式,即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默認(rèn)的一些通用混淆規(guī)則,proguard-rules.pro是app代碼的混淆規(guī)則,proguardDefine文件夾下所依賴模塊統(tǒng)一放置proguard規(guī)則的地方。
注意:引入的三方libs,一般都配有響應(yīng)的proguard文件,所以不需要再重復(fù)配置,gradle編譯時(shí)會將相關(guān)文件 proguard文件合并到一個configuration.txt文件中
configuration位置
proguard-android-optimize.txt
#禁用一些代碼簡化和優(yōu)化,以及字段和類合并 -optimizations!code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* #運(yùn)行優(yōu)化passes的數(shù)量為5,數(shù)值越高,混淆效果越好,但耗時(shí)也更長 -optimizationpasses5 #允許訪問和修改保護(hù)代碼 -allowaccessmodification #不允許使用大小寫混合的類名 -dontusemixedcaseclassnames #不跳過非公共庫類 -dontskipnonpubliclibraryclasses #詳細(xì)輸出 -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); } #保留實(shí)現(xiàn)Parcelable接口的類的CREATOR靜態(tài)成員 -keepclassmembersclass*implementsandroid.os.Parcelable{ publicstaticfinal**CREATOR; } #保留JavaScript接口方法上的注解 -keepclassmembersclass*{ @android.webkit.JavascriptInterface ; } #支持庫包含對新平臺版本的引用 #在應(yīng)用鏈接舊版平臺版本時(shí),不要發(fā)出警告,因?yàn)樗鼈兪前踩?-dontnoteandroid.support.** -dontnoteandroidx.** -dontwarnandroid.support.** -dontwarnandroidx.** #該類已棄用,但仍然保留用于向后兼容 -dontwarnandroid.util.FloatMath #這段混淆規(guī)則用于保護(hù)使用了@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注解的類的構(gòu)造方法 -keepclasseswithmembersclass*{ @android.support.annotation.Keep (...); } -keepclasseswithmembersclass*{ @androidx.annotation.Keep (...); } #忽略特定的冗余類 #android.jar和org.apache.http.legacy.jar中的類重復(fù) -dontnoteorg.apache.http.** -dontnoteandroid.net.http.** #android.jar和core-lambda-stubs.jar中的類重復(fù) -dontnotejava.lang.invoke.**
一些三方自帶混淆規(guī)則
這些規(guī)則同樣合并到了configuration.txt文件中retrofit2混淆規(guī)則
#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混淆規(guī)則
-dontwarnjava.util.concurrent.Flow*
Okhttp3混淆規(guī)則
#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 #混淆時(shí)不做預(yù)校驗(yàn) -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類型參數(shù)的初始化方法 -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$*{*;} #避免回調(diào)函數(shù)onXXEvent混淆 -keepclassmembersclass*{ void*(**On*Event); void*(**On*Listener); void*(**on*Changed); } #業(yè)務(wù)實(shí)體不做混淆,避免gson解析錯誤 -dontwarncom.grandstream.convergentconference.entity.** -keepclasscom.grandstream.convergentconference.entity.**{*;} #Rxjava、RxAndroid,官方ReadMe文檔中說明無需特殊配置 -dontwarnjava.util.concurrent.Flow* #okhttp3、okio、retrofit,jar包中已包含相關(guān)proguard規(guī)則,無需配置 #其他一些配置
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 設(shè)置為 true 表示啟用代碼和資源混淆,proguard_flags 指定了 Proguard 混淆規(guī)則文件,可以在文件中指定代碼和資源混淆規(guī)則。 resource_files 指定了應(yīng)用程序的資源文件,包括 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:只開啟代碼混淆,不進(jìn)行優(yōu)化。
3.LOCAL_PROGUARD_ENABLED := optimize:只開啟優(yōu)化,不進(jìn)行代碼混淆。
4.LOCAL_PROGUARD_ENABLED := obfuscate optimize:同時(shí)開啟代碼混淆和優(yōu)化。
編譯系統(tǒng)默認(rèn)應(yīng)該會加載一個android通用的混淆文件,LOCAL_PROGUARD_FLAG_FILES用于指定app特定的proguard文件。 如果我們不太會配置這些選項(xiàng),可以去查看原生應(yīng)用的混淆配置,這對你會有很大啟發(fā)。
混淆堆棧還原
使用 android 自帶的混淆堆棧還原工具 proguardgui 解決
Android/Sdk/tools/proguard/bin$ls proguard.shproguardgui.shretrace.sh /Android/Sdk/tools/proguard/bin$./proguardgui.sh
腳本運(yùn)行后會打開 ProGuard 調(diào)試界面
proguard gui tools
填寫混淆前后的映射文件 mapping.txt 和需要還原的 logcat 異常日志,點(diǎn)擊 Retrace 按鈕就能還原混淆前的異常信息
結(jié)論
總之,一些通用混淆規(guī)則Android已經(jīng)幫我寫好了,我們關(guān)心的可能就是我們自己開發(fā)的那部分代碼的混淆規(guī)則,混淆工作做的好有很多好處: 代碼更安全、包體積更小、運(yùn)行速度更快,但是混淆也會帶來意想不到的運(yùn)行異常,希望你做好測試,希望你盡快用起來。
審核編輯:湯梓紅
-
Android
+關(guān)注
關(guān)注
12文章
3935瀏覽量
127339 -
JAVA
+關(guān)注
關(guān)注
19文章
2966瀏覽量
104702 -
代碼
+關(guān)注
關(guān)注
30文章
4779瀏覽量
68521 -
編譯
+關(guān)注
關(guān)注
0文章
657瀏覽量
32852
原文標(biāo)題:Android編譯優(yōu)化之混淆配置
文章出處:【微信號:哆啦安全,微信公眾號:哆啦安全】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論