更多信息请访问:与华为官方共同建立的鸿蒙技术社区https://harmonyos.51cto.com1.前言Java代码会被编译成字节码,字节码非常容易被反编译,一旦字节码被反编译,源代码就会泄露。为了很好地保护源代码,需要对编译后的字节码文件进行混淆处理。代码经过混淆处理后,包体积会变小,并且源代码经过处理,进一步保证了应用的安全性。本文将首先介绍混淆原理和混淆命令,然后教大家如何在鸿蒙项目中配置混淆。2.ProGuardProGuard是用来混淆代码的,主要有以下四个功能。收缩:检测并删除代码中未使用的类、字段、方法和属性。可以使用以下命令关闭压缩#CloseCompression-dontshrink优化(Optimize):优化字节码并删除无用的指令。可以使用如下命令关闭优化#turnoffoptimization-dontoptimize-optimizationpassesn表示proguard对代码进行迭代优化的次数。重命名。可以使用如下命令关闭混淆#Closeobfuscation-dontobfuscate预检查(Preveirfy):在Java平台上对处理后的代码进行预检查,确保加载的字节码文件是可执行的。简而言之,Proguard是一个Java类文件压缩器、优化器、混淆器和预验证器。缩小阶段检测并删除未使用的类、字段、方法和属性。优化部分对方法的字节码进行分析和优化。混淆过程使用无意义的短变量重命名类、变量和方法。这些步骤使代码更精简、更高效并且更难逆向(破解)。那么就有一个问题,ProGuard怎么知道这段代码没有被使用过呢?这里引入一个EntryPoint的概念。入口点表示在混淆过程中不会处理的类或方法。在压缩步骤中,ProGuard会从上面的EntryPoint开始递归遍历,查找正在使用的类和类成员。对于未使用的类和类成员,它们将在压缩部分中被丢弃。在接下来的优化过程中,那些非EntryPoint的类和方法会被设置为private、static或final,没有使用的参数会被移除。此外,一些方法将被标记为内联。在混淆步骤中,ProGuard将重命名非入口点类和方法。一般来说,开启混淆后,代码越乱越不规则越好,但是有些代码是不能混淆的,否则程序会运行错误,所以我们需要熟悉混淆指令。启用混淆后,使用混淆指令告诉编译器某些代码不能被混淆。3、混淆指令3.1先看下面的指令。星号表示只保留包下的类名,子包下的类名还是会混淆。-keepclasscom.poetry.jianjia.bean.*3,2两个星号表示保留本包及其子包下的类名。-keepclasscom.poetry.jianjia.bean.**3,3使用上面的方法保留类后,虽然类名没有混淆,但是类中的方法名和变量名还是会发生变化。如果想保持类名和里面的内容不混淆的话,需要加上{*;}-keepclasscom.poetry.jianjia.bean.**{*;}3,4在此基础上,可以同样使用extends、implements等关键字来保护具体的类不要混淆,下面的例子说明实现了Serializable接口的类名不要混淆-keepclass*implementsjava.io.Serializable3,5如果想保留内部为了避免混淆,您需要使用$符号,如以下示例所示。内容没有混淆。-keepclassmembersclasscom.poetry.jianjia.slice.MainAbilitySlice$InnerClass{public*;}3,6如果只是想让类中的具体内容不混淆,可以使用;//匹配所有构造方法;//匹配所有变量;//匹配所有方法3,7可以在or前面加上private,public,native等进一步指定不要混淆的内容,如下例如,Banner类中的所有公共方法都不要混淆。-keepclasscom.poetry.jianjia.Banner{public;}3类和8类可以有重载的方法,如果想要重载的方法不混淆,可以添加方法参数,如下例,withacharacterstring参数的构造方法不混淆-keepclasscom.poetry.jianjia.Banner{public(java.lang.String);}3,9有时类名可以混淆,但希望类下的具体方法不会混淆,那就不能用keep,keep不会混淆类名,但是需要用keepclassmembers。在下面的例子中,实现Serializable接口的类名可以混淆,但类中具体的变量和方法不要混淆(java.io.ObjectOutputStream);privatevoidreadObject(java.io.ObjectInputStream);java.lang.ObjectwriteReplace();java.lang.ObjectreadResolve();}3、10部分类或类成员不能重命名,keepclasseswithmembernames会阻止类和成员被重命名。如下例所示,本地方法无法重命名。-keepclasseswithmembernamesclass*{native;}3,11防止枚举混淆-keepclassmembersenum*{publicstatic**[]values();publicstatic**valueOf(java.lang.String);}3,12用于反射到不能混淆的类。3.13配置文件中的类不能混淆。配置文件中声明的Ability默认不会混淆。配置文件中声明的类不需要额外的配置混淆。3.14使用gson、fastjson等框架解析服务端数据时,写的json对象类不能混淆,否则无法将json解析成相应的对象。3.15第三方开源库大量使用注解、反射、泛型。在使用第三方开源库或引用其他第三方SDK包时,如有特殊需求,需在混淆文件中添加相应的混淆规则。4、为鸿蒙项目配置混淆4.1混淆指令我们已经很熟悉了,那么如何为鸿蒙项目配置混淆呢?在最新版本的编译器中创建一个工程,编译器会帮我们创建一个proguard-rules.pro文件。proguard-rules.pro文件是什么?鸿蒙使用proguard进行混淆。proguard-rules.pro文件用于配置混淆规则,不可混淆的代码在proguard-rules.pro文件中配置。请注意,旧版本的编译器不支持混淆。如果使用老版本编译器创建工程,编译器不会创建proguard-rules.pro文件。4.2编译器除了帮我们创建proguard-rules.pro文件外,还会在build.gradle文件中加入新的代码。打开build.gradle文件。编译器在buildTypes闭包中添加了一个release闭包,release表示官方包。release闭包下面还有一个proguardOpt闭包,proguardOpt是用来配置混淆的。proguardEnabled表示是否开启混淆,true表示开启混淆,false表示不开启混淆。rulesFiles表示配置混淆的规则文件。可以看出,默认情况下,混淆是没有开启的。出于保护源码的考虑,我们在打包官方包的时候,需要开启混淆功能。其实我们也可以为测试包配置混淆,如下代码所示。我们手动添加了一个debug闭包,debug是指测试包,不要在测试包中开启混淆,当你在测试包中开启混淆时,断点调试时是看不到变量的值的。buildTypes{//release表示官方包release{//configureconfusionproguardOpt{//官方包enableconfusionproguardEnabledtrue//混淆规则在proguard-rules.pro文件中配置rulesFiles'proguard-rules.pro'}}//debug表示测试包debug{//配置混淆proguardOpt{//测试包不开启混淆proguardEnabledfalse//混淆规则配置在proguard-rules.pro文件rulesFiles'proguard-rules.pro'}}}4,3如果使用最新版本编译如果编译器打开老版本编译器创建的工程,那么工程中不会有proguard-rules.pro文件,build.gradle文件中也不会有proguardOpt.这时候我们需要手动创建proguard-rules.pro文件,将上面的代码添加到build.gradle文件中。4、4综上所述,鸿蒙项目如何配置混淆?只有两步,首先将proguardEnabled设置为true,其次使用proguard-rules.pro文件中的混淆指令配置混淆规则。5.给出一个普通的混淆配置#代码混淆压缩比,在0~7-optimizationpasses5之间#混合时不要使用大小写混合,混合的类名是lowercase-dontusemixedcaseclassnames#指定不忽略非publiclibrariesClass-dontskipnonpubliclibraryclasses#Do不做pre-verification,preverify是proguard的四步之一,去掉这一步可以加速混淆。-dontpreverify-verbose#避免混淆泛型-keeattributesSignature#google推荐算法-optimizations!code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*#retainannotations,internalclasses,genericType,anonymousclass-keepattributes*Annotation*,InnerClasses,Signature,EnclosingMethod#抛出异常时重命名文件名-renamesourcefileattributeSourceFile#抛出异常时保留代码行号-keeattributesSourceFile,LineNumberTable-dontwarnjavax.annotation.**#ReservelocalNative方法不混淆-keepclasseswithmembernamesclass*{native;}#Reserve枚举类不混淆-keepclassmembersenum*{publicstatic**[]values();publicstatic**valueOf(java.lang.String);}-keepclassmembersclass*implementsjava.io.Serializable{staticfinallongserialVersionUID;privatestaticfinaljava.io.ObjectStreamField[]serialPersistentFields;privatevoidwriteObject(java.io.ObjectOutputStream);privatevoidreadObject(java.io.ObjectInputStream);java.lang.ObjectwriteReplaceang();();}#OkHttp3-dontwarnokhttp3.logging.**-keepclassokhttp3.internal.**{*;}-dontwarnokio.**#gson-keepclasssun.misc.Unsafe{*;}-keepclasscom.google.gson.stream.**{*;}#在我的示例代码中,com.poetry.jianjia.bean包下的类实现了Serialized接口,#实现Serialized接口不能混淆。请将包名com.poetry.jianjia.bean替换成自己的包名-keepclasscom.poetry.jianjia.bean.**{*;}6.总结本文主要介绍混淆原理和混淆命令,并教大家如何在鸿蒙项目中配置混淆。您最好熟悉混淆说明。在实际开发中,经常会遇到这种问题。测试包没有问题,但是官方包里面有问题。这往往是因为官方包中开启了混淆,但是proguard-rules.pro文件中并没有配置混淆规则。更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区