問題背景
通過 gradle 構(gòu)建編譯時(shí), 在切換分支或代碼變更較大后, 經(jīng)常出現(xiàn) javassist.NotFoundException: broken jar file? 的編譯失敗錯(cuò)誤, 一直以來只能靠重啟 Android Studio 來解決, 非常痛苦. 最近花了些時(shí)間終于解決了這個(gè)問題.
原因分析過程
* What went wrong:
Execution failed for task ':app:transformClassesWithXXXXJarMergingForDebugQA'.
> javassist.NotFoundException: broken jar file?: com.xx.BaseRecyclerAdapter
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
首先查看相關(guān)源碼分析:
public InputStream openClassfile(String classname)
throws NotFoundException
{
URL jarURL = find(classname);
if (null != jarURL)
try {
java.net.URLConnection con = jarURL.openConnection();
con.setUseCaches(false);
return con.getInputStream();
}
catch (IOException e) {
throw new NotFoundException("broken jar file?: "
+ classname);
}
return null;
}
報(bào)錯(cuò)"broken jar file?" 是由于發(fā)生了IOException, 這個(gè)異常通常跟文件讀取有關(guān)的, 猜測(cè)是打開 jar 文件失敗引起的, 那么這個(gè) jar 文件可能是因?yàn)楸黄渌M(jìn)程占用了. 通過 lsof 命令查看jar文件占用, 過濾出 jar 文件占用的進(jìn)程.
lsof | egrep '\.jar$'
這里列出部分跟當(dāng)前 project 相關(guān)的 jar 占用進(jìn)程信息:
java 23717 luliang 504r REG 1,4 64883 8625155549 /Users/luliang/git/$myProjectXXX/app/build/intermediates/transforms/TrafficStats/debugQA/111.jar
java 23717 luliang 527r REG 1,4 422787 8625152311 /Users/luliang/git/$myProjectXXX/app/build/intermediates/transforms/GsonJarTransform/debugQA/56.jar
java 23717 luliang 542r REG 1,4 22062 8625152326 /Users/luliang/git/$myProjectXXX/app/build/intermediates/transforms/GsonJarTransform/debugQA/71.jar
java 23717 luliang 568r REG 1,4 475187 8625152557
發(fā)現(xiàn)是pid 為 23717 的 java 進(jìn)程占用了當(dāng)前 project 中多個(gè)編譯時(shí)生成的 jar 文件, 通過命令 kill 23717 殺掉這個(gè)進(jìn)程后, 再重新編譯就正常了.
那這個(gè) java 進(jìn)程到底是誰(shuí)啟動(dòng)的呢? 既然是 java 進(jìn)程,我們可以通過 jps 命令查看:
> jps
23717 GradleDaemon
23847 KotlinCompileDaemon
23963 Jps
可以看出這是一個(gè) gradle 守護(hù)進(jìn)程.
到這里,問題的原因就找到了, 是 gradle 的坑:
gradle 守護(hù)進(jìn)程一直持有 jar 文件句柄, 編譯進(jìn)程無(wú)法打開讀取 jar 文件導(dǎo)致編譯失敗.
以前關(guān)閉 Android Studio 的解決方法能夠生效是因?yàn)? 關(guān)閉 Android Studio 也會(huì)同時(shí)關(guān)閉 gradle 守護(hù)進(jìn)程.
解決方法
- 方法一(推薦): 在編譯前通過 ./gradlew --stop 命令停止守護(hù)進(jìn)程.
在切換分支或代碼發(fā)生較大變更時(shí), 開發(fā)需要有意識(shí)的執(zhí)行命令停止守護(hù)進(jìn)程.
- 方法二: 默認(rèn)關(guān)閉守護(hù)進(jìn)程, 修改 gradle 配置:
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
缺點(diǎn):
- 每次編譯都比較慢, 無(wú)法享有守護(hù)進(jìn)程提高編譯速度的特性;
- 僅限命令行執(zhí)行編譯有效, android studio 中Run仍然會(huì)開啟守護(hù)進(jìn)程