第4讲 编译插桩操纵字节码

##第4讲 编译插桩操纵字节码

拉勾教育:https://kaiwu.lagou.com/course/courseInfo.htm

这一讲的内容对我来说挺新鲜的,编译插桩只听过这个词,并一直认为是一项高不可及的黑科技,看完这节课,感觉还是没那么可怕的。这节课举了一个例子手把手地说明了怎么实现编译插桩。这里记录一下思路,具体代码看课程。

0、需求

先说一下需求,不然说了半天都不知道用这个插桩来干什么。

需求:

记录每一个页面的打开和关闭事件,并通过各种 DataTracking 的框架上传到服务器,用来日后做数据分析。

实际演示的需求是在每个activity的onCreate方法中打印一行日志

面对这样的需求,一般人都会想到,这其实就是在每一个 Activity 的 onCreate 和 onDestroy 方法中,分别添加页面打开和页面关闭的逻辑。常见的做法有以下两种:

  1. 修改项目中现有的每一个 Activity,这样显然不够高大上,并且如果项目以后需要添加新的页面,这套逻辑需要重新拷贝一遍,非常容易遗漏。
  2. 将项目中所有的 Activity 继承自 BaseActivity,将页面打开和关闭的逻辑添加在 BaseActivity中,这种方案看起来比第 1 种方案高级得多,并且后续项目中有新的 Activity,直接继承 BaseActivity 即可。但是这种方案对第三方依赖库中的界面则无能为力,因为我们没有第三方依赖库的源码。

就是在这种环境下,一种更加优雅更加完整的方案应运而生:编译插桩

一、编译插桩是什么

编译插桩就是在代码编译期间修改已有的代码或者生成新代码。

先回顾一下 Android 项目中 .java 文件的编译过程:
在这里插入图片描述

从上图可以看出,我们可以在 1、2 两处对代码进行改造。

  • 在 .java 文件编译成 .class 文件时,APT、AndroidAnnotation 等就是在此处触发代码生成。
  • 在 .class 文件进一步优化成 .dex 文件时,也就是直接操作字节码文件,也是本课时主要介绍的内容。这种方式功能更加强大,应用场景也更多。但是门槛比较高,需要对字节码有一定的理解

课程主要介绍第 2 种实现方式,过程如下示意图:
在这里插入图片描述

二、插桩工具介绍

目前市面上主要流行两种实现编译插桩的方式:

1、AspectJ(我表示没听过)

AspectJ 是老牌 AOP(Aspect-Oriented Programming)框架,如果你做过 J2EE 开发可能对这个框架更加熟悉,经常会拿这个框架跟 Spring AOP 进行比较。其主要优势是成熟稳定,使用者也不需要对字节码文件有深入的理解。

2、ASM(我仅见过这个词)

目前另一种编译插桩的方式 ASM 越来越受到广大工程师的喜爱。通过 ASM 可以修改现有的字节码文件,也可以动态生成字节码文件,并且它是一款完全以字节码层面来操纵字节码并分析字节码的框架。

ASM 是一套开源框架,其中几个常用的 API 如下:

ClassReader:负责解析 .class 文件中的字节码,并将所有字节码传递给 ClassWriter。

ClassVisitor:负责访问 .class 文件中各个元素,还记得上一课时我们介绍的 .class 文件结构吗?ClassVisitor 就是用来解析这些文件结构的,当解析到某些特定结构时(比如类变量、方法),它会自动调用内部相应的 FieldVisitor 或者 MethodVisitor 的方法,进一步解析或者修改 .class 文件内容。

ClassWriter:继承自 ClassVisitor,它是生成字节码的工具类,负责将修改后的字节码输出为 byte 数组。

二、实际思路

1、 将所有的.class文件找出来

怎么找?

通过自定义gradle插件来实现(具体是通过自定义插件内的 Transform来实现)

什么是 Transform ?

Transform 可以被看作是 Gradle 在编译项目时的一个 task,在 .class 文件转换成 .dex 的流程中会执行这些 task,对所有的 .class 文件(可包括第三方库的 .class)进行转换,转换的逻辑定义在 Transform 的 transform 方法中。实际上平时我们在 build.gradle 中常用的功能都是通过 Transform 实现的,比如混淆(proguard)、分包(multi-dex)、jar 包合并(jarMerge)。

2、从所有class文件中将目标class文件找出来,并在目标class文件中的目标方法内插入目标字节码

也就是找出activity对应的class文件,并在该文件中找到onCreate方法,然后在方法内插入打印一行日志的字节码
这一步通过ASM来实现

具体的代码怎么写就不贴了,课程里有源码,个人认为理清思路比记住源码更重要,也更容易记住。虽然代码也很重要,但记忆力是有限的,特别是像咱这种“老年人”,只好选择性地记一些东西。




由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享、探讨!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email: MingHuang1024@foxmail.com

.com/MingHuang1024](https://github.com/MingHuang1024)

Email: MingHuang1024@foxmail.com

微信:724360018

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值