Метод не найден с использованием DigestUtils в Android

Я пытаюсь использовать библиотеку DigestUtils в Android 2.3.1 с помощью JDK 1.6, однако я получаю следующую ошибку при выполнении приложения:

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

здесь у вас есть 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)

строка кода, которая вызывает исключение:

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

Я выполнил тот же код в классе Java вне Android, и он работает! Так, я не знаю, почему при работе с Android это не работает... Я поместите libraty в новую папку libs / в моем приложении и обновите путь к зданию, чтобы использовать его. Если я попытаюсь использовать md5 вместо sha1, я получу то же исключение. Любая помощь будет оценена! Спасибо.

обновление:

поскольку это очень активный вопрос, я изменил принятый ответ в пользу @DA25, поскольку его решение является простым и большое количество upvotes доказывают, что оно работает.

6 ответов


я столкнулся с той же проблемой, пытаясь использовать DigestUtils в моем приложении для Android. Это был лучший ответ, который я мог найти, ища, но я не хотел перестраивать .файл jar с измененным пространством имен. Потратив некоторое время на этот вопрос, я нашел более простой способ решить проблему для моего случая. Оператор problem для моего кода был

String s = DigestUtils.md5Hex(data);

замените это утверждение следующим, и оно будет работать:

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

аналогично, для shahex exampl вы можете измените его на

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

это работает, потому что, хотя Android не имеет encodeHexString (), у него есть encodeHex(). Надеюсь, это поможет другим, кто столкнется с той же проблемой.


поскольку нет четкого ответа на основную причину этой проблемы, я хотел бы уточнить, что здесь происходит.

почему NoSuchMethodError бросается в первую очередь?

согласно трассировке стека исключений, строка, которая вызывает ошибку, составляет 226 в DigestUtils#md5hex метод. Давайте посмотрим, что у нас есть здесь (я предполагаю, что вы использовали версию 1.4, так как это единственный выпуск, где Hex#encodeHexString метод вызывается в соответствии 226):

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

исключение говорит java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString. Давайте разберемся, почему.

прежде всего, Android framework уже включает в себя Commons Codec библиотека (кроме DigestUtils класс). Да, он не подвергается воздействию как часть Android SDK и вы не можете использовать его напрямую. Но ты все равно хочешь им воспользоваться. Так что ты делаешь? Добавить Commons Codec библиотека как часть вашего приложения. Компилятор не жалуется - с его точки зрения все было хорошо.

но что произойдет во время выполнения? Давайте следовать вашей трассировке стека исключений:
Во-первых, вы звоните DigestUtils#md5Hex от вашей деятельности onCreate метод. Как я уже писал выше, фреймворк не включает этот класс, поэтому DigestUtils (от Commons Codec версия 1.4) загружается из вашего dex.
Далее,md5hex метод пытается вызвать Hex#encodeHexString метод. Hex класс является частью Commons Codec библиотека, включенная в framework. Дело в том, что его версия 1.3 (древний выпуск с июля 2004). Hex класс существует в пути к классу загрузки, что означает, что среда выполнения всегда будет поддерживать его вместо Hex класс, который упакован внутри вашего dex. Вы можете увидеть предупреждения об этом в журналах приложений при запуске приложения (с помощью 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

Hex#encodeHexString метод был введен в версии 1.4 Commons Codec библиотека и, следовательно, она не существует в Hex класса. Среда выполнения не может найти этот метод и тем самым броски NoSuchMethodError исключения.

почему решение принятого ответа работает?

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

во-первых,DigestUtils#md5 метод называется. Как я уже сказал, DigestUtils класс, который будет использоваться, - это тот, который упакован в вашем dex. Этот метод не использует никаких других Commons Codec классы, так что никаких проблем с этим.

далее Hex#encodeHex будет называться. The Hex класс, который будет использоваться, является одним из фреймворков (версия 1.3). The encodeHex способ (что принимает один параметр-байтовый массив) существует в версии 1.3 Commons Codec библиотека, и поэтому этот код будет работать нормально.

что бы я предложил?

мое предлагаемое решение-переименовать пространство имен/пакет классов. При этом я явно указываю, какой код будет выполняться, и предотвращаю странное поведение, которое может возникнуть из-за проблем с версиями.

вы можете сделать это вручную (как написал Caumons в своем ответе), или автоматически с Джаджа.

см. этот выпуск резюме и советы по использованию jarjar в своем blogpost.


наконец-то я получил ответ, и он работает хорошо. Как описано в нет такой ошибки метода в кодеке Apache для другого типа шифрования (Base64) я попытался воспроизвести ту же проблему, и я получаю точно такую же ошибку. Таким образом, я был в случае прилагаемого вопроса. Как говорится, это похоже на внутреннее столкновение имени с именем пакета org.apache.commons.codec и, как указано @Don, я изменил его на com.apache.commons.codec и работал нормально! Как я это сделал?

Я загрузил исходный код и изменены 3 каталога org to com. Я также заменил все вхождения имени пакета в файлах, где они появляются, а также изменил ссылки на документы com/apache/commons/codec/. (Не пытайтесь переделать их вручную, или вы проведете день отверстия). Затем я скомпилировал библиотеку и сгенерировал банку с Ant, которую я назвал commons-codec-1.6-android.jar. Я поставил банку в libs/ папка моего приложения для Android и добавил его в buildpath. Кроме того, я прикрепил источники в качестве папки, которая содержит все файлы. Итак, теперь у меня есть библиотека, готовая к использованию с Android!

надеюсь, что это поможет кому-то еще!


спасибо @DA25

это работает нормально для меня

у меня есть add dependency

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

ref: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;
}

для меня proguard удалил класс во время обфускации .Добавьте это в правила Proguard.

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

вот метод, который я использовал для пакета apache.

Hex.encodeHex(digest)

добавить способ

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);
}