美文网首页Android开发经验笔记我的测试收藏android技术专栏
Android动态加载so文件(解决so文件冲突)

Android动态加载so文件(解决so文件冲突)

作者: 陈利健 | 来源:发表于2015-11-25 15:31 被阅读14861次

    在开发过程中,经常会用到第三方库,比如地图、视频、文档编辑、图表之类。依赖这些库,需要添加其SDK,有时需要用到jni层的So文件,比如百度地图等。
    那么问题来了,如果两个不同的库之间的so文件发生冲突这么办?
    比如:单独添加地图的库,运行没有问题。单独添加一个视频库,运行没有问题。但两者同时添加,其中一个库在init的时候报错。这当然和第三方库的开发水平有很大关系,但我们怎么解决这个问题呢?这就用到动态加载的方法。


    这是原先的静态加载方法,将所有依赖库的so文件全部一股脑的放进armeabi文件夹即可。


    静态加载

    动态加载的方法,我将冲突的so文件放在assets文件夹中


    动态加载

    这里需要注意的是:动态加载so的文件只能放在两个地方:1. lib文件夹中,即对应Android Studio中的jniLibs文件夹。2. 本地data/data/package数据目录下。 所以,当应用第一次启动的时候,必须将我们放在assets文件夹中的so文件拷贝乳本地数据目录下。

       //每次进入app,遍历assets目录下所有的文件,是否在data/data目录下都已经存在,不存在则拷贝
       private void initAssetsFile() {
    
            boolean needCopy = false;
    
            // 创建data/data目录
            File file = getApplicationContext().getFilesDir();
            String path = file.toString() + "/armeabi/";
    
            // 遍历assets目录下所有的文件,是否在data/data目录下都已经存在
            try {
                String[] fileNames = getApplicationContext().getAssets().list("armeabi");
                for (int i = 0; fileNames != null && i < fileNames.length; i++) {
                    if (!TFileUtils.isFileExit(path + fileNames[i])) {
                        needCopy = true;
                        break;
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            if (needCopy) {
                CommonUtils.copyFilesFassets(getApplicationContext(), "armeabi", path);
            }
        }
    
        //将旧目录中的文件全部复制到新目录
        public static void copyFilesFassets(Context context, String oldPath, String newPath) {
            try {
    
                // 获取assets目录下的所有文件及目录名
                String fileNames[] = context.getAssets().list(oldPath);
    
                // 如果是目录名,则将重复调用方法递归地将所有文件
                if (fileNames.length > 0) {
                    File file = new File(newPath);
                    file.mkdirs();
                    for (String fileName : fileNames) {
                        copyFilesFassets(context, oldPath + "/" + fileName, newPath + "/" + fileName);
                    }
                }
                // 如果是文件,则循环从输入流读取字节写入
                else {
                    InputStream is = context.getAssets().open(oldPath);
                    FileOutputStream fos = new FileOutputStream(new File(newPath));
                    byte[] buffer = new byte[1024];
                    int byteCount = 0;
                    while ((byteCount = is.read(buffer)) != -1) {
                        fos.write(buffer, 0, byteCount);
                    }
                    fos.flush();
                    is.close();
                    fos.close();
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
       //在需要初始化SDK的地方,指定so文件的路径
       private void initAPI() {   
            File file = getApplicationContext().getFilesDir();
            String path = file.toString() + "/armeabi/"; 
            EzvizAPI.init(getApplication(), key, path);
      }
    

    当然,上面这个方法是将so文件放在程序的assets文件夹。另一种方法是:也可以从网络上下载,放入本地数据目录下。这样的好处是不仅减小的了apk的大小,而且可以随时使用最新的依赖库,这也是动态加载的最多得用途之一。

    相关文章

      网友评论

      • Millie_tl:请问CommonUtils.copyFilesFassets(getApplicationContext(), "armeabi", path)这个函数是怎么写的
      • 蜂蜜_柚子茶:拷贝到本地 调用方法不变吗 怎么都没写
      • evanwo:这里我有个疑问 希望楼主给解答一下,两个冲突的so库都要放到assets里面还是一个放assets一个放jniLibs
        陈利健:一个assets,一个jni
      • Xanthuim:我现在有两个版本的库,但是由于内部名调用限制死了,loadLibrary("main");我想在调用的时候修改so库的名字怎么做啊?loadLibrary("main");这个调用我不会去控制的。
        9efe1db2c646:@彼时芒种 copyFilesFassets(context, oldPath + "/" + oldFileName, newPath + "/" + newFileName);
        9efe1db2c646:@秀宝APP段誉 拷贝的时候改名字就行了
      • WAIT丶Prenty: CommonUtils.copyFilesFassets(getApplicationContext(), "armeabi", path);这个函数怎么写的
      • 捡淑:马克
      • Maiyadatang:楼主 我现在用Android Studio 加载一个从es 导出的项目(包含依赖项目Library,Library 内有依赖的.so文件),现在,我在as上重新创建了同样的项目 把依赖包导进来,原项目的 用到的类和文件都导了进来,但是报错 dalvikvm: Trying to load lib /data/data/com.mediademo/lib/libutility.so 0x41744bd0
        09-02 06:46:07.177 14050-14050/com.mediademo D/dalvikvm: Added shared lib /data/data/com.mediademo/lib/libutility.so 0x41744bd0
        09-02 06:46:07.177 14050-14050/com.mediademo W/dalvikvm: No implementation found for native Lcom/third/videoeditor/adapter/UtilityAdapter;.FilterParserAction (Ljava/lang/String;I)I
        楼主能给个解决方法吗?谢谢啦!
        陈利健:@Maiyadatang 是不是目录有错,你把so和lib分别放到哪个目录下啦?
      • Android纯白:最后那个方法在哪使用,/EzvizAPI.init(getApplication(), key, path);这个又是什么,我遇到这个问题不知道怎么解决
        299e631bca16:@Lonely_sky 我也是,解决了吗?
      • helo123:楼主给份代码呗 691164005@qq.com
      • jiantaocd:楼主你好,请教两个问题:
        1.怎么判断自己加载so已经成功了?(我这里看到Dalvik是提示已load)
        2.自己加载完成后,第三方sdk初始化时调用System.loadLibrary报错(第三方jar包报出),您是怎么处理的?
        Mtlun:我也在遇到这问题,请问楼主解决了吗
      • 夜猫子程序猿:大神,能不能给一份完整的demo呢
      • 48da41d27731:还是不行啊 有几个so文件报了“is 32-bit instead of 64-bit” 然后又原来的异常,这个SDK只有一个so文件夹 怎么处理啊
        7a020e810f96:你的这个问题解决没有?我的也是遇到 这个问题,so文件放在libs下用普通的加载方法是没有问题的,但是一到了放到本地目录去读取就发生错误了。
        陈利健:@SiKang 你这种情况不一样。可能是CPU架构的问题,试试用32位和64位CPU的手机分别试试
      • 0b1ac496335f:您好,我也遇到不同jar包的so文件放在一起冲突的问题了,您能给发个demo参考一下吗
      • 说好的不改需求呢:楼主是如何加载sd卡下的so文件 ,sd下的文件是没有权限的
        9efe1db2c646:@ef8ab2a142f7 恩,就是这个意思
        WAIT丶Prenty:可以获取相对应目录,比如so放在本地内存中armeabi中,你可以String path = Environment.getExternalStorageDirectory()+“/armeabi/xx.so”
        System.Load(path); 这个方法是在你使用的so没有建java类文件,生成.h文件的情况下,不知道我说的好不好
        陈利健:@说好的不改需求呢 并不是房放在sd卡中,而是放在本地目录
      • 8076676b4e55:你好,jnilibs里面的冲突的不用删除?直接把冲突的拉出来就可以了?那么有个方法好像没有你哪里是怎么写的? (!TFileUtils.isFileExit(path + fileNames[i],这个方法快是干啥的?有更详细的源码啊,能否发我一份284361303@qq.com邮箱,谢谢了....
        陈利健:@shao_g
        jnilibs里面冲突的so需要删除。实际上就是从jnilibs里拉到assets中去。

        /**
        * 路径是否存在
        * @param path:路径
        * @Return: 是否
        */
        public static boolean isFileExit(String path) {
        if(path == null) {
        return false;
        } else {
        try {
        File f = new File(path);
        if(f.exists()) {
        return true;
        }
        } catch (Exception e) {
        e.printStackTrace();
        }

        return false;
        }
        }
        这一步骤其实是判断本地是否已经从assets拷贝出来so文件,因为拷贝的时间较长,每次拷贝太消耗资源,所以根据路径及文件名做一次判断,如果拷贝过则不需要再次拷贝。
      • b72e04d8558d:这样加载进去并没有对jni方法作映射吧?调用jni方法貌似会失败。
        b72e04d8558d:@Loranzo 我的锅,看错了。不是 dlopen的。
      • 9efe1db2c646:这尼玛,看到最后一行代码才知道也是萤石云作的孽。。。我勒个擦,萤石云和融云不知道为啥只会把融云的so文件拷贝进去,萤石云的就不管了。谢谢!
        9efe1db2c646:@14e28be54aff 自己手动拷贝就行了...
        14e28be54aff:@彼时芒种 你好 , 最后你解决了吗 ,最近我也遇到这个问题了

      本文标题:Android动态加载so文件(解决so文件冲突)

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