记一次gradle 插件开发 分析 android studio Unable to delete file 异常

代码传送门

 

在用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)

  

编译后文件完美删除   

 

上一篇:Eclipse: Unable to build: the file dx.jar was not loaded from the SDK folder!


下一篇:异步IO ORA-27090: Unable to reserve kernel resources for asynchronous disk I/O