在开发过程中,经常会用到第三方库,比如地图、视频、文档编辑、图表之类。依赖这些库,需要添加其SDK,有时需要用到jni层的So文件,比如百度地图等。
那么问题来了,如果两个不同的库之间的so文件发生冲突这么办?
比如:单独添加地图的库,运行没有问题。单独添加一个视频库,运行没有问题。但两者同时添加,其中一个库在init的时候报错。这当然和第三方库的开发水平有很大关系,但我们怎么解决这个问题呢?这就用到动态加载的方法。
这是原先的静态加载方法,将所有依赖库的so文件全部一股脑的放进armeabi文件夹即可。
静态加载
动态加载的方法,我将冲突的so文件放在assets文件夹中
动态加载
这里需要注意的是:动态加载so的文件只能放在两个地方:1. lib文件夹中,即对应Android Studio中的jniLibs文件夹。2. 本地data/data/package数据目录下。 所以,当应用第一次启动的时候,必须将我们放在assets文件夹中的so文件拷贝乳本地数据目录下。
//每次进入app,遍历assets目录下所有的文件,是否在data/data目录下都已经存在,不存在则拷贝
private void initAssetsFile() {
boolean needCopy = false;
// 创建data/data目录
File file = getApplicationContext().getFilesDir();
String path = file.toString() + "/armeabi/";
// 遍历assets目录下所有的文件,是否在data/data目录下都已经存在
try {
String[] fileNames = getApplicationContext().getAssets().list("armeabi");
for (int i = 0; fileNames != null && i < fileNames.length; i++) {
if (!TFileUtils.isFileExit(path + fileNames[i])) {
needCopy = true;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
if (needCopy) {
CommonUtils.copyFilesFassets(getApplicationContext(), "armeabi", path);
}
}
//将旧目录中的文件全部复制到新目录
public static void copyFilesFassets(Context context, String oldPath, String newPath) {
try {
// 获取assets目录下的所有文件及目录名
String fileNames[] = context.getAssets().list(oldPath);
// 如果是目录名,则将重复调用方法递归地将所有文件
if (fileNames.length > 0) {
File file = new File(newPath);
file.mkdirs();
for (String fileName : fileNames) {
copyFilesFassets(context, oldPath + "/" + fileName, newPath + "/" + fileName);
}
}
// 如果是文件,则循环从输入流读取字节写入
else {
InputStream is = context.getAssets().open(oldPath);
FileOutputStream fos = new FileOutputStream(new File(newPath));
byte[] buffer = new byte[1024];
int byteCount = 0;
while ((byteCount = is.read(buffer)) != -1) {
fos.write(buffer, 0, byteCount);
}
fos.flush();
is.close();
fos.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//在需要初始化SDK的地方,指定so文件的路径
private void initAPI() {
File file = getApplicationContext().getFilesDir();
String path = file.toString() + "/armeabi/";
EzvizAPI.init(getApplication(), key, path);
}
当然,上面这个方法是将so文件放在程序的assets文件夹。另一种方法是:也可以从网络上下载,放入本地数据目录下。这样的好处是不仅减小的了apk的大小,而且可以随时使用最新的依赖库,这也是动态加载的最多得用途之一。
网友评论
09-02 06:46:07.177 14050-14050/com.mediademo D/dalvikvm: Added shared lib /data/data/com.mediademo/lib/libutility.so 0x41744bd0
09-02 06:46:07.177 14050-14050/com.mediademo W/dalvikvm: No implementation found for native Lcom/third/videoeditor/adapter/UtilityAdapter;.FilterParserAction (Ljava/lang/String;I)I
楼主能给个解决方法吗?谢谢啦!
1.怎么判断自己加载so已经成功了?(我这里看到Dalvik是提示已load)
2.自己加载完成后,第三方sdk初始化时调用System.loadLibrary报错(第三方jar包报出),您是怎么处理的?
System.Load(path); 这个方法是在你使用的so没有建java类文件,生成.h文件的情况下,不知道我说的好不好
jnilibs里面冲突的so需要删除。实际上就是从jnilibs里拉到assets中去。
/**
* 路径是否存在
* @param path:路径
* @Return: 是否
*/
public static boolean isFileExit(String path) {
if(path == null) {
return false;
} else {
try {
File f = new File(path);
if(f.exists()) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
这一步骤其实是判断本地是否已经从assets拷贝出来so文件,因为拷贝的时间较长,每次拷贝太消耗资源,所以根据路径及文件名做一次判断,如果拷贝过则不需要再次拷贝。