Android通过javassist修改类

###一、原理介绍

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介绍

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文件有个标准流程,拿到输入路径->取出要处理的文件->处理文件->移动文件到输出路径


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插件的目录结构搭建


Module文件结构

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


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


发布插件

    8、引用插件


引用插件

      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。

代码:https://github.com/yaozhukuang/javassist

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容