此处参考了https://blog.csdn.net/qq_32877045/article/details/52673246这篇文章,但是这篇文章的方法其实兼容性还是不全,本文在此之上做了补全。
在做动态加载so库的过程中,发现只使用全局加载so(System.load())其实并不是完美适用于所有情况,需要添加自定义so路径至系统so路径中才不会报错。
此外,纠正几个网上广为流传的几个错误认知:
1、只有System.load()才能加载非系统目录下的so文件,不能使用System.loadLibray()。
说法错误,其实这两个方法殊路同归,并没有什么本质区别,只是System.load()可以使用全局路径,我们把自定义路径加入到系统路径中,一样可以使用System.loadLibray()。
2、添加自定义so路径至系统so路径中这段代码需要写在Application中初始化。
说法错误,你啥时候用啥时候初始化不就完了。。懒加载就行了。。直接在你需要使用的类中添加static{}静态加载块中加载就行了。。感觉网上很多东西大家不思考就往上发,很多东西都是错的。。
还有一点经验:
1、不同的classLoader不可以加载同一个so文件,源码就是这么写的。。如果你想加载,那就复制一份,再去加载就ok了。
以下就是添加自定义so路径的代码了,欢迎拍砖。
/**
* android应用的so文件路径修改,添加自定义的so路径
* @param context
*/
public static void initNativeDirectory(Context context, String path) {
if (hasDexClassLoader()) {
try {
createNewNativeDirAboveEqualApiLevel14(context, path);
} catch (Exception e) {
try {
createNewNativeDirAboveEqualApiLevel21(context, path);
} catch (Exception e1) {
e1.printStackTrace();
}
}
} else {
try {
createNewNativeDirBelowApiLevel14(context, path);
} catch (Exception e) {
if (DEBUG) {
e.printStackTrace();
}
}
}
}
/**
* 4.0以上系统添加方案
* @param context
* @param path 自定义so路径
* @throws Exception
*/
private static void createNewNativeDirAboveEqualApiLevel14(Context context, String path) throws Exception {
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
Object pathList = getPathList(pathClassLoader);
// 获取当前类的属性
Object nativeLibraryDirectories = pathList.getClass().getDeclaredField("nativeLibraryDirectories");
((Field) nativeLibraryDirectories).setAccessible(true);
// 获取 DEXPATHList中的属性值
File[] files = (File[]) ((Field) nativeLibraryDirectories).get(pathList);
Object newfiles = Array.newInstance(File.class, files.length + 1);
// 添加自定义.so路径
Array.set(newfiles, 0, new File(path));
// 追加系统路径
for (int i = 1; i < files.length + 1; i++) {
Array.set(newfiles, i, files[i - 1]);
}
((Field) nativeLibraryDirectories).set(pathList, newfiles);
}
/**
* 4.0以下系统添加方案
* @param context
* @param path 自定义so路径
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static void createNewNativeDirBelowApiLevel14(Context context, String path)
throws NoSuchFieldException, IllegalAccessException {
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
Field mLibPaths = pathClassLoader.getClass().getDeclaredField("mLibPaths");
mLibPaths.setAccessible(true);
String[] libs = (String[]) (mLibPaths).get(pathClassLoader);
Object newPaths = Array.newInstance(String.class, libs.length + 1);
// 添加自定义.so路径
Array.set(newPaths, 0, path);
// 追加系统路径
for (int i = 1; i < libs.length + 1; i++) {
Array.set(newPaths, i, libs[i - 1]);
}
mLibPaths.set(pathClassLoader, newPaths);
}
/**
* 5.0以上系统添加方案
* @param context
* @param path 自定义so路径
* @throws IllegalAccessException
* @throws NoSuchFieldException
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
private static void createNewNativeDirAboveEqualApiLevel21(Context context, String path)
throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return;
}
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
Object pathList = getPathList(pathClassLoader);
// 获取当前类的属性
Object systemNativeLibraryDirectories = pathList.getClass()
.getDeclaredField("systemNativeLibraryDirectories");
((Field) systemNativeLibraryDirectories).setAccessible(true);
List<File> systemFiles = (List<File>) ((Field) systemNativeLibraryDirectories).get(pathList);
systemFiles.add(new File(path));
((Field) systemNativeLibraryDirectories).set(pathList, systemFiles);
Object nativeLibraryDirectories = pathList.getClass().getDeclaredField("nativeLibraryDirectories");
((Field) nativeLibraryDirectories).setAccessible(true);
List<File> nativeFiles = (ArrayList<File>) ((Field) nativeLibraryDirectories).get(pathList);
nativeFiles.add(new File(path));
((Field) nativeLibraryDirectories).set(pathList, nativeFiles);
Class<?> elementClass = Class.forName("dalvik.system.DexPathList$Element");
Constructor<?> element = null;
element = elementClass.getConstructor(File.class, boolean.class, File.class, DexFile.class);
Object nativeLibraryPathElements = pathList.getClass().getDeclaredField("nativeLibraryPathElements");
((Field) nativeLibraryPathElements).setAccessible(true);
Object[] elementFiles = (Object[]) ((Field) nativeLibraryPathElements).get(pathList);
Object newElementFiles = Array.newInstance(elementClass, elementFiles.length + 1);
if (element != null) {
try {
Object newInstance = element.newInstance(
new File(path), true, null, null);
Array.set(newElementFiles, 0, newInstance);
for (int i = 1; i < elementFiles.length + 1; i++) {
Array.set(newElementFiles, i, elementFiles[i - 1]);
}
((Field) nativeLibraryPathElements).set(pathList, newElementFiles);
} catch (java.lang.IllegalArgumentException e) {
Method elements = pathList.getClass().getDeclaredMethod("makePathElements", List.class);
elements.setAccessible(true);
Object invoke = elements.invoke(null, nativeFiles);
Object nativeLibraryElements = pathList.getClass().getDeclaredField("nativeLibraryPathElements");
((Field) nativeLibraryElements).setAccessible(true);
((Field) nativeLibraryElements).set(pathList, invoke);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
/**
* 获取 pathList
* @param obj
* @return
* @throws ClassNotFoundException
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException,
IllegalAccessException {
return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
/**
* 通过反射获取成员变量
* @param obj
* @param cls
* @param str
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static Object getField(Object obj, Class cls, String str) throws NoSuchFieldException,
IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
return declaredField.get(obj);
}
/**
* 仅对4.0以上做支持
* @return
*/
private static boolean hasDexClassLoader() {
try {
Class.forName("dalvik.system.BaseDexClassLoader");
return true;
} catch (ClassNotFoundException var1) {
return false;
}
}