欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

gradle 踩坑 -- javassist.NotFoundException: broken jar file?: xxx class

程序员文章站 2022-04-29 15:12:23
...

问题背景

通过 gradle 构建编译时, 在切换分支或代码变更较大后, 经常出现 javassist.NotFoundException: broken jar file? 的编译失败错误, 一直以来只能靠重启 Android Studio 来解决, 非常痛苦. 最近花了些时间终于解决了这个问题.

原因分析过程

* 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

首先查看相关源码分析:

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;
    }

报错"broken jar file?" 是由于发生了IOException, 这个异常通常跟文件读取有关的, 猜测是打开 jar 文件失败引起的, 那么这个 jar 文件可能是因为被其他进程占用了. 通过 lsof 命令查看jar文件占用, 过滤出 jar 文件占用的进程.

lsof | egrep '\.jar$'

这里列出部分跟当前 project 相关的 jar 占用进程信息:

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

发现是pid 为 23717 的 java 进程占用了当前 project 中多个编译时生成的 jar 文件, 通过命令 kill 23717 杀掉这个进程后, 再重新编译就正常了.

那这个 java 进程到底是谁启动的呢? 既然是 java 进程,我们可以通过 jps 命令查看:

> jps
23717 GradleDaemon
23847 KotlinCompileDaemon
23963 Jps

可以看出这是一个 gradle 守护进程.

到这里,问题的原因就找到了, 是 gradle 的坑:

gradle 守护进程一直持有 jar 文件句柄, 编译进程无法打开读取 jar 文件导致编译失败.

以前关闭 Android Studio 的解决方法能够生效是因为, 关闭 Android Studio 也会同时关闭 gradle 守护进程.

解决方法

  • 方法一(推荐): 在编译前通过 ./gradlew --stop 命令停止守护进程.

在切换分支或代码发生较大变更时, 开发需要有意识的执行命令停止守护进程.

  • 方法二: 默认关闭守护进程, 修改 gradle 配置:
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties

缺点:

  1. 每次编译都比较慢, 无法享有守护进程提高编译速度的特性;
  2. 仅限命令行执行编译有效, android studio 中Run仍然会开启守护进程
相关标签: gradle