蹉跎莫遣韶光老,人生唯有读书好。这篇文章主要讲述Android开发者的混淆使用手册相关的知识,希望能为你提供帮助。
综述 毫无疑问,
混淆是打包过程中最重要的流程之一,
在没有特殊原因的情况下,
所有 app 都应该开启混淆。
首先,
这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,
混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,
这有助于规避64K方法数的瓶颈;
同时,
将类、类成员、方法重命名为无意义的简短名称,
增加了逆向工程的难度。而依靠 Gradle 的 Android 插件,
我们将移除未被使用的资源,
可以有效减小 apk 安装包大小。
本文由两部分构成,
第一部分给出混淆的最佳实践,
力求让零基础的新手都可以直接使用混淆;
第二部分会介绍一下混淆的整体、自定义混淆规则的语法与实践、自定义资源保持的规则等。
一、android混淆最佳实践
1. 混淆配置
一般情况下,
app module 的 build.gradle
文件默认会有如下结构:
< code class= " hljs bash has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > android { buildTypes { release { minifyEnabled < span class= " hljs-literal" style= " color: rgb(0, 102, 102); box-sizing: border-box; " > false< /span> proguardFiles getDefaultProguardFile(< span class= " hljs-string" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > ' proguard-android.txt' < /span> ), < span class= " hljs-string" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > ' proguard-rules.pro' < /span> } } }< /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 4< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 5< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 6< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 7< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 8< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 9< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 10< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 11< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 4< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 5< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 6< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 7< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 8< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 9< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 10< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 11< /li> < /ul>
因为开启混淆会使编译时间变长, 所以
debug
模式下不应该开启。我们需要做的是:
1. 将
release
下minifyEnabled
的值改为true
,
打开混淆;
2. 加上
shrinkResources true
,
打开资源压缩。修改后文件内容如下:
< code class= " hljs bash has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > android { buildTypes { release { minifyEnabled < span class= " hljs-literal" style= " color: rgb(0, 102, 102); box-sizing: border-box; " > true< /span> shrinkResources < span class= " hljs-literal" style= " color: rgb(0, 102, 102); box-sizing: border-box; " > true< /span> proguardFiles getDefaultProguardFile(< span class= " hljs-string" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > ' proguard-android.txt' < /span> ), < span class= " hljs-string" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > ' proguard-rules.pro' < /span> } } } < /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 4< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 5< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 6< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 7< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 8< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 9< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 10< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 4< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 5< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 6< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 7< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 8< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 9< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 10< /li> < /ul>
2. 自定义混淆规则
在
app module
下默认生成了项目的自定义混淆规则文件 proguard-rules.pro
,
多方调研后,
一份适用于大部分项目的混淆规则最佳实践如下:
< code class= " hljs haml has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > #指定压缩级别 -< span class= " ruby" style= " box-sizing: border-box; " > optimizationpasses < span class= " hljs-number" style= " color: rgb(0, 102, 102); box-sizing: border-box; " > 5< /span> < /span> #不跳过非公共的库的类成员 -< span class= " ruby" style= " box-sizing: border-box; " > dontskipnonpubliclibraryclassmembers < /span> #混淆时采用的算法 -< span class= " ruby" style= " box-sizing: border-box; " > optimizations !code/simplification/arithmetic,!field/*,!< span class= " hljs-class" style= " box-sizing: border-box; " > < span class= " hljs-keyword" style= " color: rgb(0, 0, 136); box-sizing: border-box; " > class< /span> /< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > merging< /span> /*< /span> < /span> #把混淆类中的方法名也混淆了 -< span class= " ruby" style= " box-sizing: border-box; " > useuniqueclassmembernames < /span> #优化时允许访问并修改有修饰符的类和类的成员 -< span class= " ruby" style= " box-sizing: border-box; " > allowaccessmodification < /span> #将文件来源重命名为“SourceFile”字符串 -< span class= " ruby" style= " box-sizing: border-box; " > renamesourcefileattribute < span class= " hljs-constant" style= " box-sizing: border-box; " > SourceFile< /span> < /span> #保留行号 -< span class= " ruby" style= " box-sizing: border-box; " > keepattributes < span class= " hljs-constant" style= " box-sizing: border-box; " > SourceFile< /span> ,< span class= " hljs-constant" style= " box-sizing: border-box; " > LineNumberTable< /span> < /span> #保持所有实现 Serializable 接口的类成员 -< span class= " ruby" style= " box-sizing: border-box; " > keepclassmembers < span class= " hljs-class" style= " box-sizing: border-box; " > < span class= " hljs-keyword" style= " color: rgb(0, 0, 136); box-sizing: border-box; " > class< /span> * < span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > implements< /span> < span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > java< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > io< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > Serializable< /span> {< /span> < /span> 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(); }#Fragment不需要在AndroidManifest.xml中注册, 需要额外保护下 -< span class= " ruby" style= " box-sizing: border-box; " > keep public < span class= " hljs-class" style= " box-sizing: border-box; " > < span class= " hljs-keyword" style= " color: rgb(0, 0, 136); box-sizing: border-box; " > class< /span> * < span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > extends< /span> < span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > android< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > support< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > v4< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > app< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > Fragment< /span> < /span> < /span> -< span class= " ruby" style= " box-sizing: border-box; " > keep public < span class= " hljs-class" style= " box-sizing: border-box; " > < span class= " hljs-keyword" style= " color: rgb(0, 0, 136); box-sizing: border-box; " > class< /span> * < span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > extends< /span> < span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > android< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > app< /span> .< span class= " hljs-title" style= " box-sizing: border-box; color: rgb(102, 0, 102); " > Fragment< /span> < /span> < /span> # 保持测试相关的代码 -< span class= " ruby" style= " box-sizing: border-box; " > dontnote junit.framework.** < /span> -< span class= " ruby" style= " box-sizing: border-box; " > dontnote junit.runner.** < /span> -< span class= " ruby" style= " box-sizing: border-box; " > dontwarn android.test.** < /span> -< span class= " ruby" style= " box-sizing: border-box; " > dontwarn android.support.test.** < /span> -< span class= " ruby" style= " box-sizing: border-box; " > dontwarn org.junit.** < /span> < /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 4< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 5< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 6< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 7< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 8< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 9< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 10< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 11< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 12< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 13< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 14< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 15< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 16< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 17< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 18< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 19< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 20< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 21< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 22< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 23< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 24< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 25< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 26< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 27< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 28< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 29< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 30< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 31< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 32< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 33< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 34< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 35< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 36< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 37< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 38< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 39< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 40< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 41< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 4< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 5< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 6< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 7< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 8< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 9< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 10< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 11< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 12< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 13< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 14< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 15< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 16< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 17< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 18< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 19< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 20< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 21< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 22< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 23< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 24< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 25< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 26< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 27< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 28< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 29< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 30< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 31< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 32< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 33< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 34< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 35< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 36< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 37< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 38< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 39< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 40< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 41< /li> < /ul>
真正通用的、需要添加的就是上面这些, 除此之外, 需要每个项目根据自身的需求添加一些混淆规则:
- 第三方库所需的混淆规则。正规的第三方库一般都会在接入文档中写好所需混淆规则, 使用时注意添加。
- 在运行时动态改变的代码, 例如反射。比较典型的例子就是会与 json 相互转换的实体类。假如项目命名规范要求实体类都要放在
model
包下的话,
可以添加类似这样的代码把所有实体类都保持住:
-keep public class **.*Model*.** {*;
}
-
JNI
中调用的类。 -
WebView
中JavaScript
调用的方法 -
Layout
布局使用的View
构造函数、android:onClick
等。3. 检查混淆结果
混淆过的包必须进行检查, 避免因混淆引入的bug。
一方面, 需要从代码层面检查。使用上文的配置进行混淆打包后在
<
module-name>
/build/outputs/mapping/release/
目录下会输出以下文件:
-
dump.txt
描述APK文件中所有类的内部结构
-
mapping.txt
提供混淆前后类、方法、类成员等的对照表
-
seeds.txt
列出没有被混淆的类和成员
-
usage.txt
列出被移除的代码
我们可以根据
seeds.txt
文件检查未被混淆的类和成员中是否已包含所有期望保留的,
再根据 usage.txt
文件查看是否有被误移除的代码。另一方面, 需要从测试方面检查。将混淆过的包进行全方面测试, 检查是否有 bug 产生。
4. 解出混淆栈
混淆后的类、方法名等等难以阅读, 这固然会增加逆向工程的难度, 但对追踪线上 crash 也造成了阻碍。我们拿到 crash 的堆栈信息后会发现很难定位, 这时需要将混淆反解。
在
<
sdk-root>
/tools/proguard/
路径下有附带的的反解工具(
Window 系统为 proguardgui.bat
,
Mac 或 Linux 系统为 proguardgui.sh
)
。这里以 Window 平台为例。双击运行
proguardgui.bat
后,
可以看到左侧的一行菜单。点击 ReTrace
,
选择该混淆包对应的 mapping 文件(
混淆后在 <
module-name>
/build/outputs/mapping/release/
路径下会生成 mapping.txt
文件,
它的作用是提供混淆前后类、方法、类成员等的对照表)
,
再将 crash 的 stack trace
黏贴进输入框中,
点击右下角的 ReTrace
,
混淆后的堆栈信息就显示出来了。以上使用 GUI 程序进行操作, 另一种方式是利用该路径下的
retrace
工具通过命令行进行反解,
命令是< code class= " hljs avrasm has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > retrace< span class= " hljs-preprocessor" style= " color: rgb(68, 68, 68); box-sizing: border-box; " > .bat< /span> |retrace< span class= " hljs-preprocessor" style= " color: rgb(68, 68, 68); box-sizing: border-box; " > .sh< /span> [-verbose] mapping< span class= " hljs-preprocessor" style= " color: rgb(68, 68, 68); box-sizing: border-box; " > .txt< /span> [< stacktrace_file> ]< /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul>
例如:
< code class= " hljs avrasm has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > retrace< span class= " hljs-preprocessor" style= " color: rgb(68, 68, 68); box-sizing: border-box; " > .bat< /span> -verbose mapping< span class= " hljs-preprocessor" style= " color: rgb(68, 68, 68); box-sizing: border-box; " > .txt< /span> obfuscated_trace< span class= " hljs-preprocessor" style= " color: rgb(68, 68, 68); box-sizing: border-box; " > .txt< /span> < /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul>
注意事项:
1) 所有在
AndroidManifest.xml
涉及到的类已经自动被保持,
因此不用特意去添加这块混淆规则。(
很多老的混淆文件里会加,
现在已经没必要)
2)
proguard-android.txt
已经存在一些默认混淆规则,
没必要在 proguard-rules.pro
重复添加,
该文件具体规则见附录1:
二、混淆简介 Android中的“混淆”可以分为两部分, 一部分是 Java 代码的优化与混淆, 依靠 proguard 混淆器来实现; 另一部分是资源压缩, 将移除项目及依赖的库中未被使用的资源(资源压缩严格意义上跟混淆没啥关系, 但一般我们都会放一起讲)。
1. 代码压缩
文章图片
代码混淆是包含了代码压缩、优化、混淆等一系列行为的过程。如上图所示, 混淆过程会有如下几个功能:
1. 压缩。移除无效的类、类成员、方法、属性等;
2. 优化。分析和优化方法的二进制代码; 根据proguard-android-optimize.txt中的描述, 优化可能会造成一些潜在风险, 不能保证在所有版本的Dalvik上都正常运行。
3. 混淆。把类名、属性名、方法名替换为简短且无意义的名称;
4. 预校验。添加预校验信息。这个预校验是作用在Java平台上的, Android平台上不需要这项功能, 去掉之后还可以加快混淆速度。
这四个流程默认开启。
在 Android 项目中我们可以选择将“优化”和“预校验”关闭, 对应命令是
-dontoptimize
、-dontpreverify
(
当然,
默认的 proguard-android.txt
文件已包含这两条混淆命令,
不需要开发者额外配置)
。2. 资源压缩
资源压缩将移除项目及依赖的库中未被使用的资源, 这在减少 apk 包体积上会有不错的效果, 一般建议开启。具体做法是在
build.grade
文件中,
将 shrinkResources
属性设置为 true
。需要注意的是,
只有在用minifyEnabled true
开启了代码压缩后,
资源压缩才会生效。资源压缩包含了“合并资源”和“移除资源”两个流程。
“合并资源”流程中, 名称相同的资源被视为重复资源会被合并。需要注意的是, 这一流程不受
shrinkResources
属性控制,
也无法被禁止,
gradle 必然会做这项工作,
因为假如不同项目中存在相同名称的资源将导致错误。gradle 在四处地方寻找重复资源:
-
src/main/res/
路径 - 不同的构建类型( debug、release等等)
- 不同的构建渠道
- 项目依赖的第三方库
合并资源时按照如下优先级顺序:
< code class= " hljs haskell has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > 依赖 -> main -> 渠道 -> 构建类型< /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul>
举个例子, 假如重复资源同时存在于
main
文件夹和不同渠道中,
gradle 会选择保留渠道中的资源。同时, 如果重复资源在同一层次出现, 比如
src/main/res/
和 src/main/res2/
,
则 gradle 无法完成资源合并,
这时会报资源合并错误。“移除资源”流程则见名知意, 需要注意的是, 类似代码, 混淆资源移除也可以定义哪些资源需要被保留, 这点在下文给出。
三、自定义混淆规则 在上文“混淆配置”中有这样一行代码
< code class= " hljs bash has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > proguardFiles getDefaultProguardFile(< span class= " hljs-string" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > ' proguard-android.txt' < /span> ), < span class= " hljs-string" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > ' proguard-rules.pro' < /span> < /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < /ul>
这行代码定义了混淆规则由两部分构成: 位于 SDK 的
tools/proguard/
文件夹中的 proguard-android.txt
的内容以及默认放置于模块根目录的 proguard-rules.pro
的内容。前者是 SDK 提供的默认混淆文件(
内容见附录1)
,
后者是开发者自定义混淆规则的地方。1. 常见混淆命令:
- optimizationpasses
- dontoptimize
- dontusemixedcaseclassnames
- dontskipnonpubliclibraryclasses
- dontpreverify
- dontwarn
- verbose
- optimizations
- keep
- keepnames
- keepclassmembers
- keepclassmembernames
- keepclasseswithmembers
- keepclasseswithmembernames
命令 | 作用 |
---|---|
-keep | 防止类和成员被移除或者被重命名 |
-keepnames | 防止类和成员被重命名 |
-keepclassmembers | 防止成员被移除或者被重命名 |
-keepnames | 防止成员被重命名 |
-keepclasseswithmembers | 防止拥有该成员的类和成员被移除或者被重命名 |
-keepclasseswithmembernames | 防止拥有该成员的类和成员被重命名 |
形如:
< code class= " hljs css has-numbering" style= " display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: ' Source Code Pro' , monospace; font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent; " > < span class= " hljs-attr_selector" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > [保持命令]< /span> < span class= " hljs-attr_selector" style= " color: rgb(0, 136, 0); box-sizing: border-box; " > [类]< /span> < span class= " hljs-rules" style= " box-sizing: border-box; " > { < span class= " hljs-rule" style= " box-sizing: border-box; " > [成员] }< /span> < /span> < /code> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; opacity: 0.243097; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < /ul> < ul class= " pre-numbering" style= " box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238); " > < li style= " box-sizing: border-box; padding: 0px 5px; " > 1< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 2< /li> < li style= " box-sizing: border-box; padding: 0px 5px; " > 3< /li> < /ul>
“类”代表类相关的限定条件, 它将最终定位到某些符合该限定条件的类。它的内容可以使用:
- 具体的类
- 访问修饰符(
public
、protected
、private
)
- 通配符
*
,
匹配任意长度字符,
但不含包名分隔符(.) - 通配符
**
,
匹配任意长度字符,
并且包含包名分隔符(.) -
extends
,
即可以指定类的基类 -
implement
,
匹配实现了某接口的类 - $, 内部类
“成员”代表类成员相关的限定条件, 它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:
- 匹配所有构造器
- 匹配所有域
- 匹配所有方法
- 通配符
*
,
匹配任意长度字符,
但不含包名分隔符(.) - 通配符
**
,
匹配任意长度字符,
并且包含包名分隔符(.) - 通配符
***
,
匹配任意参数类型 -
…
,
匹配任意长度的任意类型参数。比如void test(…)就能匹配任意 void test(String a)
或者是void test(int a, String b)
这些方法。 - 访问修饰符(
public
、protected
、private
)
【Android开发者的混淆使用手册】
推荐阅读
- win10 系统无法通过数据线连接安卓手机,提示无法识别驱动器的解决方案
- Android(IPC之AIDL的学习和总结)
- android launchMode理解以及应用场景
- 安卓开发之基于AccessibilityService实现聊天机器人对其他应用的调起
- Android开发学习——Android项目的目录结构
- Android圆角图片和圆形图片实现总结
- Android中的布局动画
- Android-异步任务-AsyncTask
- Android开发使用TotalControl调试遇到的问题(备注)