Ich versuche, die Bibliothek DigestUtils in Android 2.3.1 mit JDK 1.6 zu verwenden, erhalte jedoch beim Ausführen der App den folgenden Fehler:
Could not find method org.apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.apache.commons.codec.digest.DigestUtils.shaHex
Hier haben Sie die Stapelverfolgung:
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)
Die Codezeile, die die Ausnahme verursacht, lautet:
String hash = DigestUtils.shaHex("textToHash");
Ich habe den gleichen Code in einer Java-Klasse außerhalb von Android ausgeführt und es funktioniert! Ich weiß also nicht, warum es bei der Arbeit mit Android nicht funktioniert ... Ich habe die Bibliothek in einen neuen libs / Ordner in meiner App gestellt und den BuildPath aktualisiert, um sie zu verwenden. Wenn ich versuche, md5 anstelle von sha1 zu verwenden, erhalte ich die gleiche Ausnahme. Jede Hilfe wäre dankbar! Vielen Dank.
AKTUALISIEREN:
Da dies eine sehr aktive Frage ist, habe ich die akzeptierte Antwort zugunsten von @ DA25 geändert, da seine Lösung unkompliziert ist und die hohe Anzahl von Upvotes beweist, dass sie funktioniert.
Antworten:
Beim Versuch, DigestUtils in meiner Android-App zu verwenden, ist dasselbe Problem aufgetreten. Dies war die beste Antwort, die ich durch Suchen finden konnte, aber ich zögerte, die JAR-Datei mit geändertem Namespace neu zu erstellen. Nachdem ich einige Zeit mit diesem Problem verbracht hatte, fand ich einen einfacheren Weg, um das Problem für meinen Fall zu lösen. Die Problemstellung für meinen Code war
String s = DigestUtils.md5Hex(data);
Ersetzen Sie diese Anweisung durch die folgende und es wird funktionieren:
String s = new String(Hex.encodeHex(DigestUtils.md5(data)));
In ähnlicher Weise können Sie beispielsweise shaHex in ändern
String hash = new String(Hex.encodeHex(DigestUtils.sha("textToHash")));
Dies funktioniert, da Android zwar nicht über encodeHexString () verfügt, jedoch über encodeHex (). Hoffe, dies würde anderen helfen, die auf das gleiche Problem stoßen.
quelle
Hex.encodeHex()
für Sie funktioniert, nur die problematischen Methoden aufzurufen und das Ergebnis auf einen String-Konstruktor zu setzen! Könnten Sie genauer sein? Ist die MethodeencodeHex()
aus der DigestUtils-Bibliothek? Hast du esnew String(DigestUtils.md5(data));
direkt versucht ?Da es keine klare Antwort auf die Grundursache dieses Problems gibt, möchte ich klarstellen, was hier passiert.
Warum wird der NoSuchMethodError überhaupt ausgelöst?
Gemäß der Ausnahme-Stapelverfolgung ist die Zeile, die den Fehler verursacht, 226 in der
DigestUtils#md5hex
Methode. Mal sehen , was wir haben dort (ich nehme an, Sie haben gebrauchte Version 1.4, da dies die einzige ist frei , wennHex#encodeHexString
Methode in Zeile 226 aufgerufen wird):public static String md5Hex(String data) { return Hex.encodeHexString(md5(data)); }
Die Ausnahme sagt
java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString
. Lassen Sie uns verstehen, warum.Erstens enthält das Android-Framework bereits die
Commons Codec
Bibliothek (mit Ausnahme derDigestUtils
Klasse). Ja, es wird nicht als Teil von angezeigtAndroid SDK
und Sie können es nicht direkt verwenden. Aber du willst es trotzdem benutzen. Also, was machst du? Sie fügen eineCommons Codec
Bibliothek als Teil Ihrer Anwendung hinzu. Der Compiler beschwert sich nicht - aus seiner Sicht war alles in Ordnung.Aber was passiert zur Laufzeit? Folgen wir Ihrem Exception-Stack-Trace:
Zuerst rufen Sie
DigestUtils#md5Hex
von deronCreate
Methode Ihrer Aktivität aus auf . Wie ich oben geschrieben habe, enthält das Framework diese Klasse nicht, daher wirdDigestUtils
(abCommons Codec
Version 1.4) von Ihrem Dex geladen.Als nächstes
md5hex
versucht dieHex#encodeHexString
Methode , die Methode aufzurufen .Hex
Klasse ist Teil derCommons Codec
Bibliothek, die im Framework enthalten ist. Die Sache ist, dass seine Version 1.3 ist (alte Veröffentlichung von Juli 2004).Hex
Die Klasse ist im Boot-Klassenpfad vorhanden. Dies bedeutet, dass die Laufzeit sie immer anstelle derHex
Klasse bevorzugt , die in Ihrem Dex gepackt ist. Sie können Warnungen dazu in Ihren Anwendungsprotokollen sehen, wenn Sie Ihre App starten (mit Dalvik-Laufzeit):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
Die Hex # encodeHexString- Methode wurde in Version 1.4 der
Commons Codec
Bibliothek eingeführt und ist daher in der Framework-Hex
Klasse nicht vorhanden . Die Laufzeit kann diese Methode nicht finden und löst daher eineNoSuchMethodError
Ausnahme aus.Warum funktioniert die Lösung der akzeptierten Antwort?
String s = new String(Hex.encodeHex(DigestUtils.md5(data)));
Zunächst wird die
DigestUtils#md5
Methode aufgerufen. Wie ich bereits sagte, ist dieDigestUtils
Klasse, die verwendet wird, diejenige, die in Ihrem Dex verpackt ist. Diese Methode verwendet keine anderenCommons Codec
Klassen, also kein Problem damit.Als nächstes
Hex#encodeHex
wird aufgerufen. DieHex
Klasse, die verwendet wird, ist die des Frameworks (Version 1.3). DieencodeHex
Methode (die ein einzelnes Parameter-Byte-Array verwendet) ist in Version 1.3 derCommons Codec
Bibliothek vorhanden, und daher funktioniert dieser Code einwandfrei.Was würde ich vorschlagen?
Meine vorgeschlagene Lösung besteht darin, den Klassennamensraum / das Klassennamen umzubenennen. Auf diese Weise gebe ich explizit an, welcher Code ausgeführt werden soll, und verhindere bizarres Verhalten, das aufgrund von Versionsproblemen auftreten kann.
Sie können dies manuell (wie Caumons in seiner Antwort schrieb) oder automatisch mit dem Jarjar- Tool tun .
Siehe diese Problemzusammenfassung und Tipps zur Verwendung
jarjar
in meinem Blogpost .quelle
Endlich bekomme ich die Antwort und es funktioniert gut. Wie unter Kein solcher Methodenfehler im Apache-Codec für einen anderen Verschlüsselungstyp (Base64) beschrieben, habe ich versucht, dasselbe Problem zu reproduzieren, und es wird genau derselbe Fehler angezeigt. Also war ich bei der beigefügten Frage. Wie sie sagen, scheint es eine interne Namenskollision mit dem Paketnamen zu sein
org.apache.commons.codec
und wie von @Don angegeben, habe ich es geändertcom.apache.commons.codec
und es hat gut funktioniert! Wie habe ich das gemacht?Ich habe den Quellcode heruntergeladen und die 3 Verzeichnisse
org
in geändertcom
. Ich habe auch alle Vorkommen des Paketnamens in den Dateien ersetzt, in denen sie angezeigt werden, und auch die Verweise in den Dokumenten in geändertcom/apache/commons/codec/
. (Versuchen Sie nicht, sie manuell neu zu planen, sonst verbringen Sie den ganzen Tag). Dann habe ich die Bibliothek zusammengestellt und das Glas mit Ant generiert, das ich angerufen habecommons-codec-1.6-android.jar
. Ich habe das Glas in denlibs/
Ordner meiner Android-App gelegt und es dem Buildpath hinzugefügt. Außerdem habe ich die Quellen als Ordner angehängt, der alle Dateien enthält. Jetzt habe ich die Bibliothek für Android bereit!Hoffe, dass es jemand anderem hilft!
quelle
new String(DigestUtils.md5(data));
wie du vorher vorgeschlagen hast. Ist es sicher?Danke @ DA25
Das funktioniert gut für mich
Ich habe Abhängigkeit hinzugefügt
compile 'commons-codec:commons-codec:1.9'
Ref: http://mvnrepository.com/artifact/commons-codec/commons-codec/1.9
meine Funktion
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; }
quelle
Für mich hat Proguard die Klasse während der Verschleierung entfernt. Fügen Sie dies Ihren Proguard-Regeln hinzu.
-keep class org.apache.commons.** { *; }
Hier ist die Methode, die ich für das Apache-Paket verwendet habe.
Hex.encodeHex(digest)
quelle
Methode hinzufügen
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); }
quelle
Wir haben den folgenden Code verwendet und es hat funktioniert:
HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString); String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
quelle
Eine andere Möglichkeit, die
DigestUtils
Klasse umzubenennen , ist die Verwendung von Proguard. Wenn Sie kein Proguard verwenden, können Sie es aktivieren und diese eine Zeile hinzufügen, die nur dieDigestUtils
Klasse verschleiert , und alles andere intakt lassen.-keep class !org.apache.commons.codec.digest.DigestUtils,com.** { *; }
und füge dies deiner App hinzu
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' } }
Oder OPTION 2 Verwenden Sie die alte Version der Bibliothek in Ihrem Code:
implementation("commons-codec:commons-codec:1.3"){ force = true }
Muss verwendet werden,
force = true
wenn diecommon-codec
Abhängigkeit von der Bibliothek des dritten Teils stammt, andernfalls wird Gradle standardmäßig in eine höhere Version aufgelöst.quelle