我想对于静态加载 so 库文件,你们都已经很熟悉了,这里就很少说了。在 Android 开发中调用动态库文件(*.so)都是经过 jni 的方式,而静态加载每每是在 apk 或 jar 包中调用so文件时,都要将对应 so 文件打包进 apk 或 jar 包。java
静态加载,不灵活,apk 包有可能大。因此采用动态加载 so 库文件,有如下几点好处:android
动态加载 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 文件。那就是:安全
因此,so 文件动态加载的文件目录不能随便放。这是须要注意的一点。微信
既然使用动态加载的好处和陷阱咱们都大体了解了,那就能够在实现的时候,注意陷阱就能够了。那基本思路以下:网络
第一步,咱们这里能够简单忽略,假设咱们把 so 文件下载到了 /mnt/sdcard/armeabi 目录下。架构
那咱们就应该把 /mnt/sdcard/armeabi 目录下的 so 文件,复制到 应用的包路径下。app
/** * 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;
}
}
}复制代码
咱们都知道,在使用 so 文件的时候,so 库类型和 CPU 架构类型,要一致,不然是会报错的。缘由很简单,不一样 CPU 架构的设备须要用不一样类型 so 库。CPU架构有以下几种类型:ARMv5,ARMv7,x86,MIPS,ARMv8,MIPS64 和 x86_64。若是要适配不少手机,就须要在不一样的类型下,放置对应的 so 文件。
配置方法以下:gradle
defaultConfig {
applicationId "xxxx"
minSdkVersion 17
targetSdkVersion 25
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi","armeabi-v7a","x86"
}
}复制代码
复制到可加载使用的包路径下后,配置完 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)。技术文章均先首发于个人技术分享的微信公众号。