先说一下写这个的初衷, 公司做的项目是安卓设备5.0系统 最近需要集成人脸识别第三方. 犹豫环境特殊 需要做离线授权 也就是加载特定的授权离线SO 文件 由于 设备很多 避免多次打包APK 所以 通过 外部加载指定的SO文件来实现功能
下面 是具体的代码 和流程
/**
* 识别 动态调用so文件 注意事项及流程
* 1、 获取so文件存放路径(sdcard/Download)然后复制到date/date下调用 存放路径可自己设置
* 2、 FaceCheck.class 需要隐藏 System.loadLibrary("FaceCheck");
* 3、 APP bulid 下需要添加向下兼容的NDK
* 4、 提前删掉libs文件中的FaceCheck()
* 5、 Android.mk 添加 TARGET_CPU_API := armeabi APP_ABI := armeabi
*
*/
private void initdata() {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
if (LoadSoFileUtils.loadSoFile(MainActivity.this, "/sdcard/Download")!=0){
Toast.makeText(MainActivity.this,"未检测到SO文件,请配置好重新启动APP",Toast.LENGTH_LONG).show();
logger.debug("未检测到SO文件,请配置好重新启动APP,离线授权so文件可联系开发人员");
return;
}
File dir = MainActivity.this.getDir("Lib", Context.MODE_PRIVATE);
System.load(dir.getAbsolutePath() + "/libFaceCheck.so");
logger.debug("so文件动态加载成功");
} catch (Exception e) {
e.printStackTrace();
logger.debug("so文件动态加载失败");
}
Message message = Message.obtain();
message.what = SO_LOADING;
mHandler.sendMessage(message);
}
});
}
通常启用so文件 是使用System.loadLibrary 这个来通过调用 , 在此 我们复制完so文件后通过 System.load 来进行提前加载处理
package com.hongsen.outdoor.utils;
/**
* 作者:Mio
* 时间:2020/04/16.
* 邮箱:17379235050@163.com
* 描述:
*/
import android.content.Context;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* so库从sd卡拷贝到app的私有目录下,并进行比对验证和加载
*/
public class LoadSoFileUtils {
private static String nameSO = "libFaceCheck";
/**
* 加载 so 文件
*/
public static int loadSoFile(Context context, String fromPath) {
File dir = context.getDir("Lib", Context.MODE_PRIVATE);
String fromPathFile = fromPath + "/" + nameSO + ".so";
File isExist = new File(fromPathFile);
if (!isLoadSoFile(dir, isExist.exists())) {
return copy(fromPath, dir.getAbsolutePath());
} else {
return 0;
}
}
/**
* 判断 so 文件是否存在
*/
public static boolean isLoadSoFile(File dir, Boolean isExist) {
File[] currentFiles;
currentFiles = dir.listFiles();
boolean hasSoLib = false;
if (currentFiles == null) {
return false;
}
for (File currentFile : currentFiles) {
//判断里面是否存在这个库,以及sd也有这个库,那就删除,然后进行外面拷贝进去
if (currentFile.getName().contains(nameSO)) {
hasSoLib = isExist && !currentFile.delete();
}
}
return hasSoLib;
}
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();
if (currentFiles == null) {
return -1;
}
//目标目录
File targetDir = new File(toFile);
//创建目录
if (!targetDir.exists()) {
targetDir.mkdirs();
}
int statue = -1;
//遍历要复制该目录下的全部文件
for (File currentFile : currentFiles) {
if (currentFile.isDirectory()) {
//如果当前项为子目录 进行递归
copy(currentFile.getPath() + "/", toFile + currentFile.getName() + "/");
} else {
//如果当前项为文件则进行文件拷贝
if (currentFile.getName().contains(".so")) {
statue = copySdcardFile(currentFile.getPath(), toFile + File.separator + currentFile.getName());
}
}
}
return statue;
}
//文件拷贝
//要复制的目录下的所有非子目录(文件夹)文件拷贝
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;
}
}
}
//ndk兼容 是用来动态加载so库 放到 android {}里面
ndk {
abiFilters("armeabi", "armeabi-v7a", "x86")
}
下面两行放到android.mk文件中
TARGET_CPU_API := armeabi
APP_ABI := armeabi
友情提示: APK中无需再放置 授权的SO 文件 直接通弄过代码复制到指定文件 直接调用就可以
如果机器没有ROOT可以 自己下载一个re文件管理器 查看 文件是否复制成功
整体流程就那么多 有疑问 大家可以留言