Méthode non trouvée utilisant des DigestUtils dans Android

j'essaie d'utiliser la bibliothèque DigestUtils sous Android 2.3.1 en utilisant JDK 1.6, cependant j'obtiens l'erreur suivante lors de l'exécution de l'application:

Could not find method org.apache.commons.codec.binary.Hex.encodeHexString, referenced from method org.apache.commons.codec.digest.DigestUtils.shaHex

ici vous avez le stacktrace:

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.access00(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)

la ligne de code qui cause l'exception est:

String hash = DigestUtils.shaHex("textToHash");

j'ai exécuté le même code dans une classe Java en dehors D'Android et il œuvres! Donc, je ne sais pas pourquoi en travaillant avec Android il ne fonctionne pas... J'ai mis la bibliothèque à l'intérieur d'un nouveau dossier libs/ dans mon application et j'ai mis à jour le BuildPath pour l'utiliser. Si j'essaie d'utiliser md5 au lieu de sha1, j'obtiens la même exception. Toute aide serait appréciée! Remercier.

mise à jour:

comme c'est une question très active, j'ai changé la réponse acceptée en faveur de @DA25, car sa solution est simple et la haute le nombre de notes positives prouve que cela fonctionne.

63
demandé sur Caumons 2012-02-03 13:43:11

6 réponses

j'ai rencontré le même problème en essayant d'utiliser des DigestUtils dans mon application Android. C'était la meilleure réponse que j'ai pu trouver par la recherche, mais j'étais réticent à reconstruire le .jar fichier avec le namespace changé. Après avoir passé quelque temps sur cette question, j'ai trouvé un moyen plus facile de résoudre le problème pour mon cas. L'énoncé du problème pour mon code était

String s = DigestUtils.md5Hex(data);

remplacer cet énoncé par le suivant et il fonctionnera:

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

De même, pour shaHex exampl, vous pouvez le changer en

String hash = new String(Hex.encodeHex(DigestUtils.sha("textToHash")));

cela fonctionne parce que même si Android n'a pas encodeHexString(), il a encodeHex(). Espérons que cela aidera d'autres personnes qui rencontrent le même problème.

128
répondu DA25 2016-08-30 09:10:25

comme il n'y a pas de réponse claire à la cause profonde de ce problème, j'aimerais clarifier ce qui se passe ici.

pourquoi le NoSuchMethodError est jeté en premier lieu?

selon la trace de la pile d'exception, la ligne qui cause le défaut est 226 dans la méthode DigestUtils#md5hex . Voyons ce que nous avons (je suppose que vous avez utilisé la version 1.4, puisque c'est la seule version où la méthode Hex#encodeHexString est invoquée à la ligne 226):

public static String md5Hex(String data) {
    return Hex.encodeHexString(md5(data));
}

l'exception dit java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString . Nous allons comprendre pourquoi.

tout d'abord, Android framework inclut déjà la bibliothèque Commons Codec (sauf la classe DigestUtils ). Oui, il n'est pas exposé dans le cadre de la Android SDK et vous ne pouvez pas l'utiliser directement. Mais vous voulez continuer à l'utiliser. Donc, ce que vous faites? Vous ajoutez la bibliothèque Commons Codec dans votre application. Le le compilateur ne se plaint pas de son point de vue, tout allait bien.

mais que se passe-t-il à l'exécution? Suivons votre trace de pile d'exceptions:

D'abord, vous appelez DigestUtils#md5Hex de la méthode de votre activité onCreate . Comme je l'ai écrit ci-dessus, le framework n'inclut pas cette classe, donc DigestUtils (de Commons Codec version 1.4) est chargé à partir de votre dex.

Ensuite, la méthode md5hex essaie d'invoquer Hex#encodeHexString la méthode. La classe Hex fait partie de la bibliothèque Commons Codec qui est incluse dans framework. Le truc c'est que sa version est 1.3 (ancienne version de juillet 2004). La classe Hex existe dans boot classpath, ce qui signifie que le runtime la favorisera toujours à la place de la classe Hex qui est emballée dans votre dex. Vous pouvez voir des avertissements à ce sujet dans vos journaux d'application lorsque vous démarrez votre application (avec Dalvik runtime):

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
La méthode

Hex#encodeHexString a été introduite dans la version 1.4 de la bibliothèque Commons Codec et n'existe donc pas dans la classe Hex de framework. Le runtime ne trouve pas cette méthode et lance donc l'exception NoSuchMethodError .

Pourquoi l'on a accepté la réponse de la solution fonctionne?

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

D'abord, DigestUtils#md5 méthode est appelé. Comme Je L'Ai déjà indiqué, DigestUtils classe qui sera utilisé est celui qui emballé dans votre dex. Cette méthode n'utilise pas d'autres classes Commons Codec , donc pas de problème.

ensuite, Hex#encodeHex sera appelé. La classe Hex qui sera utilisée est celle du framework (version 1.3). La méthode encodeHex (qui prend un seul tableau de bytes de paramètre) existe dans la version 1.3 de la bibliothèque Commons Codec , et donc ce code fonctionnera très bien.

que suggérerais-je?

ma solution suggérée est de renommer les classes namespace/package. En faisant cela, je spécifie explicitement quel code va être exécuté, et prévient les comportements bizarres qui peuvent se produire à cause de problèmes de versioning.

vous pouvez le faire manuellement (comme Caumons a écrit dans sa réponse), ou automatiquement avec jarjar outil.

voir ce numéro résumé et conseils pour utiliser jarjar dans mon blogpost .

28
répondu Alex Lipov 2015-04-24 07:09:38

finalement je reçois la réponse et cela fonctionne bien. Comme décrit dans aucune erreur de méthode de ce type dans Apache codec pour un autre type de Crypt (Base64) j'ai essayé de reproduire le même problème et je reçois exactement la même erreur. J'étais donc dans le cas de la question ci-jointe. Comme ils disent, il semble qu'il s'agisse d'une collision de Nom interne avec le nom de paquet org.apache.commons.codec et comme indiqué par @Don Je l'ai changé en com.apache.commons.codec et a bien fonctionné! Comment j'ai fait?

I j'ai téléchargé le code source et changé les 3 répertoires org en com . J'ai aussi remplacé toutes les occurrences du nom du paquet dans les fichiers où qu'ils apparaissent et aussi changé les références dans les docs de com/apache/commons/codec/ . (N'essayez pas de les refaire manuellement ou vous passerez la journée du trou). Puis j'ai compilé la bibliothèque et généré le pot avec Ant, que j'ai appelé commons-codec-1.6-android.jar . J'ai mis le bocal dans le dossier libs/ de mon application Android et je l'ai ajouté au chemin de construction. Aussi, Je joint les sources comme le dossier qui contient tous les fichiers. Donc maintenant j'ai la bibliothèque prête à utiliser avec Android!

espérons qu'il aide quelqu'un d'autre!

19
répondu Caumons 2017-05-23 11:47:11

Merci @DA25

Cela fonctionne bien pour moi

j'ai ajouter de la dépendance

compile 'commons-codec:commons-codec:1.9'

réf: http://mvnrepository.com/artifact/commons-codec/commons-codec/1.9

ma fonction

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
répondu Bikesh M Annur 2016-03-14 07:52:31

For me proguard supprimé la classe pendant la confusion .Ajoutez ceci à vos règles Proguard.

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

Voici la méthode que j'utilisais pour le paquet apache.

Hex.encodeHex(digest)
1
répondu pratham kesarkar 2017-10-30 06:30:23

ajouter la méthode

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
répondu reznic 2017-12-08 16:23:10