在 Android 中未找到使用 DigestUtils 的方法

我正在尝试使用 Android 2.3.1中使用 JDK 1.6的 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 的时候它不能工作... 我把这个库放在我的应用程序的一个新的 libs/文件夹中,并且更新了 BuildPath 来使用它。如果我尝试使用 md5而不是 sha1,也会得到同样的异常。如果你能帮忙,我会很感激的!谢谢你。

更新:

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

44162 次浏览

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

我下载了源代码,并将3个目录 org改为 com。我还替换了文件中出现的包名称的所有匹配项,并将文档中的引用更改为 com/apache/commons/codec/。(不要试图手动重新命名他们,否则你将度过整整一天)。然后我编译了这个库并用 Ant 生成了 jar,我称之为 commons-codec-1.6-android.jar。我把 jar 放在 Android 应用程序的 libs/文件夹中,并将它添加到构建路径中。此外,我附加的资源作为文件夹,其中包含所有的文件。因此,现在我已经准备好了与 Android 一起使用的库!

希望能帮到别人!

我在 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 ()。希望这对其他遇到同样问题的人有所帮助。

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

为什么首先抛出 NoSuchMethodError?

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

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库作为应用程序的一部分。编译器没有抱怨——在他看来,一切都很好。

但是在运行时会发生什么呢? 让我们跟踪异常堆栈跟踪:
首先,从 Activity 的 onCreate方法调用 DigestUtils#md5Hex。如前所述,框架不包含这个类,所以 DigestUtils(来自 Commons Codec版本1.4)是从 dex 加载的。
接下来,md5hex方法尝试调用 Hex#encodeHexString方法。Hex类是包含在框架中的 Commons Codec库的一部分。问题是它的版本是 < em > 1.3 (2004年7月发布的古老版本)。Hex类存在于引导类路径中,这意味着运行时将始终青睐它,而不是打包在 dex 中的 Hex类。当你启动你的应用程序时(使用 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方法(采用单个参数字节数组)存在于 Commons Codec库的1.3版中,因此这段代码可以很好地工作。

我有什么建议?

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

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

请参阅这个问题的摘要以及在我的 博客文章中使用 jarjar的技巧。

谢谢@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;
}

对于我来说,在混淆期间删除了该类。将此添加到您的前卫规则。

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

下面是我使用的 apache 包的方法。

Hex.encodeHex(digest)

添加方法

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);
}

我们使用下面的代码,它工作:

  HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));

另一种重命名 DigestUtils类的方法是使用 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的依赖性来自第三方库,否则格拉德将解决默认较高的版本。