zoukankan      html  css  js  c++  java
  • Android 开发中如何动态加载 so 库文件(转载)

    动态加载的优点

    静态加载,不灵活,apk 包有可能大。所以采用动态加载 so 库文件,有以下几点好处:

    1. 灵活,so 文件可以动态加载,不是绑定死的,修改方便,so 库有问题,我们可以动态更新。
    2. so 库文件很大的话,采用动态加载可以减少 apk 的包,变小。
    3. 其实我们常用第三方 so 库,单个可能没问题,如果多个第三方 so 库文件,同时加载可能会出现冲突,而动态加载就能够解决这一问题。

    注意路径陷阱

    动态加载 so 库文件,并不是说可以把文件随便存放到某个 sdcard 文件目录下,这样做既不安全,系统也加载不了。

    我们在 Android 中加载 so 文件,提供的 API 如下:

    //第一种,pathName 库文件的绝对路径
    void System.load(String pathName);
    //第二种,参数为库文件名,不包含库文件的扩展名,必须是在JVM属性Java.library.path所指向的路径中,路径可以通过System.getProperty('java.library.path') 获得
    void loadLibrary(String libname)

    注意:而这里加载的文件路径只能加载两个目录下的 so 文件。那就是:

    1. /system/lib
    2. 应用程序安装包的路径,即:/data/data/packagename/…

    所以,so 文件动态加载的文件目录不能随便放。这是需要注意的一点。

    实现思路

    既然使用动态加载的好处和陷阱我们都大致了解了,那就可以在实现的时候,注意陷阱就可以了。那基本思路如下:

    1. 网络下载 so 文件到指定目录
    2. 从指定下载的目录复制 copy so文件到可动态加载的文件目录下,比如:/data/data/packagename/…
    3. 配置 gradle ,指定 cpu 架构
    4. load 加载

    第一步,我们这里可以简单忽略,假设我们把 so 文件下载到了 /mnt/sdcard/armeabi 目录下。

    复制目录到包路径下

    那我们就应该把 /mnt/sdcard/armeabi 目录下的 so 文件,复制到 应用的包路径下。

    /**
     * Created by loonggg on 2017/3/29.
     */
    
    public class SoFile {
        /**
         * 加载 so 文件
         * @param context
         * @param fromPath 下载到得sdcard目录
         */
        public static void loadSoFile(Context context, String fromPath) {
            File dir = context.getDir("libs", Context.MODE_PRIVATE);
            if (!isLoadSoFile(dir)) {
                copy(fromPath, dir.getAbsolutePath());
            }
        }
    
        /**
         * 判断 so 文件是否存在
         * @param dir
         * @return
         */
        public static boolean isLoadSoFile(File dir) {
            File[] currentFiles;
            currentFiles = dir.listFiles();
            boolean hasSoLib = false;
            if (currentFiles == null) {
                return false;
            }
            for (int i = 0; i < currentFiles.length; i++) {
                if (currentFiles[i].getName().contains("libwedsa23")) {
                    hasSoLib = true;
                }
            }
            return hasSoLib;
        }
    
        /**
         *
         * @param fromFile 指定的下载目录
         * @param toFile 应用的包路径
         * @return
         */
        public static int copy(String fromFile, String toFile) {
            //要复制的文件目录
            File[] currentFiles;
            File root = new File(fromFile);
            //如同判断SD卡是否存在或者文件是否存在,如果不存在则 return出去
            if (!root.exists()) {
                return -1;
            }
            //如果存在则获取当前目录下的全部文件 填充数组
            currentFiles = root.listFiles();
    
            //目标目录
            File targetDir = new File(toFile);
            //创建目录
            if (!targetDir.exists()) {
                targetDir.mkdirs();
            }
            //遍历要复制该目录下的全部文件
            for (int i = 0; i < currentFiles.length; i++) {
                if (currentFiles[i].isDirectory()) {
                    //如果当前项为子目录 进行递归
                    copy(currentFiles[i].getPath() + "/", toFile + currentFiles[i].getName() + "/");
                } else {
                    //如果当前项为文件则进行文件拷贝
                    if (currentFiles[i].getName().contains(".so")) {
                        int id = copySdcardFile(currentFiles[i].getPath(), toFile + File.separator + currentFiles[i].getName());
                    }
                }
            }
            return 0;
        }
    
        //文件拷贝
        //要复制的目录下的所有非子目录(文件夹)文件拷贝
        public static int copySdcardFile(String fromFile, String toFile) {
            try {
                FileInputStream fosfrom = new FileInputStream(fromFile);
                FileOutputStream fosto = new FileOutputStream(toFile);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while ((len = fosfrom.read(buffer)) != -1) {
                    baos.write(buffer, 0, len);
                }
                // 从内存到写入到具体文件
                fosto.write(baos.toByteArray());
                // 关闭文件流
                baos.close();
                fosto.close();
                fosfrom.close();return0;}catch(Exception ex){return-1;}}}

    配置 grade 指定 cpu 架构

    我们都知道,在使用 so 文件的时候,so 库类型和 CPU 架构类型,要一致,否则是会报错的。原因很简单,不同 CPU 架构的设备需要用不同类型 so 库。CPU架构有如下几种类型:ARMv5,ARMv7,x86,MIPS,ARMv8,MIPS64 和 x86_64。如果要适配很多手机,就需要在不同的类型下,放置对应的 so 文件。
    配置方法如下:

    defaultConfig {
            applicationId "xxxx"
            minSdkVersion 17
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
    
            ndk {
                abiFilters "armeabi","armeabi-v7a","x86"
            }
        }

    load 加载 so 文件

    复制到可加载使用的包路径下后,配置完 gradle 之后,就可以使用 load API 调用了。

    File dir =  getApplicationContext().getDir("libs", Context.MODE_PRIVATE);
    File[] currentFiles;
    currentFiles = dir.listFiles();
    for (int i = 0; i < currentFiles.length; i++) {
       System.load(currentFiles[i].getAbsolutePath());
    }

    这样,我们就实现了动态加载 so 文件。


    作者: 涩郎_非著名程序员 
    链接:http://www.imooc.com/article/17298
    来源:慕课网
    本文原创发布于慕课网 ,转载请注明出处,谢谢合作

  • 相关阅读:
    读《SQL优化核心思想》:你不知道的优化技巧
    MySQL 索引建立原则及注意事项
    MySQL 捕获有问题的SQL-慢查日志 实例
    MySQL 分区间进行数据展示 实例
    MySQL 删除重复数据实例
    MySql 索引优化实例
    JDK1.6 Java.lang.Null.Pointer.Exception
    关于 MySQL LEFT JOIN 不可不知的事
    搞定queryString
    数据库不得不说的陷阱
  • 原文地址:https://www.cnblogs.com/cold-ice/p/8808739.html
Copyright © 2011-2022 走看看