Proguard源码分析(四) 压缩
上一次我们讲了seed文件,这次我们说压缩,对应的输出文件是usage,可以通过命令-printusage usage.txt来配置
压缩的目的是为了删除一些我们不使用的类和方法,从而达到字节码压缩的目的。
这里我贴出我的微博:http://weibo.com/1752090185/profile?rightmod=1&wvr=5&mod=personinfo
有意向可以加我。
直接切入主题吧,压缩是用Shrinker来实现的压缩功能,看过我之前的分析,应该对这种写法并不陌生,这种写法我还没有发现它不好的地方,姑且我们就默认这种写法吧。
一样我们找execute方法。我不知道大家对访问者模式和装饰器模式是否已经有了比较深刻的了解,Proguard里面大量使用了这两种模式,访 问者的目的是将业务的实现移交给访问者,这样保持被访问者的高度扩展性,当然设计模式这种东西在不同的场景下可能呈现不同的概念功能,我不想多做深究。而 对于装饰器的话就像你吃巧克力,必须脱了外面的装饰才能吃到里面的巧克力,在面向方面,安全性验证方面,装饰器模式发挥着重要的作用。慢慢体会吧。
programClassPool.classesAccept(new ClassCleaner());
libraryClassPool.classesAccept(new ClassCleaner());
代码刚上来和前文一样,先做了初始化操作,其次定义了
UsageMarker usageMarker 这个对象,我们跟一下它的实现
public void visitProgramClass(ProgramClass programClass)
{
if (shouldBeMarkedAsUsed(programClass))
{
// Mark this class.
markAsUsed(programClass);
markProgramClassBody(programClass);
}
}
其实功能很简单就是将这个类和它的超类标记为used,好回到刚才
之后又定义一个对象,
ClassVisitor classUsageMarker =
new MultiClassVisitor(new ClassVisitor[]
{
usageMarker,
new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_INIT,
ClassConstants.INTERNAL_METHOD_TYPE_INIT,
usageMarker)
});
NamedMethodVisitor 的目的是为了让表示为name的签名类型的方法执行visitor操作,
这里的visitor就是usageMarker。
这个对象很明显是为了保持住构造器方法。
接下来又是我们的老朋友:
ClassPoolVisitor classPoolvisitor =
ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
classUsageMarker,
usageMarker,
true,
false,
false);
ClassSpecificationVisitorFactory通过keep条件列表来生成池访问者共类池访 问,createClassPoolVisitor方法中会定义个复合的访问者,这个访问者的目的是加入了正则匹配认证,当然这个正则可能不大准确,就是 Proguard本身的一套规则.主要实现类是ListParser和new ClassNameParser()
匹配的细节我们有时间再讨论,这个解析器将解析类似:
"!*.test.*",“**.test.*”这类的类通配表达式。
也就是相当于在usageMarker外加了一层解析器验证.
接下来
programClassPool.classesAccept(new InterfaceUsageMarker(usageMarker));
这个目的是比如你的类实现某个接口,那么你既然不压缩这个类,自然不能压缩这个类所实现的接口。
programClassPool.classesAccept(
new UsedClassFilter(usageMarker,
new AllAttributeVisitor(true,
new MultiAttributeVisitor(new AttributeVisitor[]
{
new InnerUsageMarker(usageMarker),
new AnnotationUsageMarker(usageMarker),
new SignatureUsageMarker(usageMarker),
new LocalVariableTypeUsageMarker(usageMarker)
}))));
这个和上面就完全一致了,要保持住你的内部类,注解,签名,和变量类型等等。
if (configuration.printUsage != null)
{
PrintStream ps =
configuration.printUsage == Configuration.STD_OUT ? System.out :
new PrintStream(
new BufferedOutputStream(
new FileOutputStream(configuration.printUsage)));
// Print out items that will be removed.
programClassPool.classesAcceptAlphabetically(
new UsagePrinter(usageMarker, true, ps));
if (ps == System.out)
{
ps.flush();
}
else
{
ps.close();
}
}
这段代码很明显是为了打印出文件,如果你没有配置configuration.printUsage的话,那么就不会走这些。