Methode mit DigestUtils in Android nicht gefunden

78

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.

Caumons
quelle
Haben Sie alle Quelldateien bearbeitet? Wie kann ich alle Quelldateien am besten bearbeiten? Wenn möglich, können Sie die neuen JAR-Dateien, die Sie erstellt haben, freigeben.
Hemant
Öffnen Sie die Quellen mit Eclipse und ändern Sie den Paketnamen nach Ihren Wünschen. Verwenden Sie dann einen Befehl, um die alten Zeichenfolgen zu ersetzen, die auf den ursprünglichen Paketnamen verweisen. Wo könnte ich das generierte Glas teilen?
Caumons

Antworten:

147

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.

DA25
quelle
7
Ich verstehe nicht, warum es 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 Methode encodeHex()aus der DigestUtils-Bibliothek? Hast du es new String(DigestUtils.md5(data));direkt versucht ?
Caumons
3
Ich weiß, dass dies eine alte Frage ist, aber für alle Interessierten: Dies wird dadurch verursacht, dass Android seine eigene Version von Commons-Codec 1.2 bündelt. Eine neuere Version als diese ist auf dem Gerät nicht verfügbar.
KennethJ
Das funktioniert nicht. Ich habe auf Android API Level 22
nachgesehen
1
@lovesh Was genau funktioniert bei dir nicht? Nur überprüft - nichts hat sich in API 22 geändert und es funktioniert genauso wie zuvor.
Alex Lipov
@Caumons die problematische Methode ist DigestUtils.md5Hex und er ruft diese nicht auf, sondern DigestUtils.md5 und konvertiert sie mit einer anderen Methode encodeHex in hex.
Fran Marzoa
36

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#md5hexMethode. Mal sehen , was wir haben dort (ich nehme an, Sie haben gebrauchte Version 1.4, da dies die einzige ist frei , wenn Hex#encodeHexStringMethode 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 CodecBibliothek (mit Ausnahme der DigestUtilsKlasse). Ja, es wird nicht als Teil von angezeigt Android SDKund Sie können es nicht direkt verwenden. Aber du willst es trotzdem benutzen. Also, was machst du? Sie fügen eine Commons CodecBibliothek 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#md5Hexvon der onCreateMethode Ihrer Aktivität aus auf . Wie ich oben geschrieben habe, enthält das Framework diese Klasse nicht, daher wird DigestUtils(ab Commons CodecVersion 1.4) von Ihrem Dex geladen.
Als nächstes md5hexversucht die Hex#encodeHexStringMethode , die Methode aufzurufen . HexKlasse ist Teil der Commons CodecBibliothek, die im Framework enthalten ist. Die Sache ist, dass seine Version 1.3 ist (alte Veröffentlichung von Juli 2004). HexDie Klasse ist im Boot-Klassenpfad vorhanden. Dies bedeutet, dass die Laufzeit sie immer anstelle der HexKlasse 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 CodecBibliothek eingeführt und ist daher in der Framework- HexKlasse nicht vorhanden . Die Laufzeit kann diese Methode nicht finden und löst daher eine NoSuchMethodErrorAusnahme aus.

Warum funktioniert die Lösung der akzeptierten Antwort?

String s = new String(Hex.encodeHex(DigestUtils.md5(data)));

Zunächst wird die DigestUtils#md5Methode aufgerufen. Wie ich bereits sagte, ist die DigestUtilsKlasse, die verwendet wird, diejenige, die in Ihrem Dex verpackt ist. Diese Methode verwendet keine anderen Commons CodecKlassen, also kein Problem damit.

Als nächstes Hex#encodeHexwird aufgerufen. Die HexKlasse, die verwendet wird, ist die des Frameworks (Version 1.3). Die encodeHexMethode (die ein einzelnes Parameter-Byte-Array verwendet) ist in Version 1.3 der Commons CodecBibliothek 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 jarjarin meinem Blogpost .

Alex Lipov
quelle
3
Vielen Dank Mann! Ich hatte das gleiche Problem mit Apache Commons Lang jar (3.2 und höher) - aber nur auf allen Handys des Anbieters Xiomi. Anscheinend haben diese Telefone eine alte Version von Apache Commons Lang als Teil der Systemlaufzeit. Daher habe ich Ausnahmen erhalten wie: STACK_TRACE = java.lang.NoSuchMethodError: org.apache.commons.lang3.mutable.MutableBoolean.setTrue STACK_TRACE = java.lang.NoSuchMethodError: org.apache.commons.lang3.StringEscapeUtils.escapeX Ich habe das jarjar-Tool verwendet, um den Paket-Namespace in einen für meine App einzigartigen Namen umzubenennen.
La Machine
1
Danke, hat mich inspiriert Sie dieses einfache Vorzeigeprojekt zu schreiben github.com/allpaykz/digest-utils
c0rp
19

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.codecund wie von @Don angegeben, habe ich es geändert com.apache.commons.codecund es hat gut funktioniert! Wie habe ich das gemacht?

Ich habe den Quellcode heruntergeladen und die 3 Verzeichnisse orgin geändert com. 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ändert com/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 habe commons-codec-1.6-android.jar. Ich habe das Glas in den libs/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!

Caumons
quelle
2
Dies ist die richtige Antwort, aber es scheint höchst unwahrscheinlich, dass Entwickler die Digest-Quelle ändern. Dies sollte Apache gemeldet werden. In meinem Entwickler habe ich die andere Option von @ DA25 gewählt
Snicolas
1
Für diejenigen, die Maven verwenden: Sie müssen dies nicht von Hand tun. Es gibt ein Plugin dafür, siehe Erklärung hier: stackoverflow.com/a/16916552/621690
Risadinha
@Caumons kannst du bitte die Bibliothek teilen, ich bin in den gleichen Schwierigkeiten.
Nitin Misra
@NitinMisra hast du das Update oder den obigen Kommentar von Risadinha gelesen?
Caumons
@Caumons benutze ich new String(DigestUtils.md5(data));wie du vorher vorgeschlagen hast. Ist es sicher?
Nitin Misra
3

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;
}
Bikesh M.
quelle
1

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)
Pratham Kesarkar
quelle
0

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);
}
reznic
quelle
0

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)));
Alok Gupta
quelle
0

Eine andere Möglichkeit, die DigestUtilsKlasse umzubenennen , ist die Verwendung von Proguard. Wenn Sie kein Proguard verwenden, können Sie es aktivieren und diese eine Zeile hinzufügen, die nur die DigestUtilsKlasse 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 = truewenn die common-codecAbhängigkeit von der Bibliothek des dritten Teils stammt, andernfalls wird Gradle standardmäßig in eine höhere Version aufgelöst.

Roman Nazarevych
quelle