美文网首页
NoSuchMethodError: org.apache.co

NoSuchMethodError: org.apache.co

作者: DD_Dog | 来源:发表于2019-07-31 16:06 被阅读0次

    一、问题描述

    在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版本:


    image.png

    1.9版本:


    image.png

    1.3版本:


    image.png

    Android4.4系统中使用的版本(我的测试机)


    image.png

    通过对比得出结论:common-codec.jar在1.4及之后的版本中才有Hex.encodeHexString方法,在AndroidStudio中编译的时候使用的是我引入的1.9版本的jar包,不会出现编译错误,但是一旦安装到Android4.4系统中,会首先寻找系统中是否有相同的API,结果找到了系统中低版本(1.3或者更低)的Hex类,然后就找不到encodeHexString方法了。

    参考链接

    Method not found using DigestUtils in Android

    相关文章

      网友评论

          本文标题:NoSuchMethodError: org.apache.co

          本文链接:https://www.haomeiwen.com/subject/xwzzrctx.html