在Android中使用DigestUtils找不到方法


78

我正在尝试使用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,因为他的解决方案很简单,并且大量的投票证明它可行。


您是否编辑了所有源文件?对我来说,编辑所有源文件的最佳方法是什么?如果可能,是否可以共享已创建的新jar文件。
Hemant 2012年

使用Eclipse打开源,然后将包名称更改为所需的名称。然后,使用某些命令替换引用原始程序包名称的旧字符串。我在哪里可以共享生成的jar?
2012年

Answers:


147

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


7
我不明白为什么只在内部调用有问题的方法Hex.encodeHex()并将结果设置为String构造函数对您有用!你可以再详细一点吗?该方法encodeHex()来自DigestUtils库吗?您new String(DigestUtils.md5(data));直接尝试过吗?
Caumons

3
我知道这是一个古老的问题,但对于任何感兴趣的人:这是由Android捆绑其自己的commons-codec 1.2版本引起的。该设备上将不提供任何版本早于的版本。
KennethJ 2014年

这是行不通的。我在Android API级别检查22
lovesh

1
@lovesh到底什么对您不起作用?刚刚检查过-API 22中没有任何更改,其工作原理与以前相同。
Alex Lipov

@Caumons,有问题的方法是DigestUtils.md5Hex,他没有调用该方法,而是DigestUtils.md5,并使用其他方法encodeHex将其转换为十六进制。
Fran Marzoa

36

由于没有找到此问题的根本原因的明确答案,因此,我想澄清一下这里发生的情况。

为什么首先抛出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方法(带有单个参数-字节数组)存在于Commons Codec库的1.3版中,因此此代码可以正常工作。

我有什么建议?

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

您可以手动执行此操作(如Caumons在其答案中所写),也可以使用jarjar工具自动执行

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


3
非常感谢你!我对Apache Commons Lang jar(3.2及更高版本)有相同的问题-但仅在供应商Xiomi的所有电话上。显然,这些电话在系统运行时中具有旧版本的Apache Commons Lang。所以我过去经常得到类似这样的异常:STACK_TRACE = java.lang.NoSuchMethodError:org.apache.commons.lang3.mutable.MutableBoolean.setTrue STACK_TRACE = java.lang.NoSuchMethodError:org.apache.commons.lang3.StringEscapeUtils.escapeXml10建议,我使用jarjar工具将包名称空间重命名为我的应用程序特有的名称。
La Machine

1
谢谢,您启发了我写这个简单的展示项目github.com/allpaykz/digest-utils
c0rp

19

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

我下载了源代码,并将3个目录更改orgcom。我还替换了出现的文件名中所有出现的包名称,并将docs中的引用更改为com/apache/commons/codec/。(请勿尝试手动退还,否则将花费整日的时间)。然后,我编译该库并使用Ant生成了jar,我称之为commons-codec-1.6-android.jar。我将罐子放在libs/Android应用程序的文件夹中,并将其添加到了buildpath中。另外,我将源附加为包含所有文件的文件夹。因此,现在我可以使用Android了!

希望对别人有帮助!


2
这是正确的答案,但开发人员更改摘要来源似乎极不可能。这应该报告给apache。在我的开发人员中,我选择了@ DA25提供的其他选项
Snicolas 2012年

1
对于使用Maven的用户:无需手动执行此操作。有一个插件,请看这里的解释:stackoverflow.com/a/16916552/621690
Risadinha 2013年

@Caumons能否请您共享图书馆,我也遇到了麻烦。
Nitin Misra 2014年

@NitinMisra您是否已阅读Risadinha的更新或上述评论?
Caumons 2014年

@Caumons我new String(DigestUtils.md5(data));按照您之前的建议使用。安全吗?
Nitin Misra,2014年

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

1

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

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

这是我使用apache包的方法。

Hex.encodeHex(digest)

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

0

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

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

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将默认解析为更高版本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.