博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 开发中如何动态加载 so 库文件
阅读量:6670 次
发布时间:2019-06-25

本文共 4567 字,大约阅读时间需要 15 分钟。

我想对于静态加载 so 库文件,大家都已经很熟悉了,这里就不多说了。在 Android 开发中调用动态库文件(*.so)都是通过 jni 的方式,而静态加载往往是在 apk 或 jar 包中调用so文件时,都要将对应 so 文件打包进 apk 或 jar 包。

动态加载的优点

静态加载,不灵活,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();            return 0;        } 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 文件。

欢迎大家关注我的技术分享公众号:非著名程序员(smart_android)。技术文章均先首发于我的技术分享的微信公众号。

转载地址:http://dilxo.baihongyu.com/

你可能感兴趣的文章
myeclipse删除项目后重新导入
查看>>
在进入新版本 的时候,进行推送引导
查看>>
Django 下载和初识
查看>>
Hawk浩客 v992 硬盘 易驱线 试用
查看>>
HBase + Solr Cloud实现HBase二级索引
查看>>
Scala之偏函数Partial Function
查看>>
hdu 5052 树链剖分
查看>>
移动端bug集合
查看>>
springboot学习笔记-thymeleaf
查看>>
Mac下到Linux主机ssh免密码登录
查看>>
git的使用(本地版本库)
查看>>
密码学之编码方式
查看>>
[网页游戏开发]Morn简介及使用教程
查看>>
Python 基础一
查看>>
oracle where 条件的执行顺序分析1
查看>>
sql server 2008学习5 sql基础
查看>>
windows store app promise
查看>>
Unsharp Mask(USM)锐化算法的的原理及其实现。
查看>>
禁用文本框粘贴功能
查看>>
EBS 责任的定义
查看>>