混淆
什么是混淆
- 我们都知道Java代码最终被执行是要被Javac编译成JVM上的可执行码(class)才可以被JVM运行,但是字节码的复杂度不高,网上一些可以反编译字节码的工具,比如jd(java decompiler),可以把jar包反编译成java源码,如果想要反编译apk,只需要用dex2-jar就可以把apk转换成jar包,然后就可以使用jd来反编译了,这样我们的代码逻辑不都一览无遗了吗,所以才有了混淆这种东西
- 混淆简单理解一下其实就是在编译前把能换的包名、类名全都变成a b c这样的名字,让代码阅读起来极其困难,但是只简单的理解是这样,其实混淆并不只有这一个作用,也并不一定是在编译之前做的这些操作
- 混淆有四个步骤
- 压缩:去除无用的资源,没有用到的方法、字段,还去除了一些调试信息,比如行号,从而减小字节码文件的体积;所以在这里就要发现,如果你对某些类用了反射,就要配置混淆规则,对这些类不进行混淆
- 优化:可能会把一些方法变成内联的,或者优化for语句等,你甚至可以配置混淆规则,在字节码中去除一些方法,比如你可以去除Log方法,当然这种配置很危险;官方也提到过,这种优化其实是有风险的,我也遇到过在debug包下是正常的,但是启用混淆生成release包时,就出现了一些错误
- 混淆:就是我们最常用的功能,防止反编译以后你的包裸奔,所以混淆器根据定义好的混淆规则把能替换的类和变量都替换成了a b c等无意义的名字,同时我们知道在class文件中,引用的类的名字都是存储在class文件里一个叫做常量池的区域中的,混淆之后也可以在很大程度上减小包的体积
- 预检验:会在Java6以上检验代码是不是符合Java的规范
Android
- 因为我对Android的混淆还有所了解,所以只能稍微写一下Android的东西了;网上有很多将proguard命令的blog,在这里我只讲一些比较简单基础的吧,不然内容实在太多了
- Android中默认使用proguard-android.txt来进行配置混淆规则,可以在build.gradle里面配置默认的混淆规则,也可以是使用自定义的proguard文件
#配置默认的混淆规则
proguardFile getDefaultProguardFile('proguard-android.txt')
#使用自己的proguard文件
proguardFile 'proguard-project.txt'
默认的proguard-android.txt里面规定了Activity名字和生命周期方法不会混淆,View的getter和setter也不会混淆,其实是因为Manifest里面要注册Activity,如果你混淆Activity成了其他名字,那Manifest里面不就找不到了吗,还有就是在View的属性动画里我们在动画结束时会改变View的属性,具体实现就是在animation结束的时候通过反射,寻找相应的prefix为set的方法,通过setter方法进行属性设置,所以也保留了而没有混淆,其实本质上这也是因为反射而导致的不可混淆。保留不混淆的语法是
-keepclass xxx
-keepclassmembers xxx
#...
- 另外就是因为压缩的时候会删除没有显示调用过的方法,但是如果你是使用反射调用的,就必须在proguard里面配置keep,否则就会出错
- 优化的相关的我也稍微讲一些
#代表指定这个类某些方法被删除了也没什么影响,可以用到这个的地方比如说Log
-assumenosideeffects class xxx {xxx}
#进行n次优化过程,n可以自己指定,如果某次优化之后体积和上次比较没有变化那么无论如何都会停止优化
-optimizationpasses n
#禁止优化
-dontoptimize