一、问题描述
在Android项目中使用到了org.apache.commons.codec.jar包下的DigestUtils.md5Hex()获取文件的md5值,结果编译通过,运行时总是报java.lang.NoSuchMethodError:org.apache.commons.codec.binary.Hex.encodeHexString([B)Ljava/lang/String;错误
我在APP中使用的是common-codec-1.9.jar
调用代码如下:
//直接传入文件输入流就可以得到文件的标准md5值 String cacheDirMd5Hex = DigestUtils.md5Hex(new FileInputStream(file)); String assetMd5Hex = DigestUtils.md5Hex(FileUtils.getBytes(context.getAssets().open(SkinConfig.SKIN_DIR_NAME + File.separator + fileName))); DDLog.i(getClass(), "assetMd5Hex=" + assetMd5Hex + ",cacheDirMd5Hex=" +cacheDirMd5Hex); if (!TextUtils.equals(assetMd5Hex, cacheDirMd5Hex)){ //如果缓存路径中与assets中的文件不相同 DDLog.i(getClass(), "skin资源文件校验错误"); file.delete(); SkinFileUtils.copySkinAssetsToDir(context, fileName, SkinFileUtils.getSkinDir(context)); }
栈信息如下 :
System.err: java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Hex.encodeHexString System.err: at org.apache.commons.codec.digest.DigestUtils.md5Hex(DigestUtils.java:310) System.err: at com.skin.loader.SkinManager.setUpSkinFile(SkinManager.java:106) System.err: at com.skin.loader.SkinManager.access$100(SkinManager.java:38) System.err: at com.skin.loader.SkinManager$1.run(SkinManager.java:73)
二、先上解决办法
既然找不到该方法,那么让它找到这个方法或者使用其它方法就可以解决此问题。
思路一是:保证AndroidStudio中调用的API与系统中的API版本一致,这样就不会导致方法找不到的问题。
思路二是:去掉系统中的相关API,这样就应用就会只调用我们自己APP中的API,但是这样未免代价太大,而且系统中其它地方可能对这些API有依赖,不推荐。
方法一、依据思路一:在AndroidStudio中使用1.3或者更低版本的API
另寻途径,改用其它方法。高版本的API使用起来是更方便快捷的,更换到低版本后就需要自己手动做一些工作了
1.3版本的DigestUtils.md5Hex(byte[] data)只能传入字节数组,所以需要自己把文件读出转换成字节数组才行
String cacheDirMd5Hex = DigestUtils.md5Hex(FileUtils.getBytes(file)); String assetMd5Hex = DigestUtils.md5Hex(FileUtils.getBytes(context.getAssets().open(SkinConfig.SKIN_DIR_NAME + File.separator + fileName))); DDLog.i(getClass(), "assetMd5Hex=" + assetMd5Hex + ",cacheDirMd5Hex=" +cacheDirMd5Hex); if (!TextUtils.equals(assetMd5Hex, cacheDirMd5Hex)){ //如果缓存路径中与assets中的文件不相同 DDLog.i(getClass(), "skin资源文件校验错误"); file.delete(); SkinFileUtils.copySkinAssetsToDir(context, fileName, SkinFileUtils.getSkinDir(context)); }
//没办法,自己写个工具类吧 public class FileUtils { /** * 将文件转换成字节数组 * * @param filePath * @return */ public static byte[] getBytes(String filePath) { FileInputStream fis = null; ByteArrayOutputStream baos = null; try { fis = new FileInputStream(filePath); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read = 0; while ((read = fis.read(buffer)) != -1) { baos.write(buffer, 0, read); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return new byte[0]; } /** * 将文件转换成字节数组 * * @param file * @return */ public static byte[] getBytes(File file) { FileInputStream fis = null; ByteArrayOutputStream baos = null; try { fis = new FileInputStream(file); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read = 0; while ((read = fis.read(buffer)) != -1) { baos.write(buffer, 0, read); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return new byte[0]; } /** * 将文件转换成字节数组 * * @param is * @return */ public static byte[] getBytes(InputStream is) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read = 0; while ((read = is.read(buffer)) != -1) { baos.write(buffer, 0, read); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return new byte[0]; } }
方法二、依据思路一:在更新系统中的common-codec库版本为1.4以上
因为common库会兼容低版本,所以不会引入更多问题,而且是一种更值得做的方法,开历史倒车总是不好的。当然该方法仅限于你是做系统开发的,可以修改系统源码,否则只有依据方法一。
需要懂一些Android Build相关知识。
//TODO 有空再更
三、问题原因
通过网络查找相关文章,发现根本原因如下:
其根本原因在于android内置了一个Codec库,但是版本过老,如果使用了外部引入的新版本的codec.jar中的DigestUtils执行方法的时候,DigestUtils优先调用的是系统自带的老版本的codec库中相应的方法,当老版本的安卓自带codec.jar中不包含新版本DigestUtil需要的方法时,就产生了异常!
由于我使用的是Android4.4版本,于是在代码中搜索了一番,发现在源码编译后的out目录下:
out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes/org/apache/commons/codec/
编译了系统使用的common-codec库,其对应的源码中的jar包为common.jar,于是我打开了common-codec-1.9.jar,common-codec-1.4.jar,common-codec-1.3.jar和common.jar,对比发现在common.jar和common-codec-1.3.jar中的Hex类中没有对应的方法Hex.encodeHexString,如下图:
1.4版本:
1.9版本:
1.3版本:
Android4.4系统中使用的版本(我的测试机)
通过对比得出结论:common-codec.jar在1.4及之后的版本中才有Hex.encodeHexString方法,在AndroidStudio中编译的时候使用的是我引入的1.9版本的jar包,不会出现编译错误,但是一旦安装到Android4.4系统中,会首先寻找系统中是否有相同的API,结果找到了系统中低版本(1.3或者更低)的Hex类,然后就找不到encodeHexString方法了。



