Android で Apache Commons Codec を使うと、存在するはずのメソッドを使っているのに、コンパイルエラーや実行時エラーになることがある。
確認環境
Android Studio 2.1.3
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
useLibrary 'org.apache.http.legacy'
}
dependencies {
compile 'commons-codec:commons-codec:1.10'
}
コンパイルエラーの例
byte[] bin = "abc".getBytes();
String hex = Hex.encodeHexString(bin);
エラー: シンボルを見つけられません
シンボル: メソッド encodeHexString(byte[])
場所: クラス Hex
実行時エラーの例
byte[] bin = "abc".getBytes();
String hex = DigestUtils.sha256Hex(bin);
java.lang.NoSuchMethodError: No static method encodeHexString([B)Ljava/lang/String; in class Lorg/apache/commons/codec/binary/Hex; or its super classes (declaration of 'org.apache.commons.codec.binary.Hex' appears in /system/framework/org.apache.http.legacy.boot.jar)
at org.apache.commons.codec.digest.DigestUtils.sha256Hex(DigestUtils.java:500)
原因
Android が持つ(Android M では org.apache.http.legacy となった) Apache HTTP Client に古い Commons Codec が内包されており、 build.gradle
で指定した新しいバージョンのほうが使われず、古いほうが使われるため。
-
Hex.encodeHexString
がコンパイルエラーになるのは、Android側のHexクラス自体にそのメソッドがないため -
DigestUtils.sha256Hex
がコンパイルエラーにならないのは、Android側にはDigestUtilsクラス自体がなく、アプリ側の新しいバージョンに含まれるDigestUtilsが使われるため(?) - しかし、DigestUtils.sha256Hexの中で呼ばれているHexクラスはAndroid側のものが使われるから(?)、実行時に NoSuchMethodError となる
Android が持つ Commons Codec ってどのくらい古いのか
ソースコードを見ると、1.3 て書いてある。
https://android.googlesource.com/platform/external/apache-http/+/android-7.0.0_r6/src/org/apache/commons/codec/overview.html#20
対処
Hexクラスの場合は、v1.3に存在するメソッドのほうを使う
byte[] bin = "abc".getBytes();
//String hex = Hex.encodeHexString(bin); こっちはコンパイルエラーになる
char[] hexChars = Hex.encodeHex(bin);
String hex = new String(hexChars);
DigestUtilsなどv1.3に存在しないクラスの場合は、中の実装次第なので、表面上からは判別できない?
とりあえず DigestUtils.sha256Hex は、以下のようにすればok。
byte[] bin = "abc".getBytes();
//String hex = DigestUtils.sha256Hex(bin); こっちはランタイムエラーになる
byte[] sha256 = DigestUtils.sha256(bin);
char[] hexChars = Hex.encodeHex(sha256);
String hex = new String(hexChars);
おまけ
Apache Commons Codec の公式サイトを見ても古ーーい 1.3 の Javadoc が見当たらなかったので、Androidのソースコードから生成してみた。
git clone https://android.googlesource.com/platform/external/apache-http
cd apache-http/
javadoc -locale en_US -public -d doc -sourcepath src -subpackages org.apache.commons.codec