学习路线:
1.看developer对于代码混淆的介绍:http://developer.android.com/tools/help/proguard.html. 很短,大概20分钟看完(我只看了ant编译的部分)。核心点:
a, 背景。原来proguard不仅是代码混淆,还能通过去掉无用代码减少代码体积,对代码做优化
b, 一定要记得,为每个release版本保存mapping.txt! 非常重要,否则可能无法还原之前版本的trace!
c,具体的proguard使用,参见http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html
2. 去看proguard手册:http://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/introduction.html。 导读:
a,"Input/output options” 这一节要看完。估计花费15分钟
b,重点看-keep Options.花点时间把class specification这部分看完。其他部分可以暂时不看。看到这里,对于基本的规则已经比较清楚了,那么问题来了,对于android,有没有一套一般性的规则呢?哪些class要keep住?
看到这里不得不骂一下谷歌,文档居然没提这茬。 再看看proguard的example:https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/examples.html
这里面重要的几句话:
Most importantly, we're keeping all fundamental classes that may be referenced by the AndroidManifest.xml
file of the application. If your manifest file contains other classes and methods, you may have to specify those as well. ------在manifest中提到的class要keep。例如activity,broadcastreceiver, service, contentprovider等等。
We're keeping annotations, since they might be used by custom RemoteViews --------remoteview没用过,不知道有何影响
We're keeping any custom View
extensions and other classes with typical constructors, since they might be referenced from XML layout files. -------在layout中可能引用的类要keep
We're also keeping the required static fields in Parcelable
implementations, since they are accessed by introspection. --------实现parceble的要keep住. 因为传递参数后要还原parceble对象的一些内部成员
If applicable, you should add options for processing native methods, callback methods, enumerations, and resource files. --------native,回调等要keep住。估计这些类并没有明显的被调用,怕被优化时误杀。
View的几种构造函数也要保护,因为他们在代码中并没有显式地被调用。
-keep public class * extends android.view.View { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); }
可以总结出需要被keep的类/方法/字段的原则:
1, 在XML等外部配置文件中引用名字的。原因:如果是类之间相互引用,proguard是知道的,被混淆后也可以找到类,但是外部引用proguard是不知道的。如果被proguard混淆了,外部引用就找不到对应类了
2,在代码中没有显式调用的函数(就是你用“open call hierarchy"结果为空的),比如native方法,系统回调,反射的函数,改了名字后就找不到类了。
接下来,实际动手执行混淆。可以在网上找个模板上,然后以此为基础稍加改动。模板例子:http://blog.csdn.net/lovexjyong/article/details/24652085
实际调试过程中遇到的问题:
1,自带的ADT工具中,proguard是4.7版本的,其中支持JDK 8有问题,自己在sourceforge上下载了proguard 5.x,将bin和lib拷贝到ADT proguard对应目录下,搞定。
2,不知道哪个类库引用了com.jboss和org.joda下的类,proguard报告警,用-dontwarn org.jboss.** -dontwarn org.joda.** 搞定。
3. 遇到报错 [java.lang.IllegalArgumentException] (Value is not a reference value。。。。 google之,是proguard的一个bug,增加这句搞定:-optimizations !class/unboxing/enum
4. 遇到报错
[dx] Uncaught translation error: com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type int. This i
s symptomatic of .class transformation tools that ignore local variable information
google了一下,没有找到明确答案,但是确认是由优化引起的。先把-dontoptimize加上,发现OK,然后再去掉,发现通过了。。。不知道解决的原因。