在Android开发中,混淆是一种保护代码的方式。它可以将代码转换为难以阅读和理解的形式,从而防止他人轻易地反编译您的应用程序。如果您想开启混淆,您需要在app层下找到build.gradle文件,找到buildTypes下的release设置minifyEnabled为true即可 。proguard-rules.pro为混淆规则文件 。
optimizationpasses 5 # 代码混淆的压缩比例,值介于0-7,默认5
-verbose # 混淆时记录日志
-dontoptimize # 不优化输入的类文件
-dontshrink # 关闭压缩
-dontpreverify # 关闭预校验(作用于Java平台,Android不需要,去掉可加快混淆)
-dontoptimize # 关闭代码优化
-dontobfuscate # 关闭混淆
-ignorewarnings # 忽略警告
-dontwarn com.squareup.okhttp.** # 指定类不输出警告信息
-dontusemixedcaseclassnames # 混淆后类型都为小写
-dontskipnonpubliclibraryclasses # 不跳过非公共的库的类
-printmapping mapping.txt # 生成原类名与混淆后类名的映射文件mapping.txt
-useuniqueclassmembernames # 把混淆类中的方法名也混淆
-allowaccessmodification # 优化时允许访问并修改有修饰符的类及类的成员
-renamesourcefileattribute SourceFile # 将源码中有意义的类名转换成SourceFile,用于混淆具体崩溃代码
-keepattributes SourceFile,LineNumberTable # 保留行号
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod # 避免混淆注解、内部类、泛型、匿名类
-optimizations !code/simplification/cast,!field/ ,!class/merging/ # 指定混淆时采用的算法
首先,我们来了解一下 keep 关键字。keep 关键字有以下几种形式:
1. keep:保留类和类中的成员,防止被混淆或者移除。
2. keepnames:保留类和类中的成员,防止被混淆,但是当成员没有被引用时会被移除。
3. keepclassmembers:只保留类中的成员,防止他们被混淆或者移除。
4. keepclassmembersnames:只保留类中的成员,防止他们被混淆或者移除,但是当类中的成员没有被引用时还是会被移除。
5. keepclasseswithmembers:保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆。
6. keepclasseswithmembersnames:保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆。需要注意的是没有被引用的成员会被移除。
接下来,我们来看看通配符。通配符有以下几种形式:
1. `
2. `
3. `
4. `*`:匹配任意长度字符,但不含包名分隔符(.)。例如,完整类名是 com.example.test.MyActivity,使用 com.、com.example. 都是无法匹配的,因为 * 无法匹配包名中的分隔符。正确的匹配方式是 com.example. 或 com.example.test.。如果你不写任何其他内容,只有一个 *,那就表示匹配所有的东西。
5. `**`:匹配任意长度字符,并且包含包名分隔符(.)。例如 proguard-android.txt 中使用的 -dontwarn android.support.** 可以匹配 android.support 包下的所有内容,包括任意长度的子包。
6. `***`:匹配任意参数类型。例如 void set*(
) 可以匹配任意传入的参数类型,get*() 可以匹配任意返回值的类型。
7. `...`:匹配任意长度的任意类型参数。例如 void test(...) 可以匹配任意 void test(String a) 或者是 void test(int a, String b) 这些方法。
下面是我使用的混淆模板,供大家参考:
```
# 混淆后类型都为小写-dontusemixedcaseclassnames
# 表示不跳过library中的非public的类
-dontskipnonpubliclibraryclasses
# 混淆时记录日志
-verbose
#表示不进行优化,建议使用此选项
-dontoptimize
#表示不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度
-dontpreverify
# 避免混淆注解、内部类、泛型、匿名类
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
#保持所有类的 native 方法不被混淆
-keepclasseswithmembernames class * { native <methods>; }
#表示不混淆 View 的子类中的 set 和 get 方法,因为 View 中的属性动画需要 setter 和 getter,混淆了就无法工作了
-keepclassmembers public class * extends android.view.View { void set*(***); *** get*(); }
#表示不混淆 Activity 及 Activity 子类中方法参数是 (android.view.View) 的方法
-keepclassmembers class * extends android.app.Activity { public void *(android.view.View); }
#表示不对美剧类中的 calues 和 valueOf(java.lang.String) 进行混淆
-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
#表示不混淆 Parcelable 的实现类中的 CREATOR,我们知道序列化与反序列化的过程都需要 CREATOR, 混淆了就无法工作了
-keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; }
#表示不混淆 R 类中 的 static 变量,在 R 类中。这些资源 ID 是系统自动帮我们生成的,混淆了就无法找到相应的资源。
-keepclassmembers class **.R$* { public static <fields>; }
#表示混略 android.support 包下代码的警告
-dontwarn android.support.**
#表示不混淆 Keep 类
-keep class androidx.annotation.Keep
#添加自定义混淆规则,以及第三方库的混淆规则
-keep class com.kl.tdoalocalization.mqtt.MqttManager{ public <methods>; public <fields>; }
```
四、混淆字典的使用
1. 为什么要讲混淆字典?
在开发Flutter插件时,可能会遇到需要使用自己开发的Android原生SDK的情况。然而,在项目编译过程中,可能会出现如下异常:
```
原因:红色框标注的两个SDK发生了冲突,但是这两个SDK的包名和类名都不一样。怎么会冲突呢?
解决方法:通过jd-gui工具发现,两个SDK都是被混淆后的,混淆后的包名和类名都变为a、b、c这样的字符,导致两个SDK都有同样的a.a.a.a类,从而产生了冲突。此时就需要引入混淆字典来自定义混淆后的字符,不使用a、b、c这种默认字符。
```
2. 如何使用混淆字典?
2.1 在proguard-rules.pro同级目录中创建一个filename.txt混淆字典文件。
下面是字典的示例,建议可在每个关键字之前加上一些特殊的前缀,例如:
```
xxdo
xxif
```
在Java项目中,为了避免与Java关键字冲突,可以使用混淆字典(obfuscation dictionary)来指定混淆后生成的名字。以下是一个包含常用Java关键字的混淆字典示例:
```plaintext
# This obfuscation dictionary contains reserved Java keywords. They can't
# be used in Java source files, but they can be used in compiled class files.
# Note that this hardly improves the obfuscation. Decent decompilers can
# automatically replace reserved keywords, and the effect can fairly simply be
# undone by obfuscating again with simpler names.
Usage: java -jar proguard.jar ..... -obfuscationdictionary filename.txt
```
其中,`filename.txt` 是用来指定混淆后生成的名字的字典文件。该文件中的空格、标点符号、重复的词以及以 '#' 开头的行都会被忽略。需要注意的是,添加了字典并不会显著提高混淆的效果,它主要有两个作用:一是避免与其他包混淆后重名;二是更不利于阅读。
要在proguard-rules.pro文件中使用自定义的混淆字典,可以按照以下步骤进行设置:
1. 在proguard-rules.pro文件中添加以下配置语句:
```plaintext
-keep public class com.example.** { *; }
-keepclassmembers class com.example.** { *; }
```
这里的`com.example`是你的包名。上述配置语句的作用是保留你的包及其下的所有成员类和成员变量不被混淆。
2. 在proguard-rules.pro文件的末尾添加以下配置语句:
```plaintext
-dontoptimize
-keep public class ** { *; }
```
这两个配置语句的作用是禁用代码优化并保留所有公共类及其成员不被混淆。这样可以确保混淆器不会替换这些公共类和成员。
3. 在proguard-rules.pro文件的末尾添加以下配置语句:
```plaintext
-obfuscationdictionary filename.txt
```
将上述配置语句中的`filename.txt`替换为你实际使用的混淆字典文件的路径。这将告诉混淆器使用指定的字典文件进行混淆操作。
完成以上设置后,你可以运行ProGuard对Java项目进行混淆。混淆后的代码将使用字典中指定的名字替换关键字,从而避免与Java关键字冲突并增加代码的可读性。
以下是重构后的内容:
混淆字典配置:
在进行混淆操作之前,需要为混淆过程指定一些混淆字典。这些字典用于指定如何混淆特定的类名、成员变量名和方法名。以下是如何使用这些选项来指定混淆字典的示例:
1. 指定一个混淆类名、成员变量名、方法名的字典:
```bash
-obfuscationdictionary filename.txt -classobfuscationdictionary filename.txt -packageobfuscationdictionary filename.txt
```
2. 指定一个混淆类名的字典:
```bash
-classobfuscationdictionary filename.txt
```
在这种情况下,字典的格式应与 `-obfuscationdictionary` 相同。
3. 指定一个混淆包名的字典:
```bash
-packageobfuscationdictionary filename.txt
```
在这种情况下,`filename` 指定一个混淆包名的字典,字典格式应与 `-obfuscationdictionary` 相同。
通过以上配置,我们已经设置好了混淆字典。