在 Android 中找不到使用 DigestUtils 的方法
Posted
技术标签:
【中文标题】在 Android 中找不到使用 DigestUtils 的方法【英文标题】:Method not found using DigestUtils in Android 【发布时间】:2012-02-25 22:45:00 【问题描述】:我正在尝试使用 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文件。 用 Eclipse 打开源代码并将包名更改为您想要的。然后,使用一些命令来替换引用原始包名称的旧字符串。我可以在哪里分享生成的 jar? 【参考方案1】:最后我得到了答案,而且效果很好。正如No such method error in Apache codec 中描述的另一种加密类型(Base64),我尝试重现相同的问题,但得到完全相同的错误。所以我是在附加问题的情况下。正如他们所说,这似乎是与包名称org.apache.commons.codec
的内部名称冲突,正如@Don 所说,我将其更改为com.apache.commons.codec
并且工作正常!我是怎么做到的?
我下载了源码,把org
这3个目录改成了com
。我还替换了它们出现的文件中所有出现的包名称,并将文档中的引用更改为com/apache/commons/codec/
。 (不要尝试手动重新制作它们,否则您将度过空洞日)。然后我编译了这个库并用 Ant 生成了这个 jar,我称之为commons-codec-1.6-android.jar
。我将 jar 放在我的 Android 应用程序的 libs/
文件夹中,并将其添加到构建路径中。另外,我将源文件附加为包含所有文件的文件夹。所以现在我已经准备好与 Android 一起使用的库了!
希望对其他人有所帮助!
【讨论】:
这是正确的答案,但开发人员更改摘要源的可能性很小。这应该报告给 apache。在我的开发中,我选择了@DA25 给出的另一个选项 对于那些使用 Maven 的人:无需手动执行此操作。它有一个插件,请看这里的解释:***.com/a/16916552/621690 @Caumons 你能分享一下图书馆吗,我也遇到了同样的麻烦。 @NitinMisra 你读过 Risadinha 的更新或上面的评论吗? @Caumons 我使用new String(DigestUtils.md5(data));
,正如您之前建议的那样。安全吗?【参考方案2】:
我尝试在我的 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()。希望这能帮助遇到同样问题的其他人。
【讨论】:
我不明白为什么只在Hex.encodeHex()
中调用有问题的方法并将结果设置为 String 构造函数对你有用!你可以再详细一点吗?方法 encodeHex()
来自 DigestUtils 库吗?你试过直接new String(DigestUtils.md5(data));
吗?
我知道这是一个老问题,但对于任何感兴趣的人来说:这是由 Android 捆绑了自己的 commons-codec 1.2 版本引起的。任何高于 的版本都将无法在设备上使用。
这不起作用。我检查了 Android API 级别 22。
@lovesh 到底什么对你不起作用?刚刚检查过 - API 22 没有任何变化,它的工作方式与以前相同。
@Caumons 有问题的方法是 DigestUtils.md5Hex,他没有调用那个,而是 DigestUtils.md5 并使用不同的方法 encodeHex 将其转换为十六进制。【参考方案3】:
由于这个问题的根本原因没有明确的答案,我想澄清一下这里发生了什么。
为什么首先抛出 NoSuchMethodError?
根据异常堆栈跟踪,导致错误的行是DigestUtils#md5hex
方法中的226。让我们看看我们有什么 there(我假设您使用的是 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
。正如我在上面所写,框架不包含该类,因此从您的 dex 加载 DigestUtils
(来自 Commons Codec
版本 1.4)。
接下来,md5hex
方法尝试调用Hex#encodeHexString
方法。 Hex
类是包含在框架中的 Commons Codec
库的一部分。问题是它的版本是 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
方法(采用单个参数 - 字节数组)存在于 1.3 版的 Commons Codec
库中,因此此代码可以正常工作。
我有什么建议?
我建议的解决方案是重命名类命名空间/包。通过这样做,我明确指定要执行哪些代码,并防止由于版本控制问题而可能发生的奇怪行为。
您可以手动完成(正如 Caumons 在他的回答中所写),也可以使用 jarjar 工具自动完成。
在我的blogpost 中查看此问题摘要和使用jarjar
的提示。
【讨论】:
非常感谢你!我在使用 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 工具将包命名空间重命名为我的应用程序独有的名称。 谢谢,你启发了我写这个简单的展示项目github.com/allpaykz/digest-utils【参考方案4】:感谢@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;
【讨论】:
【参考方案5】:对我来说,proguard 在混淆过程中删除了该类。将此添加到您的 Proguard 规则中。
-keep class org.apache.commons.** *;
这是我使用 apache 包的方法。
Hex.encodeHex(digest)
【讨论】:
【参考方案6】:添加方法
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);
【讨论】:
【参考方案7】:我们使用了下面的代码并且成功了:
HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
【讨论】:
【参考方案8】:另一种重命名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
如果common-codec
依赖来自第三方库,则需要使用force = true
,否则Gradle 将默认解析为更高版本。
【讨论】:
以上是关于在 Android 中找不到使用 DigestUtils 的方法的主要内容,如果未能解决你的问题,请参考以下文章
使用 Videojs 在 android 中找不到此视频的兼容源