META-INF/versions/9/module-info.class:损坏的 class 文件? (此功能需要 ASM6)

META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)

我在使用 Bouncycastle 时遇到问题,只有在 运行 :lint 任务时才会出现。

一般好像是Java9字节码版本53.0/ASM版本冲突

这些是依赖项:

// https://mvnrepository.com/artifact/org.bouncycastle
implementation "org.bouncycastle:bcprov-jdk15on:1.64"
implementation "org.bouncycastle:bcpkix-jdk15on:1.64"

导致 :lint 任务抛出处理错误:

> Task :mobile:lint
Error processing bcpkix-jdk15on-1.64.jar:META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)
Error processing bcprov-jdk15on-1.64.jar:META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)

META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)

同样适用于:

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation "com.google.code.gson:gson:2.8.6"

自从 1.4.1 升级到 1.4.2-native-mt 之后,还是一样:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2-native-mt"

kotlin-stdlib-1.4.0.jar:META-INF\versions\module-info.class: broken class file? (Module requires ASM6)

更新:请看我当前的,它解决了问题。
此答案仅作为 Gradle 脚本编写的示例。


使用旧版本时(可能是用 Java 8 构建的),没有这样的处理错误:

// https://mvnrepository.com/artifact/org.bouncycastle
implementation "org.bouncycastle:bcprov-jdk15on:1.60"
implementation "org.bouncycastle:bcpkix-jdk15on:1.60"

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation "com.google.code.gson:gson:2.8.5"

这个问题显然是在版本 1.61 / 2.8.6 中引入的(可能是用 Java 9 构建的)。


当 Google 把人带回自己的答案时,这很烦人,这不是真正的答案。我没有保留版本或编辑 JAR,而是编写了一个 DeleteModuleInfoTask 和一个 shell 脚本,它们自动从任何给定的 Java 依赖项中删除 module-info.class

由于commandLine只接受一个命令,几乎要调用一个脚本。这应该作为自定义 Exec 任务的一个很好的例子。

对于Linux:module_info.sh考虑versions/9/module-info.classmodule-info.class

#!/usr/bin/env bash
GRADLE_CACHE_DIR=$HOME/.gradle/caches/modules-2/files-2.1
ZIP_PATHS=(META-INF/versions/9/module-info.class module-info.class)   
if [[ $# -ne 3 ]]; then
  echo "Illegal number of parameters"
  exit 1
else
  if [ -d "$GRADLE_CACHE_DIR" ]; then
    DIRNAME=${GRADLE_CACHE_DIR}///
    if [ -d "$DIRNAME" ]; then
      cd ${DIRNAME} || exit 1
      find . -name -.jar | (
        read ITEM;
        for ZIP_PATH in "${ZIP_PATHS[@]}"; do
          INFO=$(zipinfo ${ITEM} ${ZIP_PATH} 2>&1)
          if [ "${INFO}" != "caution: filename not matched:  ${ZIP_PATH}" ]; then
            zip ${ITEM} -d ${ZIP_PATH} # > /dev/null 2>&1
          fi
        done
      )
      exit 0
    fi
  fi
fi

对于Windows:module_info.bat取决于7-Zip

@echo off
REM delete module.info from JAR file - may interfere with the local IDE.
for /R %USERPROFILE%\.gradle\caches\modules-2\files-2.1\%1\%2\%3\ %%G in (%2-%3.jar) do (
  if exist %%G (
      7z d  %%G META-INF\versions\module-info.class > NUL:
      7z d  %%G versions\module-info.class > NUL:
      7z d  %%G module-info.class > NUL:
  )
) 

更新:经过一些测试,我得出的结论是,在 Windows 上开发时手动编辑文件可能会更好,因为 Android Studio 和 Java 会锁定JAR,随后将阻止编辑并留下临时文件。


文件 tasks.gradle 提供 DeleteModuleInfoTask:

import javax.inject.Inject

abstract class DeleteModuleInfoTask extends Exec {
    @Inject
    DeleteModuleInfoTask(String dependency) {
        def os = org.gradle.internal.os.OperatingSystem.current()
        def stdout = new ByteArrayOutputStream()
        def stderr = new ByteArrayOutputStream()
        ignoreExitValue true
        standardOutput stdout
        errorOutput stderr
        workingDir "${getProject().getGradle().getGradleUserHomeDir()}${File.separator}caches${File.separator}modules-2${File.separator}files-2.1${File.separator}${dependency.replace(":", File.separator).toString()}"
        String script = "${getProject().getRootDir().getAbsolutePath()}${File.separator}scripts${File.separator}"
        def prefix = ""; def suffix = "sh"
        if (os.isWindows()) {prefix = "cmd /c "; suffix = "bat"}
        String[] item = dependency.split(":")
        commandLine "${prefix}${script}module_info.${suffix} ${item[0]} ${item[1]} ${item[2]}".split(" ")
        // doFirst {println "${commandLine}"}
        doLast {
            if (execResult.getExitValue() == 0) {
                if (stdout.toString() != "") {
                    println "> Task :${project.name}:${name} ${stdout.toString()}"
                }
            } else {
                println "> Task :${project.name}:${name} ${stderr.toString()}"
            }
        }
    }
}

用法示例:

// Bouncycastle
tasks.register("lintFixModuleInfoBcPkix", DeleteModuleInfoTask, "org.bouncycastle:bcpkix-jdk15on:1.64")
lint.dependsOn lintFixModuleInfoBcPkix

tasks.register("lintFixModuleInfoBcProv", DeleteModuleInfoTask, "org.bouncycastle:bcprov-jdk15on:1.64")
lint.dependsOn lintFixModuleInfoBcProv

// GSON
tasks.register("lintFixModuleInfoGson", DeleteModuleInfoTask, "com.google.code.gson:gson:2.8.6")
lint.dependsOn lintFixModuleInfoGson

// Kotlin Standard Library
tasks.register("lintFixModuleInfoKotlinStdLib", DeleteModuleInfoTask, "org.jetbrains.kotlin:kotlin-stdlib:1.4.32")
lint.dependsOn lintFixModuleInfoKotlinStdLib

确保只为单个模块注册这些任务。

根据 resmon“资源监视器”>“关联句柄”,studio64java 可能会锁定 JAR 文件,因此 7-Zip 可能只能在 Android Studio 和 Java 关闭时编辑存档;至少它在 Linux.

上对 CI 很好用

文件 module-info.class 是自 Java 9 以来引入的 Java 模块系统的一部分。根据 this issue 在 Android IssueTracker 上,自 Android Studio 3.4 以来,错误已得到修复。

如前所述,这是在 Java 9 中引入的,Android 不支持。您可以只使用 packagingOptions 删除那些 类。

android {
    packagingOptions {
        exclude "**/module-info.class"
    }
}

这应该不会影响实际执行的代码,并且还应该删除 类 以进行 lint 检查,因为 lint 正在处理字节码。

我收到以下错误消息:

Error processing C:\Users\mypc\.gradle\caches\modules-2\files-2.1\com.google.code.gson\gson.8.680733b7df8542621dc12e21e87557e8c99b8cb\gson-2.8.6.jar:module-info.class: broken class file? (This feature requires ASM6)

在不使用像 Android Studio 这样的开发系统的情况下会发生此错误。我使用 Gradle 6.1.1.

我预防错误如下:

  1. 打开错误消息中命名的文件gson-2.8.6.jar
  2. 删除位于根目录
  3. 中的文件module-info.class

有一个更简单的解决方法。基本上问题可以被识别为“运行 Gradle with Java 8, while handling files which were builded with Java 9”。我的新方法是使用 Java 11 构建(GitHub Actions 也使用 Java 11 构建,而 Gradle 6.7.1 目前最多支持 Java 15)。

  • 安装 Java 11 后 sudo dnf install java-11-openjdk

  • alternatives --display java 将列出要使用的 JDK。

例如:/usr/lib/jvm/java-11-openjdk-11.0.11.0.9-0.el8_3.x86_64:


附带说明,使用 JDK 11 构建也修复了此警告:

Current JDK version 1.8.0_172-b11 has a bug (https://bugs.openjdk.java.net/browse/JDK-8007720) that prevents Room from being incremental. Consider using JDK 11+ or the embedded JDK shipped with Android Studio 3.5+.

“Android Studio 3.5+ 附带的嵌入式 JDK”仍然是 Java 8 ...