Метод не найден с помощью DigestUtils в Android - PullRequest
68 голосов
/ 03 февраля 2012

Я пытаюсь использовать библиотеку 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

Здесь у вас есть трассировка стека:

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

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

ОБНОВЛЕНИЕ:

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

Ответы [ 7 ]

135 голосов
/ 15 февраля 2012

Я столкнулся с той же проблемой, пытаясь использовать DigestUtils в моем приложении для Android. Это был лучший ответ, который я смог найти, выполнив поиск, но я не хотел перестраивать файл .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 (). Надеюсь, что это поможет другим, кто столкнется с той же проблемой.

31 голосов
/ 23 апреля 2015

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

Почему 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, которая включена в каркас.Дело в том, что его версия 1.3 (древний выпуск от июля 2004 года).Класс Hex существует в bootpath classpath, что означает, что среда выполнения всегда будет отдавать ему предпочтение вместо класса Hex, упакованного внутри вашего dex.Предупреждения об этом можно увидеть в журналах приложений при запуске приложения (со средой выполнения 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 метод был введен в версии 1.4 библиотеки Commons Codecи поэтому он не существует в классе Hex фреймворка.Среда выполнения не может найти этот метод и, таким образом, выдает NoSuchMethodError исключение.

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

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

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

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

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

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

Вы можете сделать это вручную (как писал Камон в своем ответе) или автоматически с помощью инструмента jarjar .

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

19 голосов
/ 04 февраля 2012

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

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

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

2 голосов
/ 30 октября 2017

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

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

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

Hex.encodeHex(digest)
1 голос
/ 14 марта 2016

Спасибо @ DA25

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

У меня есть добавленная зависимость

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;
}
0 голосов
/ 02 мая 2019

Мы использовали приведенный ниже код, и он работал:

  HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
  String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
0 голосов
/ 08 декабря 2017

Добавить метод

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