Метод не знайдений за допомогою DigestUtils в Android


78

Я намагаюся використовувати бібліотеку 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, оскільки його рішення є простим, і велика кількість голосів "за" підтверджує, що воно працює.


Ви редагували всі вихідні файли? Який для мене найкращий спосіб редагувати всі вихідні файли? Якщо можливо, чи можете ви поділитися новими файлами jar, які ви створили.
Гемант

Відкрийте джерела за допомогою Eclipse і змініть назву пакета на те, що ви хочете. Потім, використовуйте якусь команду, щоб замінити старі рядки, що посилаються на ім’я оригінального пакета. Де я можу поділитися створеною банкою?
Каумонс

Відповіді:


147

Я зіткнувся з тією ж проблемою, намагаючись використовувати 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 (). Сподіваюся, це допоможе іншим, хто стикається з тим самим питанням.


7
Я не розумію, чому просто виклик проблемних методів всередині Hex.encodeHex()і встановлення результату в конструкторі String працює для вас! Чи можете ви бути більш конкретними? Метод encodeHex()із бібліотеки DigestUtils? Ви пробували new String(DigestUtils.md5(data));безпосередньо?
Каумонс

3
Я знаю, що це старе запитання, але для тих, хто цікавиться: це спричинено тим, що Android об’єднує власну версію commons-codec 1.2. Будь-яка версія, новіша за яку, не буде доступна на пристрої.
Кеннет Дж.

Це не працює. Я перевірив рівень Android API рівня 22.
Loveh

1
@lovesh Що саме у вас не працює? Щойно перевірили - в API 22 нічого не змінилося, і він працює так само, як і раніше.
Alex Lipov

@Caumons проблемним методом є DigestUtils.md5Hex, і він не викликає його, а замість цього DigestUtils.md5 і перетворює його у шістнадцятковий за допомогою іншого методу encodeHex.
Fran Marzoa

36

Оскільки чіткої відповіді на першопричину цієї проблеми немає, я хотів би пояснити, що тут відбувається.

Чому NoSuchMethodError кидається в першу чергу?

Відповідно до трасування стека винятків, рядок, що спричиняє несправність, у DigestUtils#md5hexметоді 226 . Давайте подивимось, що у нас там (я припускаю, що ви використовували версію 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 . Як я писав вище, фреймворк не включає цей клас, тому DigestUtilsCommons Codecверсії 1.4) завантажується з вашого dex.
Далі md5hexметод намагається викликати Hex#encodeHexStringметод. Hexclass є частиною Commons Codecбібліотеки, яка включена в фреймворк. Річ у тім, що його версія 1.3 (стародавній реліз від липня 2004 р.). Hexклас існує в boot 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в моєму блозі .


3
Щиро дякую чоловіче! У мене була та ж проблема з банкою Apache Commons Lang (3.2 і вище), але лише на всіх телефонах від постачальника Xiomi. Очевидно, ці телефони мають стару версію Apache Commons Lang як частину часу роботи системи. Тому я звик отримувати такі винятки, як: STACK_TRACE = java.lang.NoSuchMethodError: org.apache.commons.lang3.mutable.MutableBoolean.setTrue STACK_TRACE = java.lang.NoSuchMethodError: org.apache.commons.lang3.StringEscapeUtilsStringEscapeUtilsStringEscapeUtilsls10StringEscapeUtilsStringEscapeUstils10 порада, я використав інструмент jarjar, щоб перейменувати простір імен пакетів на щось унікальне для мого додатка.
La Machine

1
Дякую, ви надихнули мене написати цей простий демонстраційний проект github.com/allpaykz/digest-utils
c0rp

19

Нарешті я отримую відповідь, і вона працює добре. Як описано в Помилка такого методу в кодеку 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
Це правильна відповідь, але здається вкрай неймовірним, що розробники змінять джерело дайджесту. Про це слід повідомити apache. У своєму розробнику я вибрав інший варіант, наданий @ DA25
Snicolas

1
Для тих, хто використовує Maven: не потрібно робити це вручну. Існує плагін для нього, см.об'ясненіе тут: stackoverflow.com/a/16916552/621690
Risadinha

@Caumons, будь ласка, поділіться бібліотекою, у мене такі ж проблеми.
Нітін Місра

@NitinMisra Ви читали оновлення чи коментар вище від Risadinha?
Каумонс

@Caumons я використовую, new String(DigestUtils.md5(data));як ви пропонували раніше. Це безпечно?
Нітін Місра

3

Дякую @ 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;
}

1

Для мене proguard видалив клас під час затухання. Додайте це до своїх правил Proguard.

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

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

Hex.encodeHex(digest)

0

Додати метод

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

Ми використовували код нижче, і він працював:

  HmacUtils hmacUtils = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, keyString);
  String digest = new String( Hex.encodeHex(hmacUtils.hmac(msg)));

0

Інший спосіб перейменувати 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 вирішить за замовчуванням вищу версію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.