Android에서 DigestUtils를 사용하여 메서드를 찾을 수 없음


79

JDK 1.6을 사용하여 Android 2.3.1에서 DigestUtils 라이브러리를 사용하려고 하지만 앱을 실행할 때 다음 오류가 발생합니다.

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

Android 외부의 Java 클래스에서 동일한 코드를 실행했으며 작동합니다! 그래서 Android로 작업 할 때 왜 작동하지 않는지 모르겠습니다. libraty를 내 앱의 새 libs / 폴더에 넣고 BuildPath를 업데이트하여 사용했습니다. sha1 대신 md5를 사용하려고하면 동일한 예외가 발생합니다. 어떤 도움을 주시면 감사하겠습니다! 감사합니다.

최신 정보:

이것은 매우 적극적인 질문이므로 @ DA25에 찬성하여 수락 된 답변을 변경했습니다. 그의 솔루션은 간단하고 많은 수의 찬성 투표가 작동한다는 것을 증명하기 때문입니다.


모든 소스 파일을 편집 했습니까? 모든 소스 파일을 편집하는 가장 좋은 방법은 무엇입니까? 가능하면 생성 한 새 jar 파일을 공유 할 수 있습니다.
Hemant

Eclipse로 소스를 열고 패키지 이름을 원하는대로 변경하십시오. 그런 다음 일부 명령을 사용하여 원래 패키지 이름을 참조하는 이전 문자열을 바꿉니다. 생성 된 항아리를 어디에서 공유 할 수 있습니까?
Caumons

답변:


148

내 Android 앱에서 DigestUtils를 사용하려고 할 때 동일한 문제가 발생했습니다. 이것은 검색을 통해 찾을 수있는 가장 좋은 답변이지만 네임 스페이스가 변경된 .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 ()가 있기 때문에 작동합니다. 이것이 같은 문제에 직면 한 다른 사람들에게 도움이되기를 바랍니다.


7
문제가있는 메서드를 내부에서 호출 Hex.encodeHex()하고 결과를 String 생성자로 설정하는 이유를 이해할 수 없습니다 . 좀 더 구체적으로 말씀해 주시겠습니까? encodeHex()DigestUtils 라이브러리 의 메소드 입니까? new String(DigestUtils.md5(data));직접 시도해 보셨습니까 ?
Caumons 2012

4
나는 이것이 오래된 질문이라는 것을 알고 있지만 관심있는 사람이라면 Android가 자체 버전의 commons-codec 1.2를 번들로 제공하기 때문에 발생합니다. 이후 버전은 장치에서 사용할 수 없습니다.
KennethJ

작동하지 않습니다. Android API 레벨 22에서 확인했습니다.
lovesh

1
@lovesh 정확히 당신을 위해 작동하지 않는 것은 무엇입니까? 방금 확인했습니다. API 22에서는 변경된 사항이 없으며 이전과 동일하게 작동합니다.
Alex Lipov 2015

@Caumons 문제가되는 메서드는 DigestUtils.md5Hex이고 그는 그 메서드를 호출하지 않고 대신 DigestUtils.md5를 다른 메서드 인 encodeHex로 16 진수로 변환합니다.
Fran Marzoa

37

이 문제의 근본 원인에 대한 명확한 답이 없기 때문에 여기에서 무슨 일이 일어나고 있는지 명확히하고 싶습니다.

NoSuchMethodError가 처음에 발생하는 이유는 무엇입니까?

예외 스택 추적에 따르면 오류를 유발하는 라인은 DigestUtils#md5hex메서드 에서 226입니다 . 우리가 거기에 무엇을 가지고 있는지 보자 (나는 당신이 버전 1.4를 사용했다고 가정하고, 이것은 Hex#encodeHexString226 행 에서 메소드가 호출되는 유일한 릴리스이기 때문이다 ) :

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#md5HexActivity의 onCreate메서드 에서 호출 합니다 . 위에서 썼 듯이 프레임 워크에는 해당 클래스가 포함되어 있지 않으므로 DigestUtils( Commons Codec버전 1.4 부터 ) dex에서로드됩니다.
다음으로, md5hex메소드는 메소드 호출을 시도합니다 Hex#encodeHexString. Hex클래스는 Commons Codec프레임 워크에 포함 된 라이브러리의 일부입니다 . 문제는 그 버전이 1.3 (2004 년 7 월의 고대 릴리스)이라는 것입니다. Hex클래스는 부팅 클래스 경로에 존재하므로 런타임이 Hexdex 내부에 패키징 된 클래스 대신 항상 선호합니다 . 앱을 시작할 때 (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 메서드는 Commons Codec라이브러리 버전 1.4에서 도입 되었으므로 프레임 워크의 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라이브러리, 따라서이 코드는 잘 작동합니다.

내가 무엇을 제안할까요?

내가 제안한 해결책은 클래스 네임 스페이스 / 패키지의 이름을 바꾸는 것입니다. 이렇게함으로써 실행할 코드를 명시 적으로 지정하고 버전 관리 문제로 인해 발생할 수있는 기괴한 동작을 방지합니다.

수동으로 (Caumons가 답변에서 썼 듯이) 또는 jarjar 도구를 사용 하여 자동으로 수 있습니다.

이 문제 요약 및 jarjar블로그 게시물 에서 사용하기위한 팁을 참조하십시오 .


3
정말 감사합니다! 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.apache.commons.lang3.StringEscapeUtils.escapeXml10 조언, jarjar 도구를 사용하여 패키지 네임 스페이스의 이름을 내 앱에 고유 한 이름으로 바꿨습니다.
La Machine

1
감사합니다, 당신은이 간단한 쇼케이스 프로젝트 작성하는 나에게 영감을 github.com/allpaykz/digest-utils을
c0rp

19

마침내 나는 답을 얻었고 잘 작동합니다. 다른 유형의 암호화 (Base64)에 대한 Apache 코덱에서 이러한 방법 오류 없음에 설명 된대로 동일한 문제를 재현하려고 시도했지만 정확히 동일한 오류가 발생합니다. 그래서 저는 첨부 된 질문의 경우였습니다. 그들이 말했듯이 패키지 이름 org.apache.commons.codec과 내부 이름 충돌로 보이며 @Don에서 언급했듯이 변경하고 com.apache.commons.codec잘 작동했습니다! 내가 어떻게 했어?

소스 코드를 다운로드하고 3 개의 디렉토리 orgcom. 또한 나타나는 파일에서 패키지 이름의 모든 항목을 바꾸고 문서의 참조도 com/apache/commons/codec/. (수동으로 다시 만들려고하지 마십시오. 그렇지 않으면 홀 데이를 보내 게됩니다). 그런 다음 라이브러리를 컴파일하고 Ant로 jar를 생성했습니다 commons-codec-1.6-android.jar. libs/내 Android 앱 의 폴더에 항아리를 넣고 빌드 경로에 추가했습니다. 또한 모든 파일이 포함 된 폴더로 소스를 첨부했습니다. 이제 Android에서 사용할 수있는 라이브러리가 준비되었습니다!

다른 사람에게 도움이되기를 바랍니다!


2
이것은 정답이지만 개발자가 다이제스트 소스를 변경할 가능성은 매우 낮습니다. 이것은 Apache에보고되어야합니다. 내 dev에, 나는 @ DA25에 의해 주어진 다른 옵션 선택
Snicolas

1
Maven을 사용하는 경우 :이 작업을 직접 수행 할 필요가 없습니다. 그것에 대한 플러그인이 있습니다. 여기 설명 참조 : stackoverflow.com/a/16916552/621690
Risadinha 2013 년

@Caumons 라이브러리를 공유해 주시겠습니까? 저도 같은 문제에 처해 있습니다.
Nitin Misra

@NitinMisra Risadinha의 업데이트 또는 위의 의견을 읽었습니까?
Caumons 2014 년

@Caumons new String(DigestUtils.md5(data));이전에 제안한대로 사용 합니다. 안전 해요?
Nitin Misra 2014 년

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.** { *; }

다음은 아파치 패키지를 사용한 방법입니다.

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이 기본적으로 상위 버전으로 확인됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.