静态编译不再多说,可以查看这篇文章:《Android6.0 NDK 和 .So 之间的关系》

优点

为什么我们需要动态加载?因为静态加载中CPU的文件夹我们可能需要兼容的话需要放在不同arm文件夹下,那么就会导致apk 包体过大,还有安卓Android SDK系统版本导致的差异,所以我们采用动态加载 so 库文件的话最主要的好处就是可以减小包体,其次,我们去修改so库时也比较方便,因为是动态的,我们可以动态的同步更新。

还有另外一个原因就是,我们常常会用到第三方的 so 库,如果单个库可能没问题,如果多个第三方 so 库文件,同时加载可能会出现冲突,比如说腾讯的YSDK和BUGLY,而动态加载就能够很好的解决这个问题。

实现步骤

基本步骤如下:

  • 第一步,从网络下载 so库文件到指定目录。
  • 然后,从指定的目录中 复制 copy so库文件到自身包名目录下,比如:/data/data/packagename/… ,当然我们需要配置 gradle ,这一点很多人会不记得操作,记得需要指定 cpu 的架构和可动态加载的文件。如(​/data/data/immqy/miqiyun/...)。

最后load 加载。

那我们如何操作? 第一步:网络下载so库,其实就是下载文件,比较简单,这里不再过多的阐述,可查看文件下载的​《Android 异步文件下载》 第二步:我们假设指定目录为SdCard中的IMMQY文件夹,那么路径为:​/mnt/sdcard/IMMQY/,把此路径复制到包下的可运行路径,代码如下:

/**
*
* Author:Karl-Dujinyang
*
* 2016/11/10
*/
public class SoFile {

/**
* 加载 so 文件
* @param context
* @param fromPath 下载到得sdcard目录
*/
public static void loadSoFile(Context context, String fromPath) {
File dirs = context.getDir("libs", Context.MODE_PRIVATE);
if (!isLoadSoFiles(dirs)) {
copyFile(fromPath, dir.getAbsolutePath());
}
}

/**
* 判断immqy so 文件是否存在
* @param dir
* @param name "libimmqy" so库
* @return boolean
*/
public static boolean isLoadSoFiles(String name,File dirs) {
boolean getSoLib = false;
File[] currentFiles;
currentFiles = dirs.listFiles();
if (currentFiles == null) {
return false;
}
for (int i = 0; i < currentFiles.length; i++) {
if (currentFiles[i].getName().contains(name)) {
getSoLib = true;
}
}
return getSoLib;
}


/**
* 要复制的目录下的所有非子目录(文件夹)文件拷贝
* @param fromFile 指定的下载目录
* @param toFile 应用的包路径
* @return int
*/
public static int copySdcardFile(String fromFiles, String toFile) {
try {
FileInputStream fileInput = new FileInputStream(fromFiles);
FileOutputStream fileOutput = new FileOutputStream(toFile);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
byte[] buffer = new byte[1024*1];
int len = -1;
while ((len = fileInput.read(buffer)) != -1) {
byteOut.write(buffer, 0, len);
}
// 从内存到写入到具体文件
fileOutput.write(baos.toByteArray());
// 关闭文件流
byteOut.close();
fileOutput.close();
fileInput.close();
return 0;
} catch (Exception ex) {
return -1;
}
}



/**
*
* @param fromFile 指定的下载目录
* @param toFile 应用的包路径
* @return int
*/
public static int copyFile(String fromFiles, String toFile) {
//要复制的文件目录
File[] currentFiles;
File root = new File(fromFiles);
//如同判断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()) {
//如果当前项为子目录 进行递归
copyFile(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;
}
}

代码写入到目录后,我们就需要配置 grade 了。

配置方法如下:

defaultConfig {
applicationId "immqy"
minSdkVersion 16
targetSdkVersion 26
versionCode 2
versionName "2.2"
ndk {
abiFilters "armeabi","armeabi-v7a","x86"
}
}

提示:CPU架构有如下几种类型:ARMv5,ARMv7,ARMV7A,x86,MIPS,ARMv8,MIPS64 和 x86_64,so 库类型和 CPU 架构类型要一致,否则是会报错的。

配置完成后,就剩下最后一步了。

第三步:load代码加载进行调用,很简单,代码如下:

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

需要注意的是,getApplicationContext引用不能为空,所以最好还是先判断下上下文对象是否为空。

这样就可以使用 load API 调用动态加载 so 文件了。

踩坑

有人会想,文件下载过来是否安全,其实是否定的,大家也可以把文件加密,或者把文件路径丢深一点。

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

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

注意:而这里加载的文件路径只能加载两个目录下的 so 文件,那就是:/system/lib和/data/data/packagename/…(应用程序安装包下的路径)

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

|| 版权声明:本文为博主杜锦阳原创文章,转载请注明出处。