在用javassist 开发gradle插件时
可能很多人碰到这个问题 第一个想到的重启idea 网上比较多的办法就是
1. 重启android studio
2.任务管理器里面杀掉java进程
但是 每次重新编译的时候都会出现这个问题,总不能每次冷编译都重启 android studio把,很多人都说是as的bug,但感觉不应该,不然谷歌应该早修复了
对照任务管理器 发现插件编译的时候多了好几个java进程,应该是删不掉的文件被这几个进程占用了。
Gradle 守护进程(有时也称为构建守护进程) 的目的是改善 Gradle 的启动和执行时间。 那么会不会是gradle 守护进程的原因呢
于是尝试用gradle stop 来杀掉 守护进程 然后重新gradle clean 发现文件能删掉了 守护进程默认能存活4小时 总不能编译一次等4小时把 我还不如重启
于是又想到 能不能在gradle 编译结束的时候启动一个task直接杀掉守护进程 gradle本身可以直接用groove语言 可以直接在需要监听的task 加上doLast的监听
问题又来了 并行开发肯定不只是在wins mac上如果也碰到类似问题呢
继续定位问题 一个个文件来排查 一开始以为是kotlin的问题
单java 单kotlin 多类 一次次编译 分析 发现 如果是类继承了系统类的话 就是造成这个错误
总结下来 应该时 某个类如果引用到了 第三方jar库中的包 编译执行完 进程会继续持有该jar包 导致文件被占用而无法删除 定位到问题了 那么目标就是怎么让进程释放对jar的持有
于是问题变成了 javassist 类加载器 怎么释放 对jar的引用
Jar 在Java中是用URLClassloader来加载的 现在项目中是用javassist默认的加载器 没法直接拿到加载jar的类加载器
mClassPool.appendClassPath(new JarClassPath(jarInput.getFile().getAbsolutePath()));
那么是否可以自定义 类加载器来加载jar呢 通过分析javassist源码 找到了
VisitableURLClassLoader 他是实现Urlclassloader的一个类加载器
自定义类加载器
urls = new URL[input.getJarInputs().size()]; int len = input.getJarInputs().size(); ClassLoader parent; for (int i=0;i<len;i++) { JarInput jarInput = input.getJarInputs().get(i); jarSet.add(jarInput) urls[i] = jarInput.getFile().toURI().toURL() } parent = mClassPool.getClassLoader().getParent() jarLoader= new VisitableURLClassLoader("third jar",urls,parent); mClassPool.appendClassPath(new LoaderClassPath(jarLoader))
所有操作处理完后 释放jar
ClassLoaderUtils.tryClose(jarLoader)
编译后文件完美删除