转载请注明出处:http://blog.csdn.net/llew2011/article/details/78548660
在上篇文章Android 源码系列之<十七>自定义Gradle Plugin,优雅的解决第三方Jar包中的bug<上>中由于篇幅原因我们主要讲解了如何创建自定义Gradle Plugin以及修复第三方Jar包中的bug的思路,如果你还没看过上篇文章,强烈建议阅读一下。这篇文章就带领小伙伴们借助Javassist开源库实现对class文件的修改。
上篇文章中我们讲到了修改第三方Jar包的时机是在BytecodeFixTransform的transform()方法中,也就是在待修改Jar包在被拷贝目标文件夹之前先做修改,修改完成之后我们直接把修改过的Jar包拷贝进目标文件夹而不是原来的Jar包。既然要修改Jar包里的class文件,我们就要知道是哪一个class需要修复,然后还要是class里边的哪一个方法需要修复,还要清楚要修复的内容是什么等等,因此我定义一个BytecodeFixExtension类来表示修复配置,如下所示:
package com.llew.bytecode.fix.extension
public class BytecodeFixExtension {
/**
* 字节码修复插件是否可用,默认可用
*/
boolean enable = true
/**
* 是否开启日志功能,默认开启
*/
boolean logEnable = true
/**
* 是否保留修复过的jar文件,默认保留
*/
boolean keepFixedJarFile = true
/**
* 时候保留修复过的class文件,默认保留
*/
boolean keepFixedClassFile = true
/**
* 构建字节码所依赖的第三方包绝对路径,默认包含了Android.jar文件
*/
ArrayList<String> dependencies = new ArrayList<String>()
/**
* 配置文件集合,配置格式:className##methodName(param1,param2...paramN)##injectValue##injectLine
*/
ArrayList<String> fixConfig = new ArrayList<>();
// 省略了setters and getters 方法
}
在BytecodeFixExtension中需要注意dependencies和fixConfig的配置。dependencies表示在利用Javassist修复class文件时所依赖的Jar包,例如修复上篇文章中提到的getMobileAPInfo()方法就需要引入ContextCompat类,因此需要添加ContextCompat所在Jar包的绝对路径。fixConfig表示修复信息集合,它的格式是固定的,必须以##做分隔符,格式如下:className##methodName(param1,param2...paramN)##injectValue##injectLine,具体字段说明如下所示:
- className:表示全类名
例如:com.tencent.av.sdk.NetworkHelp - methodName(param1,param2...paramN):表示方法名及相关参数,参数只写类型且必须以逗号(,)分隔,非基础数据类型要写全路径
例如:getAPInfo(android.content.Context)
例如:getAPInfo(android.content.Context, int) - injectValue:表示待插入代码块,注意代码块要有分号(;),其中$0表示this;$1表示第一个参数;$2表示第二个参数;以此类推
例如:$1 = null;System.out.println("I have hooked this method by BytecodeFixer Plugin !!!");
$1 = null;就是表示把第一个参数置空;接着是打印一句日志
【注意:】如果injectValue为{}表示给原有方法添加try-catch操作 - injectLine:表示插在方法中的哪一行,该参数可选,如果省略该参数则默认把injectValue插在方法的最开始处
injectLine > 0 插入具体行数
injectLine = 0 插入方法最开始处
injectLine < 0 替换方法体
package com.llew.bytecode.fix.injector
import com.llew.bytecode.fix.extension.BytecodeFixExtension
import com.llew.bytecode.fix.task.BuildJarTask
import com.llew.bytecode.fix.utils.FileUtils
import com.llew.bytecode.fix.utils.Logger
import com.llew.bytecode.fix.utils.TextUtil
import javassist.ClassPool
import javassist.CtClass
import javassist.CtMethod
import org.gradle.api.Project
import java.util.jar.JarFile
import java.util.zip.ZipFile
public class BytecodeFixInjector {
private static final String INJECTOR = "injector"
private static final String JAVA = ".java"
private static final String CLASS = ".class"
private static final String JAR = ".jar"
private static ClassPool sClassPool
private static BytecodeFixInjector sInjector
private Project mProject
private String mVersionName
private Byteco