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浏览量
127333 -
JAVA
+关注
关注
19文章
2966浏览量
104700 -
代码
+关注
关注
30文章
4779浏览量
68516 -
编译
+关注
关注
0文章
657浏览量
32851
原文标题:Android编译优化之混淆配置
文章出处:【微信号:哆啦安全,微信公众号:哆啦安全】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论