Javassit ClassPool.getDefault的问题
在Android开发中,我们经常通过Gradle Plugin配合Android Gradle Plugin提供的Tranform API,并应用Javassit字节码编辑库在Android打包过程中做一些特殊操作。例如:自动埋点,热修复等。
Javassit提供了一个方便获取ClassPool
的方法,ClassPool.getDefault()
,它是个单列对象
。
当你在使用assemble
命令打包你的Android应用时,默认会执行assembleDebug
和assembleRelease
,如果你增加了定义的buildTypes或者flavors,所有的assembleXXX
命令都会执行。因此assemble
多次调用void transform(TransformInvocation transformInvocation)
方法。
此时,如果你是使用ClassPool.getDefault()
来存放你需要操作的Class,并且在自定义Transform
中对CtClass
应用writeFile()
,toClass()
或者toByteCode()
方法将其转换成Class文件,那么Javassist就会冻结(frozen)这个CtClass
对象,之后就不能修改这个CtClass
对象了。所以transform
方法第二次执行时,我们在对ClassPool.getDefault()
里面的CtClass
做writeFile()
,toClass()
或者toByteCode()
操作就会发生xxx class is frozen.
的错误。
Javassit的此异常是为了警告开发者不要修改已经被JVM加载的class文件,因为JVM不允许重新加载一个类。
解决方法:不要使用ClassPool.getDefault()
来获取ClassPool
,通过ClassPool classPool = new ClassPool(true)
的方式自己创建,因为每次都是新创建的ClassPool,所以在执行assemble
后多次调用void transform(TransformInvocation transformInvocation)
方法不会出现上述异常。