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

问题背景

通过 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 踩坑 -- javassist.NotFoundException: broken jar file?: xxx class)