混淆的开启和配置

在Android开发中,为了保护应用的代码不被轻易逆向分析,开发者通常会使用混淆工具对代码进行混淆。混淆的主要目的是将代码中的类名、方法名等信息替换为无意义的名称,从而使得反编译后的代码难以阅读和理解。本文将介绍如何开启混淆以及常见的混淆配置。

开启混淆

要开启混淆,需要修改项目的`build.gradle`文件。具体操作如下:

1. 打开项目的`app`目录下的`build.gradle`文件。

2. 在`android`闭包内添加以下代码:

```groovy

release {

minifyEnabled true //是否启动混淆 true:打开 false:关闭

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

```

这样就完成了混淆的开启。

proguard-rules.pro文件的作用就是配置混淆规则。接下来我们来了解一下Proguard是什么以及它的常见配置。

Proguard简介

Proguard是一个集文件压缩、优化、混淆和校验等功能的工具。它可以检测并删除无用的类、变量、方法和属性,优化字节码并删除无用的指令,通过将类名、变量名和方法名重命名为无意义的名称实现混淆效果,最后还校验处理后的代码。

混淆的常见配置

在使用Proguard进行混淆时,需要注意一些关键字的使用,这些关键字对于配置混淆规则非常重要。以下是一些常用的Proguard关键字:

1. dontwarn:用于忽略警告信息,尤其是在处理引入的library时。

2. keep:保留类和类中的成员,防止被混淆或移除。

3. keepnames:保留类和类中的成员,防止被混淆,成员没有被引用会被移除。

4. keepclassmembers:只保留类中的成员,防止被混淆或移除。

5. keepclassmembernames:只保留类中的成员,防止被混淆,成员没有引用会被移除。

6. keepclasseswithmembers:保留类和类中的成员,防止被混淆或移除,保留指明的成员。

7. keepclasseswithmembernames:保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除。

示例:

1. 保留某个包下面的类以及子包:

```groovy

-keep public class com.droidyue.com.widget.** {*;}

```

2. 保留所有类中使用otto的public方法:

```groovy

# Otto -keepclassmembers class ** { @com.squareup.otto.Subscribe public *; @com.squareup.otto.Produce public *; }

```

3. 保留Contants类的BOOK_NAME属性:

```groovy

-keepclassmembers class com.example.Constants { public static final java.lang.String BOOK_NAME; }

```

在 ProGuard 配置文件中,可以使用 keepclassmembers 规则来保留指定类中的所有成员(包括字段、方法等)。例如:

```

-keepclassmembers class com.example.admin.proguardsample.Constants {

public static java.lang.String BOOK_NAME;

}

```

这段配置表示保留 com.example.admin.proguardsample.Constants 类中的所有成员,特别是 BOOK_NAME 静态字段。

4. 引入的 library 可能存在一些无法找到的引用和其他问题,在 build 时可能会发出警告。为了保证 build 继续进行,我们需要使用 dontwarn 处理这些我们无法解决的 library 的警告。例如,关闭 Twitter SDK 的警告,可以这样配置:

```

-dontwarn com.twitter.sdk.**

```

Proguard 中的通配符有以下几种用法:

1. ``:匹配类中的所有字段。

2. ``:匹配类中的所有方法。

3. ``:匹配类中的所有构造函数。

4. `*`:匹配任意长度字符,不包含包名分隔符(.)。

5. `**`:匹配任意长度字符,包含包名分隔符(.)。

6. `***`:匹配任意参数类型。

7. `...`:这个具体含义不明,可能是某种特殊情况的匹配方式。

```java

// 不混淆某个类-keep public class name.huihui.example.Test { *; }

// 不混淆某个类的子类

-keep public class * extends name.huihui.example.Test { *; }

// 不混淆所有类名中包含了“model”的类及其成员

-keep public class **.*model*.** {*;}

// 不混淆某个接口的实现

-keep class * implements name.huihui.example.TestInterface { *; }

// 不混淆某个类的构造方法

-keepclassmembers class name.huihui.example.Test {

public <init>();

}

// 不混淆某个类的特定的方法

-keepclassmembers class name.huihui.example.Test {

public void test(java.lang.String);

}

// 不混淆某个类的内部类

-keep class name.huihui.example.Test$* { *; }

// 两个常用的混淆命令,注意:

// 一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆;

// 两颗星表示把本包和所含子包下的类名都保持;

-keep class com.suchengkeji.android.ui.** -keep class com.suchengkeji.android.ui.*

```

在混淆过程中,有一些特定的类和方法不应该被混淆。以下是需要重点关注的几点:

1. 使用自定义控件时,要确保这些控件不参与混淆。这是因为自定义控件可能包含特殊的逻辑或功能,如果将其混淆,可能会影响应用程序的正常运行。

2. 使用了枚举类型的地方,要保证枚举类型不被混淆。这样可以避免在使用枚举类型时出现意料之外的问题。

3. 对于第三方库中的类,不要进行混淆。这是因为第三方库可能已经对混淆进行了处理,如果再进行混淆,可能会导致不必要的冲突和问题。

4. 运用了反射的类也不应该进行混淆。这是因为反射在很多情况下都是必要的,如果将使用反射的类混淆,可能会导致无法正常调用方法或访问属性。

5. 在使用 Gson 等工具时,要确保 JavaBean 类(即实体类)不被混淆。这样可以保持 JSON 数据与 Java 对象之间的一致性。

6. 在引用第三方库时,通常会标明库的混淆规则。建议在使用库时将其混淆规则添加到项目中,以免到最后才去找。

7. 如果使用了 WebView 进行 JS 调用,也需要确保编写的接口方法不被混淆。原因同第1条所述。

8. Parcelable 的子类和 Creator 静态成员变量不应该被混淆。否则可能会导致 Android.os.BadParcelableException 异常。

9. 在四大组件(Activity、Service、BroadcastReceiver、ContentProvider)以及自定义的 Application* 实体类中,也应该尽量避免混淆。

10. 在 JNI 中调用的类也不应该进行混淆。这是因为 JNI 调用可能涉及到底层系统资源的操作,如果将其混淆,可能会导致系统不稳定或崩溃。

11. 最后,Layout布局使用的 View 构造函数(如自定义控件)以及 android:onClick 等属性也不应该被混淆。这样可以确保布局和点击事件能够正常工作。