Метод не найден с помощью 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.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, то получаю то же исключение. Любая помощь будет оценена! Спасибо.
UPDATE:
Поскольку это очень активный вопрос, я изменил принятый ответ в пользу @DA25, так как его решение прост и большое количество upvotes доказывают, что он работает.
Ответы
Ответ 1
Я столкнулся с той же проблемой, пытаясь использовать DigestUtils в своем приложении для Android. Это был лучший ответ, который я смог найти, выполнив поиск, но я не хотел восстанавливать файл .jar с измененным пространством имен. Потратив некоторое время на эту проблему, я нашел более простой способ решить проблему для моего дела. Заявление о проблеме для моего кода было
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(). Надеюсь, это поможет другим, столкнувшимся с тем же вопросом.
Ответ 2
Поскольку нет четкого ответа на основную причину этой проблемы, я хотел бы уточнить, что происходит здесь.
Почему в первую очередь выбрано 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
из вашего метода Activity onCreate
. Как я уже писал выше, структура не включает этот класс, поэтому DigestUtils
(из Commons Codec
версия 1.4) загружается из вашего dex.
Затем метод md5hex
пытается вызвать метод Hex#encodeHexString
. Hex
класс является частью библиотеки Commons Codec
, которая включена в структуру. Дело в том, что его версия 1.3 (древний релиз с июля 2004 года). Hex
class существует в bootpathpath, что означает, что время выполнения всегда будет выгодно для него вместо класса 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). В версии 1.3 библиотеки Commons Codec
существует метод encodeHex
(который принимает единственный массив байтов параметров), поэтому этот код будет работать нормально.
Что бы я предложил?
Мое предлагаемое решение - переименовать пространство имен/пакет классов. Поступая таким образом, я явно указываю, какой код будет выполняться, и предотвратите причудливое поведение, которое может возникнуть из-за проблем с версиями.
Вы можете сделать это вручную (как писал Камонс в своем ответе) или автоматически с помощью инструмента jarjar.
См. этот выпуск и советы по использованию jarjar
в блоге .
Ответ 3
Наконец, я получаю ответ, и он работает хорошо. Как описано в Нет такой ошибки метода в кодеке 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!
Надеюсь, что это поможет кому-то еще!
Ответ 4
Спасибо @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;
}
Ответ 5
Для меня proguard удалил класс во время обфускации. Добавьте это в свои правила Proguard.
-keep class org.apache.commons.** { *; }
Здесь я использовал метод apache.
Hex.encodeHex(digest)
Ответ 6
Добавить метод
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);
}
Ответ 7
Мы использовали приведенный ниже код, и он работал:
HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));
Ответ 8
Другой способ переименовать класс 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 разрешит по умолчанию более высокую версию.