我想对于静态加载 so 库文件,大家都已经很熟悉了,这里就不多说了。在 Android 开发中调用动态库文件 (*.so) 都是通过 jni 的方式,而静态加载往往是在 apk 或 jar 包中调用 so 文件时,都要将对应 so 文件打包进 apk 或 jar 包。
静态加载,不灵活,apk 包有可能大。所以采用动态加载 so 库文件,有以下几点好处:
动态加载 so 库文件,并不是说可以把文件随便存放到某个 sdcard 文件目录下,这样做既不安全,系统也加载不了。
我们在 Android 中加载 so 文件,提供的 API 如下:
- //第一种,pathName 库文件的绝对路径
- void System.load(String pathName);
- //第二种,参数为库文件名,不包含库文件的扩展名,必须是在JVM属性Java.library.path所指向的路径中,路径可以通过System.getProperty('java.library.path') 获得
- voidloadLibrary(String libname)
注意:而这里加载的文件路径只能加载两个目录下的 so 文件。那就是:
所以,so 文件动态加载的文件目录不能随便放。这是需要注意的一点。
既然使用动态加载的好处和陷阱我们都大致了解了,那就可以在实现的时候,注意陷阱就可以了。那基本思路如下:
第一步,我们这里可以简单忽略,假设我们把 so 文件下载到了 /mnt/sdcard/armeabi 目录下。
那我们就应该把 /mnt/sdcard/armeabi 目录下的 so 文件,复制到 应用的包路径下。
- /**
- * Created by loonggg on 2017/3/29.
- */
- public classSoFile{
- /**
- * 加载 so 文件
- * @param context
- * @param fromPath 下载到得sdcard目录
- */
- publicstaticvoidloadSoFile(Context context, String fromPath){
- File dir = context.getDir("libs", Context.MODE_PRIVATE);
- if (!isLoadSoFile(dir)) {
- copy(fromPath, dir.getAbsolutePath());
- }
- }
- /**
- * 判断 so 文件是否存在
- * @param dir
- * @return
- */
- publicstaticbooleanisLoadSoFile(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
- */
- publicstaticintcopy(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;
- }
- //文件拷贝
- //要复制的目录下的所有非子目录(文件夹)文件拷贝
- publicstaticintcopySdcardFile(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 文件。
配置方法如下:
- 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("l ibs", Context.MODE_PRIVATE);
- File[] currentFiles;
- currentFiles = dir.listFiles();
- for (int i = 0; i < currentFiles.length; i++) {
- System.load(currentFiles[i].getAbsolutePath());
- }
这样,我们就实现了动态加载 so 文件。
来源: http://www.tuicool.com/articles/JFZZBfz