背景
最近将我们项目的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处理的地方,在后面增加一段处理即可。
网友评论