Метод не найден с помощью 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

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

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 это не работает ... Я поместил libraty в новую папку libs / в своем приложении и обновил BuildPath, чтобы использовать его. Если я попытаюсь использовать md5 вместо sha1, я получу такое же исключение. Любая помощь будет оценена по достоинству! Спасибо.

ОБНОВЛЕНИЕ:

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


person Caumons    schedule 03.02.2012    source источник
comment
Вы редактировали все исходные файлы? Как мне лучше всего редактировать все исходные файлы? Если возможно, можете ли вы поделиться созданными вами новыми jar-файлами.   -  person Hemant    schedule 18.05.2012
comment
Откройте исходные коды с помощью Eclipse и измените имя пакета на то, что хотите. Затем используйте какую-нибудь команду для замены старых строк, ссылающихся на исходное имя пакета. Где я могу поделиться сгенерированной банкой?   -  person Caumons    schedule 21.05.2012


Ответы (8)


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

person DA25    schedule 14.02.2012
comment
Я не понимаю, почему для вас работает простой вызов проблемных методов внутри Hex.encodeHex() и установка результата в конструктор String! Не могли бы Вы уточнить? Метод encodeHex() из библиотеки DigestUtils? Вы пробовали new String(DigestUtils.md5(data)); напрямую? - person Caumons; 23.02.2012
comment
Я знаю, что это старый вопрос, но для всех, кто интересуется: это вызвано тем, что Android связывает собственную версию commons-codec 1.2. Любая версия новее чем будет недоступна на устройстве. - person KennethJ; 01.09.2014
comment
Это не работает. Я проверил Android API уровня 22. - person lovesh; 01.05.2015
comment
@lovesh Что именно у вас не работает? Только что проверил - в API 22 ничего не изменилось и работает так же, как и раньше. - person Alex Lipov; 07.07.2015
comment
@Caumons проблемным методом является DigestUtils.md5Hex, и он вызывает не его, а DigestUtils.md5 и преобразовывает его в шестнадцатеричный формат другим методом encodeHex. - person Fran Marzoa; 21.03.2018
comment
Обратите внимание, что DigestUtils.sha() устарел. В моем случае я просто использовал: String hash = String(Hex.encodeHex(DigestUtils.sha1(data))); - person K. Stopa; 16.03.2020

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

Почему вообще возникает ошибка 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 уже включает библиотеку Commons Codec (кроме класса DigestUtils). Да, он не является частью Android SDK, и вы не можете использовать его напрямую. Но вы все равно хотите им пользоваться. Так, что вы делаете? Вы добавляете Commons Codec библиотеку как часть своего приложения. Компилятор не жалуется - с его точки зрения все было нормально.

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

person Alex Lipov    schedule 23.04.2015
comment
Большое спасибо, чувак! У меня была такая же проблема с Apache Commons Lang jar (3.2 и выше), но только на всех телефонах от поставщика Xiomi. Судя по всему, на этих телефонах установлена ​​старая версия Apache Commons Lang как часть среды выполнения системы. Поэтому я получал такие исключения, как: STACK_TRACE = java.lang.NoSuchMethodError: org.apache.commons.lang3.mutable.MutableBoolean.setTrue STACK_TRACE = java.lang.NoSuchMethodError: org.apachering.commons.langtils.St после Совет, я использовал инструмент jarjar, чтобы переименовать пространство имен пакета во что-то уникальное для моего приложения. - person La Machine; 13.07.2016
comment
Спасибо, вы вдохновили меня на написание этого простого демонстрационного проекта github.com/allpaykz/digest-utils - person c0rp; 03.08.2017

Наконец-то я получил ответ, и он работает хорошо. Как описано в Нет такой ошибки метода в кодеке Apache для другого типа encrypt (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!

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

person Caumons    schedule 04.02.2012
comment
Это правильный ответ, но кажется маловероятным, что разработчики изменят исходный код дайджеста. Об этом следует сообщить в apache. В моем разработчике я выбрал другой вариант, предоставленный @ DA25 - person Snicolas; 22.06.2012
comment
Для тех, кто использует Maven: делать это вручную не нужно. Для этого есть плагин, см. Объяснение здесь: stackoverflow.com/a/16916552/621690 - person Risadinha; 09.09.2013
comment
@Caumons, не могли бы вы поделиться библиотекой, у меня такие же проблемы. - person Nitin Misra; 31.01.2014
comment
@NitinMisra, вы читали обновление или комментарий от Risadinha выше? - person Caumons; 31.01.2014
comment
@Caumons Я использую new String(DigestUtils.md5(data));, как вы предложили ранее. Это безопасно? - person Nitin Misra; 31.01.2014
comment
@NitinMisra, большое количество голосов за ответ, кажется, доказывает это;) - person Caumons; 01.02.2014

Спасибо @ DA25

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

Я добавил зависимость

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

ссылка: 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;
}
person Bikesh M    schedule 14.03.2016

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

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

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

Hex.encodeHex(digest)
person pratham kesarkar    schedule 30.10.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);
}
person reznic    schedule 08.12.2017

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

  HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
  String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
person Alok Gupta    schedule 02.05.2019

Другой способ переименовать класс DigestUtils будет с помощью proguard. Если вы не используете proguard, вы можете включить его и добавить эту строку, которая будет скрывать только DigestUtils класс, а все остальное оставить нетронутым.

-keep class !org.apache.commons.codec.digest.DigestUtils,com.** { *; }

и добавьте это в свое приложение 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'
        }
    }

Или ВАРИАНТ 2 Используйте старую версию библиотеки в своем коде:

implementation("commons-codec:commons-codec:1.3"){
        force = true
    }

Необходимо использовать force = true, если common-codec зависимости исходят из библиотеки третьей части, иначе Gradle по умолчанию разрешит более высокую версию.

person Roman Nazarevych    schedule 02.10.2019