美文网首页
gradle升级带来的java.lang.NoClassDefF

gradle升级带来的java.lang.NoClassDefF

作者: InnerNight | 来源:发表于2022-03-22 16:11 被阅读0次

    背景

    最近将我们项目的android gradle plugin(以下简称agp)以及gradle版本进行了升级,之前因为依赖集团内部的打包插件,agp版本还停留在很老的3.4.2版本。这次先升级到了4.0.2版本(对应gradle版本为6.1.1)后,遇到了打出来的包会报错R文件相关产物缺失的运行时崩溃。

    问题原因与解决方案

    关于这个问题,首先在google上搜到了这样一个文章,感觉是类似的问题:
    java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/appcompat/R$drawable;
    其中的原因是在agp版本3.6.0-alpha01之后,R文件不在跟别的文件一起生成R.java,而是单独生成R.jar产物,因此对于一些gradle plugin,需要单独将这个产物一起输出。而在适配比较老的agp版本的很多gradle plugin里,这点都被忽略了。
    因此,如果你在gradle升级时遇到了这个问题,第一步应该是先确定当前项目所依赖的gradle plugin哪个插件引入了这个问题,然后就想上面的文章中所提到的那样,去升级这个plugin到适配了3.6.0以上agp的版本

    ========== 大部分人的分割线 ===========

    对大部分安卓开发者来说不需要关心下面的内容。

    自己动手修改plugin适配更高版本agp

    但是如果做完以上步骤后,你和我一样发现出问题的plugin已经没有更新的版本了,或者不再维护了,而你需要自己手动来改。这时,才需要阅读下面的内容。
    首先,我找到了前面文章中提到的realm项目,翻阅了它的release notes和issues后,找到了当时这个项目升级适配的改动,并且照着他们的改动对我们的plugin进行了修改。以下是核心代码:

        @Override
        public void transform(TransformInvocation invocation)
                throws IOException, TransformException, InterruptedException {
            for (TransformInput input : invocation.getInputs()) {
                for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
                    // ... 原先的逻辑,处理所有的directory输入
                }
    
                // 增加的逻辑,对于jar类型的输入进行处理,一般就是拷贝到目标目录即可
                for (JarInput jarInput : input.getJarInputs()) {
                    File inputFile = jarInput.getFile()
                    // ===========  核心代码块 start =====================
                    File outputDir = invocation
                            .getOutputProvider()
                            .getContentLocation(
                                    jarInput.getName(),
                                    getOutputTypes(),
                                    getScopes(),
                                    Format.JAR);
                    if (inputFile == null || !inputFile.exists() || outputDir == null) {
                        continue
                    }
                    Files.createParentDirs(outputDir);
                    Files.write(Files.asByteSource(inputFile).read(), outputDir);
                    // ===========  核心代码块 end =====================
                }
            }
        }
    

    这个改动的点在于增加一段对于jar类型文件的拷贝处理。这里我参考了原来我们项目中的写法,和realm项目的改动在写法细节上不太一致,但是基本原理是一样的。在着手改动时,首先找到之前directoryInput处理的地方,在后面增加一段处理即可。

    相关文章

      网友评论

          本文标题:gradle升级带来的java.lang.NoClassDefF

          本文链接:https://www.haomeiwen.com/subject/ychtjrtx.html