83

我正在尝试使用 JDK 1.6 在 Android 2.3.1 中使用库DigestUtils,但是在执行应用程序时出现以下错误:

Could not find method org.apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.apache.commons.codec.digest.DigestUtils.shaHex

这里有堆栈跟踪:

02-03 10:25:45.153: I/dalvikvm(1230): Could not find method org.apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.apache.commons.codec.digest.DigestUtils.shaHex
02-03 10:25:45.153: W/dalvikvm(1230): VFY: unable to resolve static method 329: Lorg/apache/commons/codec/binary/Hex;.encodeHexString ([B)Ljava/lang/String;
02-03 10:25:45.153: D/dalvikvm(1230): VFY: replacing opcode 0x71 at 0x0004
02-03 10:25:45.153: D/dalvikvm(1230): VFY: dead code 0x0007-0008 in Lorg/apache/commons/codec/digest/DigestUtils;.shaHex ([B)Ljava/lang/String;
02-03 10:25:45.163: D/AndroidRuntime(1230): Shutting down VM
02-03 10:25:45.163: W/dalvikvm(1230): threadid=1: thread exiting with uncaught exception (group=0x40015560)
02-03 10:25:45.173: E/AndroidRuntime(1230): FATAL EXCEPTION: main
02-03 10:25:45.173: E/AndroidRuntime(1230): java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString
02-03 10:25:45.173: E/AndroidRuntime(1230):     at org.apache.commons.codec.digest.DigestUtils.md5Hex(DigestUtils.java:226)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at com.caumons.trainingdininghall.ConnectionProfileActivity.onCreate(ConnectionProfileActivity.java:20)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1586)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1638)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:928)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.os.Looper.loop(Looper.java:123)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at android.app.ActivityThread.main(ActivityThread.java:3647)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at java.lang.reflect.Method.invokeNative(Native Method)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at java.lang.reflect.Method.invoke(Method.java:507)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
02-03 10:25:45.173: E/AndroidRuntime(1230):     at dalvik.system.NativeStart.main(Native Method)

导致异常的代码行是:

String hash = DigestUtils.shaHex("textToHash");

我已经在 Android 之外的 Java 类中执行了相同的代码,并且可以正常工作!所以,我不知道为什么在使用 Android 时它不起作用......我将 libraty 放在我的应用程序的一个新的 libs/ 文件夹中,并更新了 BuildPath 以使用它。如果我尝试使用 md5 而不是 sha1 我会得到同样的异常。任何帮助,将不胜感激!谢谢你。

更新:

由于这是一个非常活跃的问题,我已经更改了接受的答案以支持@DA25,因为他的解决方案很简单,并且大量的赞成票证明它有效。

4

8 回答 8

152

我在我的 Android 应用程序中尝试使用 DigestUtils 时遇到了同样的问题。这是我可以通过搜索找到的最佳答案,但我不愿意在更改命名空间的情况下重建 .jar 文件。在这个问题上花了一些时间后,我找到了一种更简单的方法来解决我的问题。我的代码的问题陈述是

String s = DigestUtils.md5Hex(data);

将此语句替换为以下内容,它将起作用:

String s = new String(Hex.encodeHex(DigestUtils.md5(data)));

同样,对于 shaHex 示例,您可以将其更改为

String hash = new String(Hex.encodeHex(DigestUtils.sha("textToHash")));

这是有效的,因为即使 Android 没有 encodeHexString(),它也有 encodeHex()。希望这会帮助遇到同样问题的其他人。

于 2012-02-14T20:59:00.537 回答
37

由于这个问题的根本原因没有明确的答案,我想澄清一下这里发生了什么。

为什么首先抛出 NoSuchMethodError?

根据异常堆栈跟踪,导致错误的行是DigestUtils#md5hex方法中的 226。让我们看看那里有什么(我假设您使用的是 1.4 版,因为这是Hex#encodeHexString在第 226 行调用方法的唯一版本):

public static String md5Hex(String data) {
    return Hex.encodeHexString(md5(data));
}

异常说java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString。让我们明白为什么。

首先,Android 框架已经包含了Commons Codec库(DigestUtils类除外)。是的,它没有作为 的一部分公开Android SDK,您不能直接使用它。但是你仍然想使用它。那你干什么?您将Commons Codec库添加为应用程序的一部分。编译器没有抱怨——从他的角度来看,一切都很好。

但是在运行时会发生什么?让我们跟踪您的异常堆栈跟踪:
首先,您DigestUtils#md5Hex从 Activity 的onCreate方法中调用。正如我上面写的,框架不包括那个类,所以DigestUtils(从Commons Codec1.4 版开始)是从你的 dex 加载的。
接下来,md5hex方法尝试调用Hex#encodeHexString方法。Hex类是Commons Codec包含在框架中的库的一部分。问题是它的版本是1.3(2004 年 7 月的旧版本)。Hex类存在于引导类路径中,这意味着运行时将始终偏爱它,而不是Hex打包在 dex 中的类。当您启动应用程序(使用 Dalvik 运行时)时,您可以在应用程序日志中看到有关它的警告:

D/dalvikvm? DexOpt: 'Lorg/apache/commons/codec/binary/Hex;' has an earlier definition; blocking out
I/dalvikvm? DexOpt: not resolving ambiguous class 'Lorg/apache/commons/codec/binary/Hex;'
D/dalvikvm? DexOpt: not verifying/optimizing 'Lorg/apache/commons/codec/binary/Hex;': multiple definitions
I/dalvikvm? Could not find method org.apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.apache.commons.codec.digest.DigestUtils.md5Hex

Hex#encodeHexString方法是在Commons Codec库的 1.4 版中引入的,因此它不存在于框架的Hex类中。运行时找不到此方法,因此引发NoSuchMethodError异常。

为什么接受的答案的解决方案有效?

String s = new String(Hex.encodeHex(DigestUtils.md5(data)));

首先,DigestUtils#md5方法被调用。正如我已经说过的,DigestUtils将使用的类是打包在你的 dex 中的类。此方法不使用任何其他Commons Codec类,因此没有问题。

接下来,Hex#encodeHex将被调用。将使用的Hex类是框架的类(1.3 版)。该encodeHex方法(采用单个参数 - 字节数组)存在于 1.3 版Commons Codec库中,因此此代码可以正常工作。

我会建议什么?

我建议的解决方案是重命名类命名空间/包。通过这样做,我明确指定了要执行的代码,并防止由于版本问题而可能发生的奇怪行为。

您可以手动完成(正如 Caumons 在他的回答中所写),也可以使用jarjar工具自动完成。

jarjar请参阅我的博文中的问题摘要和使用技巧。

于 2015-04-23T19:55:38.143 回答
19

最后我得到了答案,而且效果很好。如另一种加密类型(Base64)的 Apache 编解码器中没有此类方法错误中所述,我尝试重现相同的问题,但得到完全相同的错误。所以我是在附加问题的情况下。正如他们所说,这似乎是与包名称的内部名称冲突,org.apache.commons.codec正如@Don 所说,我将其更改为com.apache.commons.codec并且工作正常!我是怎么做到的?

我下载了源代码并将 3 个目录更改orgcom. 我还替换了它们出现的文件中所有出现的包名称,并将文档中的引用更改为com/apache/commons/codec/. (不要尝试手动重修它们,否则您将度过空洞日)。然后我编译了库并用 Ant 生成了 jar,我称之为commons-codec-1.6-android.jar. 我将 jar 放在libs/我的 Android 应用程序的文件夹中,并将其添加到构建路径中。另外,我将源文件附加为包含所有文件的文件夹。所以现在我已经准备好与 Android 一起使用的库了!

希望它可以帮助别人!

于 2012-02-04T11:11:00.483 回答
3

谢谢@DA25

这对我来说很好用

我添加了依赖

compile 'commons-codec:commons-codec:1.9'

参考: http: //mvnrepository.com/artifact/commons-codec/commons-codec/1.9

我的功能

public String encode(String key, String data) {
    try {

        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);

        return new String(Hex.encodeHex(sha256_HMAC.doFinal(data.getBytes("UTF-8"))));

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return null;
}
于 2016-03-14T07:52:31.557 回答
1

对我来说,proguard 在混淆过程中删除了该类。将此添加到您的 Proguard 规则中。

-keep class org.apache.commons.** { *; }

这是我使用 apache 包的方法。

Hex.encodeHex(digest)
于 2017-10-30T06:30:23.850 回答
0

重命名类的另一种方法DigestUtils是使用 proguard。如果你没有使用 proguard,你可以启用它并添加这一行,它只会混淆DigestUtils类,而其他所有内容都保持不变。

-keep class !org.apache.commons.codec.digest.DigestUtils,com.** { *; }

并将其添加到您的应用中build.gradle

buildTypes {
        debug {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

选项 2 在您的代码中使用旧版本的库:

implementation("commons-codec:commons-codec:1.3"){
        force = true
    }

force = true如果common-codec依赖来自第三方库,则需要使用,否则 Gradle 将默认解析为更高版本。

于 2019-10-02T12:14:11.713 回答
0

添加方法

public static String byteArrayToHexString(byte[] bytes) {
    final char[] toDigits = "0123456789abcdef".toCharArray();
    int l = bytes.length;
    char[] out = new char[l << 1];

    int i = 0; for (int j = 0; i < l; ++i) {
        out[(j++)] = toDigits[((0xF0 & bytes[i]) >>> 4)];
        out[(j++)] = toDigits[(0xF & bytes[i])];
    }
    return new String(out);
}
于 2017-12-08T16:23:10.663 回答
0

我们使用了下面的代码并且它有效:

  HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
  String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
于 2019-05-02T14:40:36.603 回答