alwaystest / Blog

24 stars 2 forks source link

继续说说Android的混淆 #48

Open alwaystest opened 7 years ago

alwaystest commented 7 years ago

继续说说Android的混淆

标签(空格分隔): Android


前段时间遇上了新问题,依赖的库太多,即使Proguard压缩了方法数,依然逼近64K大关。是时候再优化优化规则,挤点乳沟出来了,我就是不愿意开启MultiDex。

使用dexcount看看方法数占用。发现android开头的包下面占用最多,20K个方法。下面supoport包又占了大半,但是毕竟是android开头的包名啊,动手之前还是要查查有没有后台的。

想起来之前看到过Android混淆的文章提到过:

AndroidMainfest中的类不混淆,所以四大组件和Application的子类和Framework层下所有的类默认不会进行混淆。自定义的View默认也不会被混淆;所以像网上贴的很多排除自定义View,或四大组件被混淆的规则在Android Studio中是无需加入的;

文/CPPAlien(简书作者) 原文链接:http://www.jianshu.com/p/7436a1a32891 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。 我就引用下,原文连接也贴出来了,不要那么麻烦找授权了吧。

然后又找到郭霖大神的文章说:

接着看一下MyFragment,这个类也是混淆的比较彻底的,基本没有任何保留。那有些朋友可能会有疑问,Fragment怎么说也算是系统组件吧,就算普通方法名被混淆了,至少像onCreateView()这样的生命周期方法不应该被混淆吧?其实生命周期方法会不会被混淆和我们使用Fragment的方式有关,比如在本项目中,我使用的是android.support.v4.app.Fragment,support-v4包下的,就连Fragment的源码都被一起混淆了,因此生命周期方法当然也不例外了。但如果你使用的是android.app.Fragment,这就是调用手机系统中预编译好的代码了,很明显我们的混淆无法影响到系统内置的代码,因此这种情况下onCreateView()方法名就不会被混淆,但其它的方法以及变量仍然会被混淆。

http://blog.csdn.net/guolin_blog/article/details/50451259

那就开始下手吧。

首先把之前从网上粘的冗余规则删掉删掉!

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

然后是support库的规则删掉删掉!

-keep class android.support.** {*;}

网上还有规则考虑到了fragment没有在manifest中注册,然后添加了fragment的保护规则,这点我也没有弄的很明白,就相信郭霖大神的结论吧。验证什么的,有时间再考虑。

最终,成功的把方法数降低了1W+。又能导好多库了呢。

再引一个回答:

The build process runs the tool aapt to automatically create the configuration file bin/proguard.txt, based on AndroidManifest.xml and other xml files. The build process then passes the configuration file to ProGuard. So ProGuard itself indeed doesn't consider AndroidManifest.xml, but aapt+ProGuard do.

http://stackoverflow.com/a/19192262/3819519

我本来以为是Proguard对Android做了定制,会读取一遍manifest然后生成对应规则的,看了这个回答才明白,之前的想法是削足适履。aapt对自己进行处理,生成一份规则再导给Proguard,完美。

再看看aapt生成的规则文件。我的路径是 app/build/intermediates/proguard-rules/xxx

生成的规则是这样的

# view AndroidManifest.xml #generated: 1
-keep class com.lalala.lala.activity.MainActivity { <init>(...); }

看起来比这样的规则要好些。指定的更细化了。

-keep public class * extends android.app.Activity

再考虑不是support库的fragment的混淆,如果说Activity指定保护的方法只有构造函数。那么重写父类的生命周期函数是不需要指定规则也不会被重命名掉的。fragment也一样。

可以这么理解,混淆代码就是重构方法名,实现Proguard的时候,必须考虑到重构之后效果和之前是一样的,所以要重命名子类重写的父类的方法时,也要把父类的方法名重命名掉。如果是我们自定义的接口,那么很简单,重构即可。但是如果是Android SDK中提供的类,打包的时候是不需要打包到APK中的,手机系统中就提供了类的实现,就像JRE可以提供Java原始类的环境一样。这样在混淆的时候,Proguard是不具备重命名父类方法的条件的,所以那些生命周期方法就不会被重构掉。所以就没有必要指定保护Activity和Fragment的生命周期方法了。其他方法的话,只要不涉及反射,就可以放心的交给Proguard去混淆。