在完成javassist迁移(适配)到android后,就可以考虑使用javassist在运行时生成类并使用android中的动态加载机制将其加载运行,以下简述demo的做法。
整体的思路是这样子的,首先我考虑动态生成一个Activity,将其生成为.class,然后再转换为dex再使用DexClassLoader将其加载进内存。
Activity的全路径默认为cn.demonk.dynamic.TestActivity,首先使用javassist4android获取到一个pool及生成一个CtClass,以后我们的操作都可以针对CtClass对象来操作,就像操作一个真实的class类一样。
1 | public static final String PACKAGE_NAME = "cn.demonk.dynamic"; |
activity需要有一个Activity的父类,由于mPool是适用android的pool,其可以从android的运行时环境中获取到android的类,获取到类对象后,就可以将其作为父类设置到另一个类中。
1 | CtClass activityClass = mPool.get("android.app.Activity"); |
虽然Activity一般无构造方法,为了验证功能还是简单写了一个构造方法,方法比较简单,生成后里面是没内容,像修饰符什么的也好理解。
1 | private CtConstructor createConstructor() throws CannotCompileException { |
接着构建一个onCreate方法,以下代码我使用了直接使用字符来构建一个方法的方法,在编辑好需要生成的方法声明及方法体后,使用CtMethod的make方法就可以针对CtClass生成一个方法。
需要注意的是,为了类查找时可以正确查找类,引用其他类时建议使用全路径。
1 | private CtConstructor createConstructor() throws CannotCompileException { |
如此,就可以生成一个有父类,有一个构造方法一个成员方法的类,接着,使用CtClass提供的writeFile即可以将生成的类写出到文件中。
1 | mClass.writeFile(path); |
生成后的类是.class的正常java字节码文件,使用jd-gui打开可以看到结构。
生成到class后,就可以使用dx.jar将class转换成dex格式,首先需要引用dx的源码,源码在系统的dalvik/dx中。直接引用源码比较麻烦,需要修改些东西适配,所以我直接使用了android sdk中的dx.jar引入。
首先需要生成一个javassist.android.DexFile的文件描述,用于描述我们将要生成的dex文件,其内主要是使用dx.jar中的com.android.dx.dex.file.DexFile。
1 | private final com.android.dx.dex.file.DexFile file; |
然后添加一个方法,为了将上面生成的class添加到dexFile对象中。
1 | public void addClass(String path, String pkgName, String className) { |
方法很简单,主要是读取刚生成的class文件的二进制,然后使用bytecode与class的路径来生成一个DirectClassFile对象(这个视dx.jar的版本而家,在旧版本中是不存在这个类的)。需要注意的是,在生成DirectClassFile时,dx会从bytecode中解析出类的全路径名(包+类名),并判断全路径名是否与传入的filePath前缀匹配(即DirectClassFile的第二个参数),且第二个参数必须以.class结尾,否则会校验失败。
使用方法按参数传入就可以了,class文件的所在目录,包名,类名。
最后写出DexFile对象就可以了。
1 | public void writeFile(String filePath) throws IOException { |
如此就生成了一个包含自定义动态生成class的dex。最后使用DexClassLoader加载dex即可。不写。
demo地址:demo for javassist
另外,android上还有更方便的dexmaker可以使用,后续学习。