除了以上方式,你也可以选择使用 @Keep
注解来保留期望代码,防止它们被混淆处理。比如,我们通过 @Keep
修饰一个类来保留它不被混淆:
@Keep
data class CloudMusicBean(var createDate: String,
var id: Long,
var name: String,
var url: String,
val imgUrl: String)
同样地,我们也可以让 @Keep
来修饰方法或者字段进而保留它们。
其他命令
- dontwarn
-dontwarn
命令一般在我们引入新的 library 时会使用到,常用于处理 library 中无法解决的警告。如:
-keep class twitter4j.** { *; }
-dontwarn twitter4j.**
- 其他的命令用法可参考 Android 系统提供的默认混淆规则:
#混淆时不生成大小写混合的类名
-dontusemixedcaseclassnames
#不跳过非公共的库的类
-dontskipnonpubliclibraryclasses
#混淆过程中记录日志
-verbose
#关闭预校验
-dontpreverify
#关闭优化
-dontoptimize
#保留注解
-keepattributes Annotation
#保留所有拥有本地方法的类名及本地方法名
-keepclasseswithmembernames class * {
native ;
}
#保留自定义View的get和set方法
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#保留Activity中View及其子类入参的方法,如: onClick(android.view.View)
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#保留枚举
-keepclassmembers enum * {
**[] $VALUES;
public *;
}
#保留序列化的类
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
#保留R文件的静态成员
-keepclassmembers class *.R$ {
public static ;
}
-dontwarn android.support.**
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep (…);
}
更多混淆命令可以参考文章:Proguard 最全混淆规则说明 ,这里就不做详细讲解了。
混淆"黑名单"
我们在了解了混淆的基本命令之后,很多人应该还是一头雾水:到底哪些内容该混淆?其实,我们在使用代码混淆时,ProGuard 对我们项目中大部分代码进行了混淆操作,为了防止编译时出错,我们应该通过 keep
命令保留一些元素不被混淆。所以,我们只需要知道哪些元素不应该被混淆:
枚举
项目中难免可能会用到枚举类型,然而它不能参与到混淆当中去。原因是:枚举类内部存在 values
方法,混淆后该方法会被重新命名,并抛出 NoSuchMethodException
。庆幸的是,Android 系统默认的混淆规则中已经添加了对于枚举类的处理,我们无需再去做额外工作。想了解更多枚举内部细节可以去查看源码,篇幅有限不再细说。
被反射的元素
被反射使用的类、变量、方法、包名等不应该被混淆处理。原因在于:代码混淆过程中,被反射使用的元素会被重命名,然而反射依旧是按照先前的名称去寻找元素,所以会经常发生 NoSuchMethodException
和 NoSuchFiledException
问题。
实体类
实体类即我们常说的"数据类",当然经常伴随着序列化与反序列化操作。很多人也应该都想到了,混淆是将原本有特定含义的"元素"转变为无意义的名称,所以,经过混淆的"洗礼"之后,序列化之后的 value
对应的 key
已然变为没有意义的字段,这肯定是我们不希望的。同时,反序列化的过程创建对象从根本上来说还是借助于反射,混淆之后 key
会被改变,所以也会违背我们预期的效果。
四大组件
Android 中的四大组件同样不应该被混淆。原因在于:
-
四大组件使用前都需要在
AndroidManifest.xml
文件中进行注册声明,然而混淆处理之后,四大组件的类名就会被篡改,实际使用的类与manifest
中注册的类并不匹配,故而出错。 -
其他应用程序访问组件时可能会用到类的包名加类名,如果经过混淆,可能会无法找到对应组件或者产生异常。
JNI 调用的Java 方法
当 JNI 调用的 Java 方法被混淆后,方法名会变成无意义的名称,这就与 C++ 中原本的 Java 方法名不匹配,因而会无法找到所调用的方法。
其他不应该被混淆的
-
自定义控件不需要被混淆
-
JavaScript 调用 Java 的方法不应混淆
-
Java 的 native 方法不应该被混淆
-
项目中引用的第三方库也不建议混淆
混淆后的堆栈跟踪
代码经过 ProGuard 混淆处理后,想要读取 StackTrace(堆栈追踪)信息就会变得很困难。由于方法名称和类的名称都经过混淆处理,即使程序发生崩溃问题,也很难定位问题所在。幸运的是,ProGuard 为我们提供了补救的措施,在着手进行之前,我们先来看一下 ProGuard 每次构建后生成了哪些内容。
混淆输出结果
混淆构建完成之后,会在 <module-name>/build/outputs/mapping/release/
目录下生成以下文件:
- dump.txt
说明 APK 内所有类文件的内部结构。
- mapping.txt
提供混淆前后的内容对照表,内容主要包含类、方法和类的成员变量。
- seeds.txt
罗列出未进行混淆处理的类和成员。
- usage.txt
罗列出从 APK 中移除的代码。
恢复堆栈跟踪
了解完混淆构建完毕后输出的内容之后,我们现在就来看一下之前的问题:混淆处理后,StackTrace 定位困难。如何来恢复 StackTrace 的定位能力呢?系统为我们提供了 retrace 工具,结合上文提到的 mapping.txt
文件,就可以将混淆后的崩溃堆栈追踪信息还原成正常情况下的 StackTrace 信息。主要有两种方式来恢复 StackTrace,为了方便理解,我们以下面这段崩溃信息为例,借助两种方式分别来还原:
java.lang.RuntimeException: Unable to start activity
Caused by: kotlin.KotlinNullPointerException
at com.moos.media.ui.ImageSelectActivity.k(ImageSelectActivity.kt:71)
at com.moos.media.ui.ImageSelectActivity.onCreate(ImageSelectActivity.kt:58)
at android.app.Activity.performCreate(Activity.java:6237)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
- 通过 retrace 脚本工具
首先我们要进入到 Android SDK 路径的 /tools/proguard/bin
目录中,这里以 Mac 系统为例,可以看到如下内容:
可以看到如上三个文件,而 proguardgui.sh
才是我们需要的 retrace
脚本(Windows系统下为 proguardgui.bat
)。Windows 系统中只需要双击脚本 proguardgui.bat
即可运行,至于 Mac 系统,如果你没有做任何配置,只需要将 proguardgui.sh
脚本拖动到 Mac 自带的终端中,回车键即可运行。接着,我们会看到如下界面:
选择 ReTrace
栏 ,并添加我们项目中混淆生成的 mapping.txt
文件所在位置,然后将我们的混淆后的崩溃信息复制到 Obfuscated stack trace
那一栏,点击 ReTrace!
按钮即可还原出我们的崩溃日志信息,结果如上图所示,我们之前的混淆日志:at com.moos.media.ui.ImageSelectActivity.k(ImageSelectActivity.kt:71)
被还原成了 at com.moos.media.ui.ImageSelectActivity.initView(ImageSelectActivity.kt:71)
。ImageSelectActivity.k
是我们混淆后的方法名,ImageSelectActivity.initView
则是最初未混淆前的方法名,借助于 ReTrace 工具的帮助,我们就可以像以前一样很快定位到崩溃代码区域了。
- 通过 retrace 命令行
我们先要将崩溃信息复制到 txt
格式的文件(如:proguard_stacktrace.txt)中保存,然后执行以下命令即可(MAC系统):
retrace.sh -verbose mapping.txt proguard_stacktrace.txt
如果你是 windows 系统,可以执行以下命令:
retrace.bat -verbose mapping.txt proguard_stacktrace.txt
最终还原的结果和之前效果一样:
也许你通过以上两种方式在对 stackTrace 进行恢复时,发现 Unknown Source
问题:
值得注意的是,记得在混淆规则中加上如下配置来提升我们的 StackSource 查找效率:
保留源文件名和具体代码行号
-keepattributes SourceFile,LineNumberTable
此外,我们每次使用 ProGuard 创建发布构建时都都会覆盖之前版本的 mapping.txt
文件,因此我们每次发布新版本时都必须小心地保存一个副本。通过为每个发布构建保留一个 mapping.txt
文件副本,我们就可以在用户提交的已混淆的 StackTrace 来对旧版本应用的问题进行调试和修复。
涨姿势的操作
经过上文的介绍,我们知道,APK 在经过代码混淆处理后,包名、类名、成员名被转化为无意义、难以理解的名称,增加反编译的成本。Android ProGuard 为我们提供了默认的"混淆字典",即将元素名称转为英文小写字母的形式。那么,我们可以定义自己的混淆字典吗?卖个关子,我们先来看一张效果图:
这个波操作是不是有点"出类拔萃"了?哈哈,就不卖关子了,其实很简单,只要生成一套自己的 txt
格式的混淆字典,然后在混淆规则 Proguard-rules.pro
中应用一下即可:
最后说一下我的学习路线
其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:
- 架构师筑基必备技能
- Android框架体系架构(高级UI+FrameWork源码)
- 360°Androidapp全方位性能调优
- 设计思想解读开源框架
- NDK模块开发
- 移动架构师专题项目实战环节
- 移动架构师不可不学习微信小程序
- 混合开发的flutter
Android学习的资料
我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。
330页PDF Android学习核心笔记(内含上面8大板块)
Android学习的系统对应视频
总结
我希望通过我自己的学习方法来帮助大家去提升技术:
-
1、多看书、看源码和做项目,平时多种总结
-
2、不能停留在一些基本api的使用上,应该往更深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理
-
3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习
-
4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!
希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理
-
3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习
-
4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!
希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!