Gradle Transform到底是什么怎么用?
一、Gradle Transform到底是什么
Gradle Transform是Android官方提供给开发者在项目构建阶段(.class -> .dex转换期间)用来修改.class文件的一套标准API,即把输入的.class文件转变成目标字节码文件,目前比较经典的应用是字节码插桩、代码注入等。
二、Gradle Transform怎么用
1、在build.gradle文件中添加Gradle插件依赖
buildscript { dependencies { classpath 'com.android.tools.build:gradle:x.x.x' // 其他插件依赖 }}apply plugin: 'com.android.application' // 或其他所需插件
2、编写Transform类
编写Transform类,在其中实现对Java字节码进行的修改。Transform类需要继承自Transform接口并实现其两个方法:
public class MyTransform extends Transform { @Override public String getName() { return "myTransform"; } @Override public Set getInputTypes() { return TransformManager.CONTENT_CLASS; } @Override public Set super QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT; } @Override public boolean isIncremental() { return false; } @Override public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { // 实现Transform逻辑 }}
3、配置Transform
在build.gradle文件中配置Transform,将其作为编译期间的一个任务执行:
android { ... // 配置Transform transformClassesWithMyTransformForDebug { // 可选配置项,如下所示 // enable false // ignoreWarnings true // enableTransformForJar false }}dependencies { ...}
完成以上步骤后,Gradle会在编译期间执行Transform对Java字节码进行修改,从而实现各种自动生成代码、字节码增强等功能。
三、Transform编写模板
1、无增量编译
AspectJTransform.groovy代码如下:
class AspectJTransform extends Transform { final String NAME = "JokerwanTransform" @Override String getName() { return NAME } @Override SetgetInputTypes() { return TransformManager.CONTENT_CLASS } @Override Set super QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT } @Override boolean isIncremental() { return false } @Override void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { super.transform(transformInvocation) // OutputProvider管理输出路径,如果消费型输入为空,你会发现OutputProvider == null TransformOutputProvider outputProvider = transformInvocation.getOutputProvider(); transformInvocation.inputs.each { TransformInput input -> input.jarInputs.each { JarInput jarInput -> // 处理Jar processJarInput(jarInput, outputProvider) } input.directoryInputs.each { DirectoryInput directoryInput -> // 处理源码文件 processDirectoryInputs(directoryInput, outputProvider) } } } void processJarInput(JarInput jarInput, TransformOutputProvider outputProvider) { File dest = outputProvider.getContentLocation( jarInput.getFile().getAbsolutePath(), jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR) // to do some transform // 将修改过的字节码copy到dest,就可以实现编译期间干预字节码的目的了 FileUtils.copyFiley(jarInput.getFile(), dest) } void processDirectoryInputs(DirectoryInput directoryInput, TransformOutputProvider outputProvider) { File dest = outputProvider.getContentLocation(directoryInput.getName(), directoryInput.getContentTypes(), directoryInput.getScopes(), Format.DIRECTORY) // 建立文件夹 FileUtils.forceMkdir(dest) // to do some transform // 将修改过的字节码copy到dest,就可以实现编译期间干预字节码的目的了 FileUtils.copyDirectory(directoryInput.getFile(), dest) }}
2、有增量编译
AspectJTransform.groovy代码如下:
class AspectJTransform extends Transform { final String NAME = "JokerWanTransform" @Override String getName() { return NAME } @Override SetgetInputTypes() { return TransformManager.CONTENT_CLASS } @Override Set super QualifiedContent.Scope> getScopes() { return TransformManager.SCOPE_FULL_PROJECT } @Override boolean isIncremental() { return true } @Override void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { super.transform(transformInvocation) boolean isIncremental = transformInvocation.isIncremental() // OutputProvider管理输出路径,如果消费型输入为空,你会发现OutputProvider == null TransformOutputProvider outputProvider = transformInvocation.getOutputProvider() if (!isIncremental) { // 不需要增量编译,先清除全部 outputProvider.deleteAll() } transformInvocation.getInputs().each { TransformInput input -> input.jarInputs.each { JarInput jarInput -> // 处理Jar processJarInputWithIncremental(jarInput, outputProvider, isIncremental) } input.directoryInputs.each { DirectoryInput directoryInput -> // 处理文件 processDirectoryInputWithIncremental(directoryInput, outputProvider, isIncremental) } } } void processJarInputWithIncremental(JarInput jarInput, TransformOutputProvider outputProvider, boolean isIncremental) { File dest = outputProvider.getContentLocation( jarInput.getFile().getAbsolutePath(), jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR) if (isIncremental) { // 处理增量编译 processJarInputWhenIncremental(jarInput, dest) } else { // 不处理增量编译 processJarInput(jarInput, dest) } } void processJarInput(JarInput jarInput, File dest) { transformJarInput(jarInput, dest) } void processJarInputWhenIncremental(JarInput jarInput, File dest) { switch (jarInput.status) { case Status.NOTCHANGED: break case Status.ADDED: case Status.CHANGED: // 处理有变化的 transformJarInputWhenIncremental(jarInput.getFile(), dest, jarInput.status) break case Status.REMOVED: // 移除Removed if (dest.exists()) { FileUtils.forceDelete(dest) } break } } void transformJarInputWhenIncremental(JarInput jarInput, File dest, Status status) { if (status == Status.CHANGED) { // Changed的状态需要先删除之前的 if (dest.exists()) { FileUtils.forceDelete(dest) } } // 真正transform的地方 transformJarInput(jarInput, dest) } void transformJarInput(JarInput jarInput, File dest) { // to do some transform // 将修改过的字节码copy到dest,就可以实现编译期间干预字节码的目的了 FileUtils.copyFile(jarInput.getFile(), dest) } void processDirectoryInputWithIncremental(DirectoryInput directoryInput, TransformOutputProvider outputProvider, boolean isIncremental) { File dest = outputProvider.getContentLocation( directoryInput.getFile().getAbsolutePath(), directoryInput.getContentTypes(), directoryInput.getScopes(), Format.DIRECTORY) if (isIncremental) { // 处理增量编译 processDirectoryInputWhenIncremental(directoryInput, dest) } else { processDirectoryInput(directoryInput, dest) } } void processDirectoryInputWhenIncremental(DirectoryInput directoryInput, File dest) { FileUtils.forceMkdir(dest) String srcDirPath = directoryInput.getFile().getAbsolutePath() String destDirPath = dest.getAbsolutePath() Map fileStatusMap = directoryInput.getChangedFiles() fileStatusMap.each { Map.Entry entry -> File inputFile = entry.getKey() Status status = entry.getValue() String destFilePath = inputFile.getAbsolutePath().replace(srcDirPath, destDirPath) File destFile = new File(destFilePath) switch (status) { case Status.NOTCHANGED: break case Status.REMOVED: if (destFile.exists()) { FileUtils.forceDelete(destFile) } break case Status.ADDED: case Status.CHANGED: FileUtils.touch(destFile) transformSingleFile(inputFile, destFile, srcDirPath) break } } } void processDirectoryInput(DirectoryInput directoryInput, File dest) { transformDirectoryInput(directoryInput, dest) } void transformDirectoryInput(DirectoryInput directoryInput, File dest) { // to do some transform // 将修改过的字节码copy到dest,就可以实现编译期间干预字节码的目的了 FileUtils.copyDirectory(directoryInput.getFile(), dest) } void transformSingleFile(File inputFile, File destFile, String srcDirPath) { FileUtils.copyFile(inputFile, destFile) }}
延伸阅读1:TransformInput
TransformInput是指输入文件的一个抽象,包括:
DitectoryInput集合:是指以源码的方式参与项目编译的所有目录结构及其目录下的源码文件JarInput集合:是指以jar包方式参与项目编译的所有本地jar包和远程jar包(此处的jar包包括aar)猜你喜欢LIKE
相关推荐HOT
更多>>关系型数据库中的字段默认值、不可为空、少数索引约束的优缺点是什么?
一、关系型数据库中的字段默认值、不可为空、少数索引约束的优缺点1.字段默认值:针对每个字段都有自己的默认值,较有利于进行统计和分析,以及...详情>>
2023-10-20 21:56:39Gradle Transform到底是什么怎么用?
一、Gradle Transform到底是什么Gradle Transform是Android官方提供给开发者在项目构建阶段(.class -> .dex转换期间)用来修改.class文件的一...详情>>
2023-10-20 20:24:09MyBatis和jOOQ有哪些区别?
一、MyBatis和jOOQ的区别1、数据库操作风格不同MyBatis是一种基于XML或注解配置的SQL映射框架。它通过编写SQL语句,并使用对象映射将结果集映射...详情>>
2023-10-20 19:06:20ACTION_CANCEL到底何时触发,滑出子View范围会发生什么?
一、ACTION_CANCEL在这些时候会触发1、父view拦截事件首先要了解ViewGroup什么情况下会拦截事件,请看下面一段代码:@Overridepublic boolean d...详情>>
2023-10-20 11:22:41热门推荐
在mysql中, 为什么只有右模糊才走索引?
沸为什么声明性语言往往适合于并行执行,命令代码很难在多个内核和多个机器之间并行化?
热SQL语言中的ALTER和UPDATE,DROP和DELETE都有什么区别?
热关系型数据库中的字段默认值、不可为空、少数索引约束的优缺点是什么?
新MySQL多表关联查询效率高点还是多次单表查询效率高,为什么?
jmeter性能测试步骤?
Gradle Transform到底是什么怎么用?
Excel与数据库有什么不同?
MyBatis和jOOQ有哪些区别?
什么是web前端?
一个大型的SNS网站,是否适合数据库全部用mongodb来做,为什么?
在数据库查询的底层实现上SQL Server和MySQL的区别是什么?
外企银行一般用什么linux版本系统和数据库呢?
neo4j有什么缺点?