一、为什么代码要混淆

  • 1、日常开发Debug包时不用混淆,正式发布Release包前开启代码混淆;
  • 2、混淆好处① → 类、方法、变量名变成短且无意义的名字,提高反编译后代码的阅读成本;
  • 3、混淆好处② → 删除无用的类、方法与属性,缩减了APK包的大小;
  • 4、混淆好处③ → 对字节码进行优化,移除无用指令,应用运行更快;
  • 5、怎么混淆 → 主项目的 build.gradle 设置 minifyEnabled trueproguard-rules.pro 加入混淆规则;
  • 6、混淆规则哪里来 → 网上搜索通用混淆模板复制粘贴,项目依赖到的第三方库官方文档复制粘贴;

二、如何开启混淆

在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保留类和类中的成员,防止被混淆或者移除
keepnames保留类和类中的成员,防止被混淆,但是当成员没有被引用时会被移除
keepclassmembers只保留类中的成员,防止他们被混淆或者移除
keepclassmembersnames只保留类中的成员,防止他们被混淆或者移除,但是当类中的成员没有被引用时还是会被移除
keepclasseswithmembers保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆
keepclasseswithmembersnames保留类和类中的成员,前提是指明的类中必须含有该成员,没有的话还是会被混淆。需要注意的是没有被引用的成员会被移除

接下来我们一起来看一下通配符

关键字描述
<filed>匹配类中的所有字段
<method>匹配类中的所有方法
<init>匹配类中的所有构造函数
*匹配任意长度字符,但不含包名分隔符(.)。比如说我们的完整类名是com.example.test.MyActivity,使用com.,或者com.exmaple.都是无法匹配的,因为无法匹配包名中的分隔符,正确的匹配方式是com.exmaple..,或者com.exmaple.test.,这些都是可以的。但如果你不写任何其它内容,只有一个*,那就表示匹配所有的东西。
**匹配任意长度字符,并且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包。
***匹配任意参数类型。比如void set*()就能匹配任意传入的参数类型, get*()就能匹配任意返回值的类型。
匹配任意长度的任意类型参数。比如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 *;
}
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

#表示不混淆 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中的关键字作字典:避免混淆后与其他包重名,而且混淆之后的代码更加不利于阅读
#
# 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
#

do
if
for
int
new
try
byte
case
char
else
goto
long
this
void
break
catch
class
const
final
float
short
super
throw
while
double
import
native
public
return
static
switch
throws
boolean
default
extends
finally
package
private
abstract
continue
strictfp
volatile
interface
protected
transient
implements
instanceof
synchronized

注:filename.txt是用来指定混淆后生成的名字的字典文件,字典文件中的空格,标点符号,重复的词,还有以’#'开头的行都会被忽略。需要注意的是添加了字典并不会显著提高混淆的效果,添加字典有两个作用:一是避免与其他包混淆后重名;二是更不利与阅读;

2.2、在proguard-rules.pro文件中做如下设置:

-obfuscationdictionary filename.txt
-classobfuscationdictionary filename.txt
-packageobfuscationdictionary filename.txt

-obfuscationdictionary filename .txt:指定一个混淆类名、成员变量名、方法名的字典。
-classobfuscationdictionary filename .txt:指定一个混淆类名的字典,字典的格式与-obfuscationdictionary相同;
-packageobfuscationdictionary filename .txt:filename 指定一个混淆包名的字典,字典格式与-obfuscationdictionary相同。

上面配置好了混淆字典。

参考连接:https://blog.csdn.net/zhayunbiao/article/details/117693922

https://juejin.cn/post/6844903504801169422?searchId=20231116113200A17806C89AF4451902B5

https://blog.csdn.net/fengyulinde/article/details/106115757/

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐