宿主与插件中so库加载区别 :
宿主应用程序 :build 生成apk ,不需要开发者自己去判断ABI,Android系统在安装APK的时候,不会安装APK里面全部的so库文件,而是会根据当前CPU类型支持的ABI,从APK里面拷贝最合适的so库,并保存在APP的内部存储路径的libs 下面。
插件应用程序apk : 动态加载插件的so,需要我们判断ABI类型来加载相应的so,Android系统不会帮我们处理。
加载so的两种方式 :
System.load()
:参数必须为库文件的绝对路径(注意点:不能放在sdcard中)
System.loadLibrary()
: 参数为库文件名,不包含库文件的扩展名
接下来,根据framework层中的源码,来了解native是如何加载,如何查找的。
应用程序会通过PathClassLoader加载java和c++的代码
HookDemo/Android插件化之so加载.md at master · 13767004362/HookDemo · GitHub
只要将插件中的so库放到nativeLibraryPathElements中,就可以自然而然的加载插件中c++代码了。
1、在插件项目中先编写c++代码文件,生成对应的cpu的so库,通过System.loadLibrary()
加载so库
2、接下来在宿主中编写。先根据手机cpu进行筛选,加载相应的so库,将宿主中so资源与插件so资源进行合并:
// 获取到DexPathList对象Class> baseDexClassLoaderClass = DexClassLoader.class.getSuperclass();Field pathListField = baseDexClassLoaderClass.getDeclaredField("pathList");pathListField.setAccessible(true);Object dexPathList = pathListField.get(appClassLoader);/*** 接下来,合并宿主so,系统so,插件so库*/Class> DexPathListClass = dexPathList.getClass();if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {// 先创建一个汇总so库的文件夹,收集全部List allNativeLibDirList = new ArrayList<>();// 先添加插件的so库地址allNativeLibDirList.addAll(pluginNativeLibraryDirList);// 获取到宿主的so库地址Field nativeLibraryDirectoriesField = DexPathListClass.getDeclaredField("nativeLibraryDirectories");nativeLibraryDirectoriesField.setAccessible(true);List old_nativeLibraryDirectories = (List) nativeLibraryDirectoriesField.get(dexPathList);allNativeLibDirList.addAll(old_nativeLibraryDirectories);// 获取到system的so库地址Field systemNativeLibraryDirectoriesField = DexPathListClass.getDeclaredField("systemNativeLibraryDirectories");systemNativeLibraryDirectoriesField.setAccessible(true);List systemNativeLibraryDirectories = (List) systemNativeLibraryDirectoriesField.get(dexPathList);allNativeLibDirList.addAll(systemNativeLibraryDirectories);//通过makePathElements获取到c++存放的ElementMethod makePathElementsMethod = DexPathListClass.getDeclaredMethod("makePathElements", List.class, List.class, ClassLoader.class);makePathElementsMethod.setAccessible(true);Object[] allNativeLibraryPathElements = (Object[]) makePathElementsMethod.invoke(null, allNativeLibDirList, new ArrayList(), appClassLoader);//将合并宿主和插件的so库,重新设置进去Field nativeLibraryPathElementsField = DexPathListClass.getDeclaredField("nativeLibraryPathElements");nativeLibraryPathElementsField.setAccessible(true);nativeLibraryPathElementsField.set(dexPathList, allNativeLibraryPathElements);} else {// 获取到宿主的so库地址Field nativeLibraryDirectoriesField = DexPathListClass.getDeclaredField("nativeLibraryDirectories");nativeLibraryDirectoriesField.setAccessible(true);File[] oldNativeDirs = (File[]) nativeLibraryDirectoriesField.get(dexPathList);int oldNativeLibraryDirSize = oldNativeDirs.length;// 创建一个汇总宿主,插件的so库地址的数组File[] totalNativeLibraryDir = new File[oldNativeLibraryDirSize + pluginNativeLibraryDirList.size()];System.arraycopy(oldNativeDirs, 0, totalNativeLibraryDir, 0, oldNativeLibraryDirSize);for (int i = 0; i < totalNativeLibraryDir.length; ++i) {totalNativeLibraryDir[oldNativeLibraryDirSize + i] = pluginNativeLibraryDirList.get(i);}// 替换成合并的so库资源数组nativeLibraryDirectoriesField.set(dexPathList, totalNativeLibraryDir);}