目前,我们读了Proguard的代码,相信已经开始对访问者模式和装饰器模式有很深的理解,
现在我们再带着这两个模式认真的回顾下代码,因为只有这样,我们才能更好的进行下面的代码阅读。但是如果你还带着疑问,不妨看下前面的章节,或者看一些有关设计模式的书体会一下。
我们回到我们熟悉的入口Proguard类的execute方法中:
第一部分:读取(readinput)InputReader.execute:
ClassFilter filter = new ClassFilter(
new ClassReader(false,configuration.skipNonPublicLibraryClasses, //false
configuration.skipNonPublicLibraryClassMembers, //true
warningPrinter,
new ClassPresenceFilter(
programClassPool,
duplicateClassPrinter,
new ClassPoolFiller(programClassPool)))
);
readInput("Reading program ",
configuration.programJars,
filter);
ClassFilter 我们可以用一个简单的类调用关系来表示:
filter = ClassFilter->ClassReader->ClassPresenceFilter->ClassPoolFiller
这是设计模式中的装饰器.
ClassFilter 继承于 FilteredDataEntryReader 并直接使用它的方法,只不过指定了它的匹配模式:
针对.class 文件.
ClassFilter 的作用是如果是.class文件的话就执行 ClassReader 操作,但是我们知道,文件读入的时候未必是class文件,也有可能是jar或者war或者文件夹.
那么它是怎么做的呢?这其实牵扯到它又是如何读入的呢?
Proguard中读入的过程调用
private void readInput(String messagePrefix,
ClassPathEntry classPathEntry,
DataEntryReader dataEntryReader)
其中dataEntryReader 参数就是上面的Filter.前文我们说过,对于不同的输入源,会采用不同的reader来读取,我们读入的既然是个jar,我们就会生成一个
JarReader来读取这个源,来看下jarreader是如何处理读取的:
public void read(DataEntry dataEntry) throws IOException
{
ZipInputStream zipInputStream = new ZipInputStream(dataEntry.getInputStream());
try
{
// Get all entries from the input jar.
while (true)
{
// Can we get another entry?
ZipEntry zipEntry = zipInputStream.getNextEntry();
if (zipEntry == null)
{
break;
}
// Delegate the actual reading to the data entry reader.
dataEntryReader.read(new ZipDataEntry(dataEntry,
zipEntry,
zipInputStream));
}
}
finally
{
dataEntry.closeInputStream();
}
}
ZipEntry就是以class为后缀的字节码文件,那么我们回到刚才,上面描述的这些通过ClassFilter的验证。那么就到了装饰器的第二层,
ClassReader.
ClassReader的目的就是为了读取,我们可以从它的构造器中看出:它是最基础的DataEntryReader,也就是说不包装任何的DataEntryReader。
在它的read方法中,它读入数据,专程不同的Clazz内部结构,通过不同的数据访问操作来访问它,当然不以装饰的方法来访问它,而是通过类似代理的结构来访问数据。
ClassReader 的最后一参数是一个classVisitor 我们跟到上面的实现类是一个装饰器:ClassPresenceFilter->ClassPoolFiller
我们说过ClassPresenceFilter的目的是为了去除重复.从
new ClassPresenceFilter(
programClassPool,
duplicateClassPrinter,
new ClassPoolFiller(programClassPool))
可以看出ClassPresenceFilter 的目的是为了去除programClassPool 中的重复数据,如果有重复的class将通过duplicateClassPrinter 打印出来
。假如我们现在没有重复的class ,通过ClassPresenceFilter 的过滤,它传递给了ClassPoolFiller,它将这个字节码加入到代码池中。
好了,到这里我们的读入目的已经达到~接下來我们进入下一步:初始化;
===============================================================
===============================================================
===============================================================
第二部分:初始化 (initialize) Initializer.execute
初始化的过程第一步先会调用:
programClassPool.classesAccept(
new ClassSuperHierarchyInitializer(programClassPool,
libraryClassPool,
classReferenceWarningPrinter,
null));
ClassSuperHierarchyInitializer 类很纯粹,没有包装任何东西,所以看起来应该不费劲。
我们来看一下:
public void visitProgramClass(ProgramClass programClass)
{
// Link to the super class.
programClass.superClassConstantAccept(this);
// Link to the interfaces.
programClass.interfaceConstantsAccept(this);
}
它实际上是关于常量的访问.
public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
{
classConstant.referencedClass =
findClass(clazz.getName(), classConstant.getName(clazz));
}
目的很明显,是为了给常量设置它的引用类。
private Clazz findClass(String referencingClassName, String name)
{
// First look for the class in the program class pool.
Clazz clazz = programClassPool.getClass(name);
// Otherwise look for the class in the library class pool.
if (clazz == null)
{
clazz = libraryClassPool.getClass(name);
if (clazz == null &&
missingWarningPrinter != null)
{
// We didn't find the superclass or interface. Print a warning.
missingWarningPrinter.print(referencingClassName,
name,
"Warning: " +
ClassUtil.externalClassName(referencingClassName) +
": can't find superclass or interface " +
ClassUtil.externalClassName(name));
}
}
else if (dependencyWarningPrinter != null)
{
// The superclass or interface was found in the program class pool.
// Print a warning.
dependencyWarningPrinter.print(referencingClassName,
name,
"Warning: library class " +
ClassUtil.externalClassName(referencingClassName) +
" extends or implements program class " +
ClassUtil.externalClassName(name));
}
return clazz;
}
我们看到,它会找到这个类并返回给你。当然有人可能会问,为什么常量的引用关系要在这里被赋予呢?为什么不再Clazz文件读入的时候就进行初始化呢?~这个说实话我也不得而知,
希望能在后面的代码中得到答案。
好了常量初始化完成之后调用
programClassPool.classesAccept(
new ClassReferenceInitializer(programClassPool,
libraryClassPool,
classReferenceWarningPrinter,
programMemberReferenceWarningPrinter,
libraryMemberReferenceWarningPrinter,
null));
这应该是成员的初始化,或者说引用类型的初始化:
public void visitProgramClass(ProgramClass programClass)
{
programClass.constantPoolEntriesAccept(this);
programClass.fieldsAccept(this);
programClass.methodsAccept(this);
programClass.attributesAccept(this);
}
这里我们只说一个访问方式programClass.attributesAccept(this);
就行那些算做Attribute呢?我推荐一个文章,http://1025250620.iteye.com/admin/blogs/1971213 先了解下class的文件结构。
class文件的属性包含有code属性和文件表述属性:
class文件自带Source属性用来标记文件名,code属性里面可以附带局部变量表,linenumber表,或者异常表。
===============================================================
===============================================================
===============================================================
第三部分:打印seed (printSeeds) SeedPrinter.write
在打印之前:
programClassPool.classesAccept(new ClassCleaner());
libraryClassPool.classesAccept(new ClassCleaner());
先清除在libraryClassPool 和 programClassPool 中的标记。
SimpleClassPrinter printer = new SimpleClassPrinter(false, ps); 是一个简单的格式化输出流。
然后调用
programClassPool.classesAcceptAlphabetically(new MultiClassVisitor(
new ClassVisitor[]
{
new KeptClassFilter(printer),
new AllMemberVisitor(new KeptMemberFilter(printer))
}));
MultiClassVisitor 是一个装饰,用来迭代平级的KeptClassFilter ,AllMemberVisitor
可以看出,最后打印的结构一定是先打印KeptClassFilter 也就是类,后打印成员 AllMemberVisitor
我们看一下 KeptClassFilter 的过滤条件
public void visitProgramClass(ProgramClass programClass)
{
if (KeepMarker.isKept(programClass))
{
classVisitor.visitProgramClass(programClass);
}
}
是通过类是否被标记作为判断条件。
AllMemberVisitor 是表示用所有的成员也就是属性和方法访问,过滤条件是:
KeptMemberFilter
也就是方法和属性是否被标记。好的,我们最本质的问题就回归到如何标记。
KeepMarker keepMarker = new KeepMarker();
ClassPoolVisitor classPoolvisitor =
ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
keepMarker,
keepMarker,
true,
true,
true);
// Mark the seeds.
programClassPool.accept(classPoolvisitor);
libraryClassPool.accept(classPoolvisitor);
如果你已经了解了大概的流程应该知道是通过classPoolvisitor 这个对象来标记的:
我们看一下ClassSpecificationVisitorFactory。createClassPoolVisitor 方法
public static ClassPoolVisitor createClassPoolVisitor(List keepClassSpecifications,
ClassVisitor classVisitor,
MemberVisitor memberVisitor,
boolean shrinking,
boolean optimizing,
boolean obfuscating)
{
MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
if (keepClassSpecifications != null)
{
for (int index = 0; index < keepClassSpecifications.size(); index++)
{
KeepClassSpecification keepClassSpecification =
(KeepClassSpecification)keepClassSpecifications.get(index);
if ((shrinking && !keepClassSpecification.allowShrinking)
||(optimizing && !keepClassSpecification.allowOptimization)
||(obfuscating && !keepClassSpecification.allowObfuscation))
{
ClassPoolVisitor classPoolVisitor = createClassPoolVisitor(keepClassSpecification,
classVisitor,
memberVisitor);
multiClassPoolVisitor.addClassPoolVisitor(classPoolVisitor);
}
}
}
return multiClassPoolVisitor;
}
它会针对不同的keep条件来生成不同的 ClassPoolVisitor ,这里我们的
ClassVisitor classVisitor,MemberVisitor memberVisitor
都是KeepMarker。
跟到最后我们跟到返回的ClassPoolVisitor的实现类是:
(ClassPoolVisitor)new NamedClassVisitor(composedClassVisitor, className) :
(ClassPoolVisitor)new AllClassVisitor(composedClassVisitor);
读过我的keep那个章节的应该知道如果你使用的是通用符号*,那么返回的就是AllClassVisitor ,否则就是 NamedClassVisitor
不论是那一种最后调用的都是:composedClassVisitor
它会将ClassVisitor classVisitor, MemberVisitor memberVisitor 组合起来
接下来,如果你是通用符号的话,也就是说:
if (className != null &&
(extendsAnnotationType != null ||
extendsClassName != null ||
containsWildCards(className)))
{
composedClassVisitor =
new ClassNameFilter(className, composedClassVisitor);
// We'll have to visit all classes now.
className = null;
}
则会将composedClassVisitor 包装个className的过滤器.
最后返回AllClassVisitor 对象。
如果它有继承配置,那么将在composedClassVisitor的基础上在增加 ClassHierarchyTraveler 用来传递到继承的标记。
最后SeedPrinter通过标记来区分打印。
===============================================================
===============================================================
===============================================================
第四部分:压缩 (shrink) Shrinker.execute
压缩使用的是UsageMarker 这个访问者,它的目的也是为了做标记。
先是对ClassSpecificationVisitorFactory.createClassPoolVisitor 做完标记,
然后再对其引用的
new InnerUsageMarker(usageMarker),
new AnnotationUsageMarker(usageMarker),
new SignatureUsageMarker(usageMarker),
new LocalVariableTypeUsageMarker(usageMarker)
做标记。
- 浏览: 229799 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
sfshine:
非常好非常好
Android 窗口管理 -
mthhk:
...
android3.0之Fragment(碎片)基础 -
FrankHB1989:
“C/C++标准不会保证这样的代码一定不会出错”的依据?你要说 ...
Duff's Device -
2006003845:
请问知道 刚体之间怎么不碰撞嘛 ?相互穿插
JBOX2D分析
发表评论
-
Java中循环嵌套跳出的高效写法
2014-04-24 19:31 3139(下面写的这些并不是Jdk的新特性,也不是Java某个版本提供 ... -
子墨对酒《三国杀》里论模式(肆)单例模式
2014-02-11 18:40 792当设计模式的观点被众 ... -
子墨对酒《三国杀》里论模式(三)适配器模式
2014-02-11 18:39 747如果你是一个Android程 ... -
子墨对酒《三国杀》里论模式(二)门面模式
2014-02-11 18:38 602学 习模式的人对门面 ... -
子墨对酒《三国杀》里论模式(一)工厂模式
2014-02-11 18:37 1096有人说模式会让代码变的优雅,也有人说模式会让你的代码更利于扩 ... -
Android各版本新增功能一览(转)
2013-12-05 20:43 1681Android3.0新增功能:主要增加全息主题 ... -
NotificationManager和Notification的使用总结(转)
2013-11-26 12:51 610这几天一直在修改twig ... -
Android应用程序资源的编译和打包过程分析 (转自老罗的博客)
2013-11-13 10:57 15512我们知道,在一个APK文件中,除了有代码文件之外,还 ... -
Proguard 源码分析 (七) 混淆
2013-11-10 21:19 1789本章我们讲Proguard非常重要的一个步骤:混淆Obfu ... -
java class文件结构(转)
2013-11-05 13:46 973学习Java的朋友应该都 ... -
Proguard源码分析(五) ConfigurationParser.keep参数
2013-10-31 14:43 3912本章节我们绕回来讲Keep参数,也就是Configurat ... -
Proguard源码分析(四) 压缩
2013-10-30 10:59 1104上一次我们讲了seed文件,这次我们说压缩,对应的输出文件是 ... -
Proguard源码分析(三)Seed文件
2013-10-29 12:45 1515Seed文件就是保持住的类文件,直白一点就是不被混淆的文件, ... -
Proguard源码分析(二)输出文件
2013-10-28 10:28 895ProGuard outputs thefollowing ... -
Android 2.2.2到Android 4.2.2源码下载地址(转)
2013-10-28 10:13 984Android 2.2.2到Android 4.2.2源码下 ... -
代码混淆器Proguard源码分析(一) 读取
2013-10-22 19:40 2289Proguard是Android中经常用的混淆工具,当然你也 ... -
Android内存之VSS/RSS/PSS/USS
2013-09-25 14:07 679Terms VSS - Vi ... -
chrome开源工程(转)
2013-09-13 10:42 1305在chrome地址栏输入about:credits就可以看 ... -
dex文件结构(转)
2013-09-03 14:10 1005Dex文件和Dalvik虚拟机 在Android系统中 ... -
android clipPath切割画布
2013-08-29 12:00 6220(转自:http://wallage.blog.163.co ...
相关推荐
资源是proguard6.2.2版本,里面附上了中文的使用教程,一看就懂,非常简单,不懂的可以私信问我。
`retrace`是ProGuard的一个配套工具,它的主要功能是反混淆堆栈跟踪,当应用在运行时出现错误时,可以通过`retrace`将混淆后的堆栈信息还原为混淆前的形式,便于调试。 `buildscripts`和`gradle`目录可能涉及到构建...
**ProGuard 使用详解** ProGuard 是一款强大的 Java 字节码混淆工具,由 Eric Lafortune 开发,主要用于优化、压缩、混淆和预校验 Java 类文件。在 Android 开发中,ProGuard 被广泛应用于保护应用源代码,避免逆向...
解决方案:找到proguard源码中proguard\src\proguard\classfile\ClassConstants.java类,然后修改ATTR_StackMapTable的值,将原来的的StackMapTable改为dummy.然后重新ant打包proguard。资源已经处理(源码+proguard...
**ProGuard支持JDK 1.8:深入理解与应用** 在Android开发中,为了提高应用程序的安全性和优化代码,我们通常会使用代码混淆工具。ProGuard是一款强大的Java字节码混淆、优化、压缩和预检查工具,它能够帮助开发者...
ProGuard是一款广泛使用的Java代码混淆、优化和压缩工具,它能有效地减小应用程序的体积,提高安全性,并且在Android开发中扮演着至关重要的角色。在介绍ProGuard 6.0.13这个最新版本之前,我们先来了解一下ProGuard...
Eclipse+ProGuard配置 Eclipse 是一个基于 Java 的集成开发环境(IDE),ProGuard 是一个 Java 类库和应用程序的保护和优化工具。通过结合使用 Eclipse 和 ProGuard,可以对 Java 应用程序进行混淆、压缩和优化,...
2. **优化**:ProGuard会分析字节码,去除无用的类、方法和字段,同时优化剩余的字节码,使其运行更加高效。这一步骤不仅可以减小应用的大小,还可以提高运行速度。 3. **压缩**:在混淆和优化之后,ProGuard会删除...
**ProGuard**是一款广泛使用的Java代码...总结,ProGuard是Java和Android开发中的重要工具,用于提高代码安全性和优化应用性能。了解并熟练使用ProGuard的配置和工作原理,能够帮助开发者更好地管理和保护自己的代码。
**ProGuard:Java代码混淆与优化工具** ProGuard 是一个强大的免费开源工具,主要用于Java程序的优化、混淆、预校验以及资源收缩。这个工具在软件发布时尤其有用,可以提高代码的安全性和难以逆向工程的程度,同时...
ProGuard 7.3.0 是一款强大的Java代码优化、混淆和压缩工具,适用于各种Java应用程序,包括Android应用。此版本是ProGuard的最新更新,旨在解决从GitHub下载速度缓慢的问题,确保用户能够快速便捷地获取和使用该工具...
ProGuard 是一款强大的 Java 类文件压缩、优化和混淆工具,广泛应用于 Android 开发中,用于减小程序体积、提高运行效率和增强代码安全性。它能够删除未使用的类、字段、方法和属性,优化字节码,以及对类、方法和...
proguard 6.2.2的魔改版本用于混淆app、jar、class等,将所有a,b,c等等的字符改成不可见字符,从而提高安全性。里面包括源码和编译好的jar包。编译源码使用core/build.sh,编译好的jar包在lib目录下。android app...
java 源码加密 混淆,proguard 配置文件,很详细,经测试可以用
**ProGuard 4.8** 是一款强大...总结,ProGuard 4.8 是一个强大的工具,通过混淆、优化和压缩Java代码,提高了程序的安全性和性能。在Android开发中,合理地配置和使用ProGuard是保护应用免受逆向工程攻击的重要手段。
找到proguard源码中proguard\src\proguard\classfile\ClassConstants.java类,然后修改ATTR_StackMapTable的值,将原来的的StackMapTable改为dummy. 然后重新ant打包proguard,使用新的proguard来混淆就不会出现上面...
总结起来,ProGuard 6.4是一个强大的工具,能够帮助开发者保护代码、优化性能并减小应用大小。通过熟练掌握其配置和使用,我们可以确保我们的Java或Android项目更加健壮和安全。在不断变化的开发环境中,保持对最新...
1. **预处理**:ProGuard首先对输入的类文件进行分析,识别出类、方法和字段的依赖关系。 2. **优化**:在预处理之后,ProGuard对字节码进行优化,例如删除无用的代码、合并相似的类和方法等,以减小代码体积并提高...
找到proguard源码中proguard\src\proguard\classfile\ClassConstants.java类,然后修改ATTR_StackMapTable的值,将原来的的StackMapTable改为dummy. 然后重新ant打包proguard,使用新的proguard来混淆就不会出现...