Android Gradle插件平台开发系列:前言
Android Gradle插件平台开发系列一:Android APT
Android Gradle插件平台开发系列二:Android SPI
Android Gradle插件平台开发系列三:自定义gradle plugin
Android Gralde插件平台开发系列四:自定义Gradle Transform
Android Gralde插件平台开发系列五:字节码修改
一、Transform是什么
Gradle Transform是Android官方提供给开发者在项目构建阶段即由class到dex转换期间修改class文件的一套api。
二、Transform原理
每个Transform其实都是一个gradle task,Android编译器中的TaskManager将每个Transform串连起来,第一个Transform接收来自javac编译的结果,以及已经拉取到在本地的第三方依赖(jar、aar),还有resource资源,注意,这里的resource并非android项目中的res资源,而是asset目录下的资源。 这些编译的中间产物,在Transform组成的链条上流动,每个Transform节点可以对class进行处理再传递给下一个Transform。
三、Transform处理流程
四、Transform开发流程
4.1 继承Transform类,完成相关抽象方法处理
class TestTransform : Transform() {
/**
* 1.Transform名称
*/
override fun getName(): String {
return "MyTransform"
}
/**
* 2.Transform要处理的数据类型,即输入文件类型
*
* CONTENT_CLASS:表示需要处理 java 的 class 文件。
* CONTENT_JARS:表示需要处理 java 的 class 与 资源文件。
* CONTENT_RESOURCES:表示需要处理 java 的资源文件。
* CONTENT_NATIVE_LIBS:表示需要处理 native 库的代码。
* CONTENT_DEX:表示需要处理 DEX 文件。
* CONTENT_DEX_WITH_RESOURCES:表示需要处理 DEX 与 java 的资源文件。
*/
override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> {
return TransformManager.CONTENT_CLASS
}
/**
* 3.Transform作用域
*
* PROJECT_ONLY:当前工程
* SCOPE_FULL_PROJECT:所有工程
*/
override fun getScopes(): MutableSet<in QualifiedContent.Scope> {
return TransformManager.SCOPE_FULL_PROJECT
}
/**
* 4.是否支持增量编译
*/
override fun isIncremental(): Boolean {
return false
}
}
4.2 重写transform方法,完成jarInputs和directoryInputs拷贝
/**
* 5.在这里进行字节码操作
*/
override fun transform(transformInvocation: TransformInvocation?) {
super.transform(transformInvocation)
transformInvocation?.inputs?.forEach {
// 1.处理jar
it.jarInputs.forEach {
// 标准写法,处理完成copy给下一个transform
val destFile = transformInvocation.outputProvider.getContentLocation(
it.name,
it.contentTypes,
it.scopes,
Format.JAR
)
FileUtils.copyFile(it.file, destFile)
}
// 2.处理directory
it.directoryInputs.forEach {
// 标准写法,处理完成copy给下一个transform
val destDir = transformInvocation.outputProvider.getContentLocation(
it.name,
it.contentTypes,
it.scopes,
Format.DIRECTORY
)
FileUtils.copyDirectory(it.file, destDir)
}
}
}
4.3 完成字节码处理操作
遍历jarInputs和directoryInputs,找到需要hook的class文件,完成字节码处理操作,字节码处理操作将在下节讲到。
4.3.1遍历jar
JarFile(it.file).entries().toList().forEach {
println(it.name)
}
4.3.2 遍历文件
FileUtils.getAllFiles(file).filter {
if (it == null) false else it.name.contains("MainActivity")
}.forEach {
// 在这里进行字节码处理
insertCode(it)
}
4.4 注册Transform
在上期开发的plugin类里完成Transform注册。
/**
* 自定义插件
*/
class TestPlugin : Plugin<Project> {
override fun apply(project: Project) {
println("==========TestPlugin==========")
val android = project.extensions.findByType(AppExtension::class.java)
android?.registerTransform(TestTransform())
}
}
|