1 摘要
字节码插桩技术可以帮我们实现业务层模块和功能模块的关联,并在项目结构避免其耦合,比如ARouter实现各模块路由表的注册;
2 背景
一般我们使用Transform会有下面两种场景
我们需要对编译class文件做自定义的处理。 我们需要读取编译产生的class文件,做一些其他事情,但是不需要修改它。
本篇主要介绍完成字节码插桩的第一步,通过Transform将编译后的class文件和jar包找到并输出;
3 正文
Transform的基本API参考:
Transform的抽象方法:
public abstract class Transform {
//返回一个本Transform的标记
public abstract String getName();
public abstract Set<ContentType> getInputTypes();
public abstract Set<? super Scope> getScopes();
public abstract boolean isIncremental(); // 是否支持增量编译
}
getName()就是指定自定义的Transform的名字。
输入的类型
Set getInputTypes()是指明你自定义的这个Transform处理的输入类型,输入类型共有以下几种:
enum DefaultContentType implements ContentType {
/** * The content is compiled Java code. This can be in a Jar file or in a folder. If * in a folder, it is expected to in sub-folders matching package names. */
CLASSES(0x01),
/** * The content is standard Java resources. */
RESOURCES(0x02);
}
即分为class文件或者java资源。class文件来自于jar或者文件夹。资源就是标准的java资源。
输入文件所属的范围 Scope
getScopes()用来指明自定的Transform的输入文件所属的范围, 这是因为gradle是支持多工程编译的。总共有以下几种:
/** * This indicates what the content represents, so that Transforms can apply to only part(s) * of the classes or resources that the build manipulates. */
enum Scope implements ScopeType {
/** Only the project content */
PROJECT(0x01), //只是当前工程的代码
/** Only the project's local dependencies (local jars) */
PROJECT_LOCAL_DEPS(0x02), // 工程的本地jar
/** Only the sub-projects. */
SUB_PROJECTS(0x04), // 只包含子工工程
/** Only the sub-projects's local dependencies (local jars). */
SUB_PROJECTS_LOCAL_DEPS(0x08),
/** Only the external libraries */
EXTERNAL_LIBRARIES(0x10),
/** Code that is being tested by the current variant, including dependencies */
TESTED_CODE(0x20),
/** Local or remote dependencies that are provided-only */
PROVIDED_ONLY(0x40);
}
对于getScopes()的返回,其实TransformManager已经为我们定义了一些,比如:
public static final Set<Scope> SCOPE_FULL_PROJECT = Sets.immutableEnumSet(
Scope.PROJECT, Scope.PROJECT_LOCAL_DEPS, Scope.SUB_PROJECTS, Scope.SUB_PROJECTS_LOCAL_DEPS, Scope.EXTERNAL_LIBRARIES);
如果一个Transform不想处理任何输入,只是想查看输入的内容,那么只需在getScopes()返回一个空集合,在getReferencedScopes()返回想要接收的范围。
public Set<? super Scope> getReferencedScopes() {
return ImmutableSet.of();
}
transform()
它是Transform的关键方法:
public void transform(@NonNull TransformInvocation transformInvocation) {}
它是一个空实现,input的内容将会打包成一个TransformInvocation对象,因为我们要想使用input,我们需要详细了解一下TransformInvocation参数。
TransformInvocation
我们看一下这个类相关的API:
public interface TransformInvocation {
Collection<TransformInput> getInputs(); // 输入作为 TransformInput 返回
TransformOutputProvider getOutputProvider(); //TransformOutputProvider 可以用来创建输出内容
boolean isIncremental();
}
public interface TransformInput {
Collection<JarInput> getJarInputs();
Collection<DirectoryInput> getDirectoryInputs();
}
public interface JarInput extends QualifiedContent {
File getFile(); //jar文件
Set<ContentType> getContentTypes(); // 是class还是resource
Set<? super Scope> getScopes(); //属于Scope: