我正在尝试使用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,因为他的解决方案很简单,并且大量的赞成证明它有效。
答案 0 :(得分:135)
我遇到了同样的问题,试图在我的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()。希望这会帮助遇到同样问题的其他人。
答案 1 :(得分:30)
由于对这个问题的根本原因没有明确的答案,我想澄清一下这里发生了什么。
为什么首先抛出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的DigestUtils#md5Hex
方法中调用onCreate
。正如我上面所写的,框架不包含该类,因此从您的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
方法(采用单个参数 - 字节数组)存在于Commons Codec
库的1.3版本中,因此此代码可以正常工作。
我建议什么?
我建议的解决方案是重命名classes namespace / package。通过这样做,我明确指定将执行哪些代码,并防止由于版本控制问题可能发生的奇怪行为。
您可以手动执行(如Caumons在其答案中所述),或使用jarjar工具自动执行此操作。
请参阅此问题摘要以及在blogpost中使用jarjar
的提示。
答案 2 :(得分:19)
最后我得到答案,效果很好。如No such method error in Apache codec中针对另一种类型的加密(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/
文件夹中,并将其添加到buildpath中。另外,我将源附加为包含所有文件的文件夹。所以现在我已准备好与Android一起使用的库了!
希望它可以帮助别人!
答案 3 :(得分:2)
对我来说,proguard在混淆期间删除了课程。将此添加到您的Proguard规则中。
-keep class org.apache.commons.** { *; }
这是我使用apache包的方法。
Hex.encodeHex(digest)
答案 4 :(得分:1)
谢谢@ 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 :(得分: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);
}
答案 6 :(得分:0)
我们使用了以下代码,并且有效:
DELETE
答案 7 :(得分:0)
重命名Call<DataModel> call = apiService.getData();
call.enqueue(new Callback<DataModel>() {
@Override
public void onResponse(Call<DataModel> call, Response<DataModel> response) {
// todo deal with returned data. what if it is 401 and i need to make the same request after making the request to auth?
}
public void onFailure(Call<DataModel> call, Throwable t) {
// todo deal with the failed network request
}
});
类的另一种方法是使用proguard。
如果您不使用proguard,则可以启用它并添加这一行,这只会混淆DigestUtils
类,而其他所有内容保持不变。
DigestUtils
并将其添加到您的应用-keep class !org.apache.commons.codec.digest.DigestUtils,com.** { *; }
build.gradle
或 OPTION 2 在您的代码中使用该库的旧版本:
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
如果implementation("commons-codec:commons-codec:1.3"){
force = true
}
依赖项来自第三方库,则需要使用force = true
,否则Gradle将默认解析为更高版本。