上一篇文章里我们 [......插入处理字节码文件的代码]没有写代码对字节码文件进行处理,是原封不动的进行的输出。
这一篇文章我们将对字节码文件进行处理。
java为我们提供了一个字节码操作的API叫ASM,但是需要了解字节码相关的知识。当然也有一个开源的框架javassist,这样框架可以让我们不了解字节码的情况下操作class文件,所以我选择了javassist修改字节码。
使用的时候要引入包
compile 'org.javassist:javassist:3.20.0-GA'
然后我们回到之前的Transform类的transform方法里来,为了代码清晰整洁,我们重新创建一个类(Inject),把操作字节码的代码写在新的类里。
该类里有一个静态方法叫injectDir(String path),我们来解析传入的.class文件然后修改。
class StatisticsInject {
private static ClassPool pool = ClassPool.getDefault()
public static void injectDir(String path) {
pool.appendClassPath(path)
File dir = new File(path)
if(dir.isDirectory()) {
dir.eachFileRecurse { File file ->
String filePath = file.absolutePath
if (filePath.endsWith(".class")
&& !filePath.contains('R$')
&& !filePath.contains('R.class')
&& !filePath.contains("BuildConfig.class")) {
//我们需要过滤出来我们自己包里的类来进行操作
int index = filePath.indexOf("com\\xxx\\xxx")//这里的com\\xxx\\xxx是包名,先写死,后面我们从清单文件里获取
if (index != -1) {
int end = filePath.length() - 6 // .class = 6 把扩展名去掉
String className = filePath.substring(index, end).replace('\\', '.').replace('/','.') //把路径里的/换成点
//这样处理我们就把原先 //D:\workspace\xxx\Example\build\intermediates\transforms\Transformer\debug\folders\1\1\realm\com\aaa\bbb\MainActivity.class
//路径形式转成了com.aaa.bbb.MainActivity这样的形式,然后我们就将该字符串传给另一个方法injectClass来进行处理
injectClass(className)
}
System.out.println("类文件" + filePath)
}
}
}
}
injectClass方法:
private static void injectClass(String className, String path) {
CtClass c = pool.getCtClass(className)
if (c.isFrozen()) {
c.defrost()
}
/**这里进行操作*/
c.writeFile(path)
c.detach()
}
这样我们就有了一个CtClass类的对象,然后我们就可以通过CtClass的API来操作该类修改class文件了。
(关于如何修改我就不做介绍了,相关资料可以查javassist的API
下一篇我们将介绍如何读取清单文件得到我们的包名和过滤出我们的Activity