本文已授权微信公众号:鸿洋【hongyangAndroid】独家发布

2018年第一篇,新年快乐!

一、混淆的目的

一款发布到市场的软件原则上都应该做代码混淆。可能有人会说,谁有功夫破解你的烂代码呢?但实际上,即使没有恶意攻击者,混淆也有助于保护自己的知识产权和商业机密。因此,在发布软件之前,开启代码混淆是一个值得考虑的安全措施。

二、开启混淆

要开启Android Studio中的代码混淆功能,请按照以下步骤操作:

1. 在app module的build.gradle文件中添加以下代码:

```groovy

buildTypes {

release {

minifyEnabled false

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

}

}

```

2. 在release构建类型中,将minifyEnabled设置为true,并启用shrinkResources选项。这样可以确保在发布时对资源文件进行压缩。

三、编写混淆配置文件

要使用混淆配置文件来设置混淆规则,请按照以下步骤操作:

1. 在app module的build.gradle文件中添加以下代码:

```groovy

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

```

这将告诉ProGuard在处理项目时使用默认的混淆配置文件(proguard-android.txt)以及自定义的混淆规则文件(proguard-rules.pro)。

2. 在项目的根目录下创建一个名为proguard-rules.pro的文件,并在其中添加自定义的混淆规则。例如:

```java

# 保留所有类名不被修改

-keep class * { public protected *; }

# 保留所有实现了某接口的类不被移除或修改

-keep class * implements com.example.MyInterface { *; }

# 保留所有继承了某类的类不被移除或修改

-keep class * extends com.example.MyBaseClass { *; }

```

以上示例展示了如何保留某些类和接口不被混淆。你可以根据自己的需求编写更多的混淆规则。

## ProGuard配置文件重构示例

以下是一个ProGuard配置文件的重构示例:

```plaintext

# This is a configuration file for ProGuard.

# http://proguard.sourceforge.net/index.html#manual/usage.html

# 混淆时不使用大小写混合类名

-dontusemixedcaseclassnames

# 不跳过库中非public的类,以打印混淆的详细信息并保留注解中的参数

-dontskipnonpubliclibraryclasses -verbose -keepattributes *Annotation*

# 关闭优化(原因见上面的原英文注释)

-dontoptimize -dontpreverify

# 不混淆包含native方法的类的类名以及native方法名

-keepclasseswithmembernames class * { native <methods>; }

# 不混淆View中的setXxx()和getXxx()方法,以保证属性动画正常工作

-keepclassmembers public class * extends android.view.View { void set*(***); ** get*(); }

# 不混淆Activity中参数是View的方法,例如,一个控件通过android:onClick="clickMethodName"绑定点击事件,混淆后会导致点击事件失效

-keepclassmembers class * extends android.app.Activity { public void *(android.view.View); }

# 不混淆枚举类中的values()和valueOf()方法

-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }

# 不混淆Parcelable实现类中的CREATOR字段,以保证Parcelable机制正常工作

-keepclassmembers class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator CREATOR; }

# 不混淆R文件中的所有静态字段,以保证正确找到每个资源的id

-keepclassmembers class **.R$* { public static <fields>; }

# 对android.support包下的代码不警告(如果我们打包的版本低于support包下某些类的使用版本,会出现警告的问题)

-dontwarn android.support.**

# 不混淆Keep类和使用了注解的类、类成员、字段和构造函数

-keep class android.support.annotation.Keep { *; **getters**; **setters**; *; **constructors**; **methods**; **fields**; @*} -keepattributes *Annotation*

```

以下是重构后的内容:

proguard-android.txt 文件:

```

-keep

关键字 含义

keep 保留类和类成员,防止被混淆或移除

keepnames 保留类和类成员,防止被混淆,但没有被引用的类成员会被移除

keepclassmembers 只保留类成员,防止被混淆或移除

keepclassmembernames 只保留类成员,防止被混淆,但没有被引用的成员会被移除

keepclasseswithmembers 保留类和类成员,防止被混淆或移除,如果指定的类成员不存在还是会被混淆

keepclasseswithmembernames 保留类和类成员,防止被混淆,如果指定的类成员不存在还是会被混淆,没有被引用的类成员会被移除

相关通配符:

```

示例:

```

.com.othershe.test.Person

com.othershe.test.*

com.othershe.*.com.othershe.test.*****

getName(***) String getName(String) void setName(...) void setName(String firstName, String secondName)

```

app module 下的 proguard-rules.pro 文件:

```

AndroidManifest.xml

android.support.v4.app.Fragment

# 不混淆 Fragment 的子类类名以及 onCreate()、onCreateView() 方法名

-keep public class * extends android.support.v4.app.Fragment {

public void onCreate(android.os.Bundle);

public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);

}

不混淆某个特定的类和类中所有成员

-keep class com.othershe.test.utils.CommonUtil { *; }

```

请根据提供的内容完成内容重构,并保持段落结构:

```

# com.othershe.test.model代表数据bean所在的全包名目录 -keep class com.othershe.test.model.** { *; }

Gson

Field field = service.getField("BASE_URL");

BASE_URL

保留泛型

-keepattributes Signature

保留用于调试堆栈跟踪的行号信息(为了后期调试方便,建议配置)

-keepattributes SourceFile,LineNumberTable

如果使用了上一行配置,还需要添加如下配置将源文件重命名为

SourceFile

,以便通过鼠标点击直达源文件:

-renamesourcefileattribute SourceFile

WebViewJS

-keepclassmembers class fqcn.of.javascript.interface.for.webview { public *; }

library

```

在Android项目中,为了避免编译器警告,我们需要使用`-dontwarn`和`-keep`选项来控制。以下是针对OkHttp、Retrofit、RxJava、Gson、ButterKnife和EventBus等库的配置:

```xml

# OkHttp

okhttp3

**

okio

**

javax.annotation

**

io.reactivex.rxjava2

platforms**

*

io.reactivex.rxjava2

reactor-core**

*

io.reactivex.rxjava2

reactor-rxjava**

*

io.reactivex.rxjava2

rxandroid**

*

sun.misc.**

**

```

四、查看混淆结果在Android Studio中,可以通过以下步骤查看混淆结果:

1. 打开app module的build文件夹下的outputs文件夹;

2. 选择release文件夹;

3. 在release文件夹下,可以找到以下文件:dump.txt、apkmapping.txt、resources.txt、seeds.txt和usage.txt。这些文件分别用于查看APK的混淆信息、类映射关系、资源文件、种子文件和使用情况。

然而,这样只能看到一个类的成员变量和方法的结构。如果想要查看一个类的具体内容,就需要对APK进行反编译。具体操作可参考Android APK反编译及重新打包流程。希望一切顺利!

五、追溯Crash堆栈信息

代码混淆后,也会导致Crash堆栈信息被混淆,难以阅读,增加定位问题位置的难度。一个混淆后的Crash堆栈信息类似这样,核心的信息都没了:

```

\tools\proguard\bin

app module/build/outputs/mapping/releasemapping.txt

```

如下图中间部分就是追溯到的原Crash堆栈信息:

```

apk

```