###一、原理介绍

1、App构建是将代码编译为.class文件,然后打包成dex文件之后输出apk

2、Gradle构建App由一个个Task组成,每个Task作用实际上是接收一个输入(编译App所需的资源)然后进行处理然后有一个输出

3、Gradle1.5以后提供了transform-api可以在代码转化为.class文件之后再打包成dex文件之前对它进行处理

4、Javassist可以处理.class文件

所以我们可以通过自定义gradle插件的方式利用javassist在打包的过程中修改.class文件,这样编译出来的apk文件中就会是我们修改过的class

###二、Transfrom-API介绍

538f6c7f6925

getName:用于指明本Transform的名字,这个 name 并不是最终的名字,在TransformManager 中会对名字再处理

getInputTypes:用于指明Transform的输入类型,可以作为输入过滤的手段

–CLASSES表示要处理编译后的字节码,可能是 jar 包也可能是目录

–RESOURCES表示处理标准的 java 资源

getScopes:用于指明Transform的作用域

–PROJECT                         只处理当前项目

–SUB_PROJECTS  只处理子项目

–PROJECT_LOCAL_DEPS  只处理当前项目的本地依赖,例如jar, aar

–EXTERNAL_LIBRARIES  只处理外部的依赖库

–PROVIDED_ONLY  只处理本地或远程以provided形式引入的依赖库

–TESTED_CODE                         只处理测试代码

isIncremental:用于指明是否是增量构建。

transform:核心方法,用于自定义处理,在这个方法中我们可以拿到要处理的.class文件路径、jar包路径、输出文件路径等,拿到文件之后就可以对他们进行操作

利用Transform-api处理.class文件有个标准流程,拿到输入路径->取出要处理的文件->处理文件->移动文件到输出路径

538f6c7f6925

Transform-api处理流程

上图展示的代码中没有包含处理过程,我们只需要在FileUtils.copy函数之前对拿到的文件进行处理即可

###三、javassist介绍

介绍:Javassist是一个动态类库,可以用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

常用类:

ClassPool:javassist的类池,使用ClassPool类可以跟踪和控制所操作的类,它的工作方式与 JVM类装载器非常相似,

CtClass: CtClass提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。不

过,    Javassist 并未提供删除类中字段、方法或者构造函数的任何方法。

CtField:用来访问域

CtMethod :用来访问方法

CtConstructor:用来访问构造器

基本用法

1、添加类搜索路径

ClassPool pool =ClassPool.getDefault();

pool.insertClassPath("/usr/local/javalib");

2、添加方法

CtClass point =ClassPool.getDefault().get("Point");

CtMethod m =CtNewMethod.make( "public int xmove(int dx) { x += dx; }", point);point.addMethod(m);

3、修改方法

CtClass point =ClassPool.getDefault().get("Point");

CtMethod m= point.getDeclaredMethod(“show", null)

m.insertAfter(“System.out.prinln(“x:” + x + “,y:) + y”))

4、添加字段

CtClass point =ClassPool.getDefault().get("Point");

CtField f = newCtField(CtClass.intType, "z", point);

point.addField(f);

###四、自定义Gradle插件

自定义插件可以直接新建一个名为buildSrc的module不过这样的插件只能自己工程用,还有另外一种方式可以发布到jcenter提供别别人用也可以发布到本地自己使用。这里是第二种方式的步骤:这里是原文

1、新建一个 Module,此Module 用于开发插件,类型选什么都无所谓,后面会大改。

2、在 Project 目录视图模式下,清空build.gradle 文件的内容,删除其余的所有文件。

3、然后在 module 中新建多个文件夹 src/main/groovy ,再新建包名文件夹。 在 main 目录下再新建resources 目录,在resources 目录下再新

建 META-INF 文件夹,再新建文件夹gradle-plugins,这样就完成了 gradle插件的目录结构搭建

538f6c7f6925

Module文件结构

4、打开 build.gralde 文件,替换全部内容

538f6c7f6925

Build.gradle内容

5、编写插件内容。在刚刚新建的包名下 再次新建一个文件 MyPlugin.groovy ,注意文件类型,一定是 groovy 类型文件

6、在resources/META-INF/gradle-plugins 目录下新建一个 .properties 文件,注意该文件的命名就是你使用此插件时的名称,这里命名

为 com.app.myplugin.properties ,一定要注意后缀名称,那么使用时的名称就是com.app.myplugin,文件里面的内容填写如下:     implementation-class=com.app.plugin.MyPlugin,这里是 key = value 的形式,值就是刚刚自定义的 插件( groovy 文件 )的全名,也就是径加上类名称。

7、发布插件到本地,修改build.gradle文件,这个时候右侧的 gradle Toolbar 就会在module下多出一个task :upload-uploadArchives。 clean工程然后运行upload-uploadArchives

538f6c7f6925

发布插件

8、引用插件

538f6c7f6925

引用插件

a、修改工程根目录下的build.gradle

b、在module的目录下的build.gradle中添加

apply plugin:'com.app.plugin.myplugin' //这里就填写.properties 文件的名称

###五、实操

我们可以按照上面的步骤进行一次demo编写,这里要做的功能是在代码中所有的View.OnClickListener类的onClick方法中插入一个Toast,在每次编译之后我们可以到文件夹D:\as_workspace\sample\javassist\app\build\intermediates\transforms\ModifyTransform\debug下查看.class文件是否修改成功,D:\as_workspace\sample\javassist\这个是代码工程目录ModifyTransform是自定义Gradle插件的名称。修改class文件在实际中还是挺有用处的,比如我们可以用来做无埋点或者修改第三方jar包中的bug。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐