【我的Android进阶之旅】字节码插桩之ASM和transform的使用

前言

什么是字节码插桩

字节码插桩就是在构建的过程中,通过修改已经编译完成的字节码文件,也就是class文件,来实现功能的添加

从技术上来说,字节码插桩是自定义Gradle插件、ASM、Java字节码、切面编程的综合应用

字节码插桩可以做什么

举个例子,APP全量统计的时候,经常需要建立很多埋点。这是个很大重复性工作,那么可以通过字节码插桩,在apk打包之前,对class文件需要的地方进行埋点。这样就可以实现无埋点的全量统计。

下面我们来逐一介绍用到的知识,可能需要学习很多东西,学完这些将会有很大的收货!!

一、切面编程 AOP

AOP(Aspect Oriented Program的首字母缩写)是一种面向切面编程的思想。这种编程思想是相对于OOP(ObjectOriented Programming即面向对象编程)来说的。

先来说一下大家熟悉的面向对象编程:面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。

但是存在一个问题,如果每个类中都需要同样的功能,例如日志,统计等。这个是面向对象的编程天生的缺点,就是分散代码的同时,也增加了代码的重复性。按照OOP的思想,我们需要在各个模块里面都添加统计代码。但是如果按照AOP的思想,可以将统计的地方抽象成切面,只需要在切面里面添加统计代码就OK了。

字节码插桩是AOP编程一种很好的实现方式,在后台开发的Spring框架中已经在使用切面编程来添加操作日志记录在这里插入图片描述

二、APK打包流程

在APK打包的时候,我们要对字节码进行修改,那么就需要了解整个打包流程,知道在哪个过程中可以获取到字节码。

官网的打包流程介绍的不是很详细,下图介绍了详细的打包流程。

apk打包使用的工具是gradle,Android 提供了Gradle插件 com.android.tools.build:gradle,使得我们可以轻松执行这个打包流程
在这里插入图片描述

经过“Java Compiler步骤”,也就是代码编译,系统便生成了.class文件。这些class文件经过dex步骤再次转化成Android识别的.dex文件。

既然我们要做字节码插桩,就必须hook打包流程,在dex步骤之前对class字节码进行扫描与重新编织,然后将编织好的class文件交给dex过程。这样就实现了所谓的无埋点。

那么问题来了,怎么才能在打包流程中,添加我们想要执行的操作。也就是说如何才能拦截住打包流程呢? 我们下面分解

三、自定义Gradle插件

整个打包流程是由Android Gradle插件 com.android.tools.build:gradle提供的,在1.5.0-beta1 及以后的版本,添加了Transform API ,允许第三方Gradle 插件,在打包为dex 之前,可以对class进行操作。(这些书都有记载,不是我在乱掰。详见官网

那么由此引出两个知识点,介绍这两个的篇幅有点长,所以列出一下网上比较好的文章

3.1、自定义Gradle插件

自定义Gradle插件 官方文档:Developing Custom Gradle Plugins

在AndroidStudio中自定义Gradle插件
拥抱 Android Studio 之五:Gradle 插件开发

对以上两边文章,有两点补充:

1、关于自定义Gradle插件 ,网上中文文档,基本都还是在介绍使用使用groovy,现在已经支持使用kotlin来编写Gradle插件。两者只在目录方面有些差异。最后的demo就是使用kotlin来编写Gradle插件的

2、在build.gradle 文件中,通常会出现 apply plugin: 'com.android.application' 这里apply plugin 的是groovy 语言调用函数的方式,这句代码会调用com.android.application插件的apply() 函数

3.2、如何使用Transform API

Transform详解

四、Java字节码

Java 字节码(英语:Java bytecode)是Java虚拟机执行的一种指令格式。通俗来讲字节码就是经过javac命令编译之后生成的Class文件。Class文件包含了Java虚拟机指令集和符号表以及若干其他的辅助信息。Class文件是一组以8位字节为基础单位的二进制流,所有数据项目严格按照顺序紧凑的排列在Class文件之中,中间没有任何分隔符,这使得整个Class文件中存储的内容几乎全是程序运行时的必要数据。

通俗的说就是,Java代码编译后生成的Class文件,这个文件是二进制。这个文件有个规定,第几位到第几位是什么数据,

4.1、关于字节码几个重要的内容::

1、 Class文件中使用全限定名来表示一个类的引用,全限定名很容易理解,即把类名所有“.”换成了“/”

例如:
android.widget.TextView 的全限定名 android/widget/TextView

2、描述符
Class文件中使用描述符,描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。

  1. 基本数据类型(byte char double float int long short boolean)以及代表无返回值的void类型都用一个大写字符( Type Signature)来表示
  2. 对象类型则用字符“L”加对象的全限定名来表示,一般对象类型末尾都会加一个“;”来表示全限定名的结束。

类型签名

Type SignatureJava Type
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
Lfully-qualified-class ;fully-qualified-class
[ typetype[]
( arg-types ) ret-typemethod type
4.2、Java字节码

一文让你明白Java字节码

看了字节码,相信你一定有疑问,是不是对class文件修改很复杂呀?其实根本没有这么复杂,而且已经有很多支持字节码编织的框架,学习字节码,是为了在使用框架修改字节码时更上手一点

五、 Java 字节码编织框架——ASM

什么是ASM?

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

看完下面这篇文章,基本可以直接上手使用ASM框架了,通过这个框架,我们可以修改class文件,增加函数,修改函数、各种逻辑等等编程操作。

AOP 的利器:ASM 3.0 介绍,这篇文章有点过时,但是基本的原理没变,所以可以学习其思想

ASM官方文档

虽然有了ASM这种框架,可以很方便的修改class文件,但是如果不熟悉框架的使用,写起来还是有点吃力
人类总是懒惰的,试图找出一些捷径,于是有了一款Idea插件——ASM Bytecode Outline

如果想深入学习ASM,可以查看这个系列的文章:
1.1 ASM-简介-目的

ASM Bytecode Outline

插件ASM Bytecode Outline,可以把java代码转为ASM框架 的代码,那么我们可以先修改好一个类的代码,把代码转为ASM框架的代码,然后把需要的代码复制到,这样就可以在自定义的gradle plugin 中批量自动去修改目标类了。

参考:
Android字节码插桩采坑笔记
通过自定义 Gradle 插件修改编译后的 class 文件

ASM官方文档
https://www.sharezer.com/archives/1574

JVM的类型签名对照表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值