1. 创建Task删除so库
- 创建Task,遍历build/intermediates/merged_native_lib 目录下的so文件
- 将自定义Task插入到mergeDebugNativeLibs之后,stripDebugDebugSymbols
task(dynamicSo) {
println("register dynamicSo")
}.doLast {
println("dynamicSo insert!!!! ")
//projectDir 在哪个project下面,projectDir就是哪个路径
print(getRootProject().findAll())
def file = new File("${projectDir}/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a")
// 删除指定的so文件
if (file.exists()) {
file.listFiles().each {
if (it.isDirectory()) {
it.listFiles().each {
target ->
println("=====> so file name: ${target.name} filesize:${target.size()/1000}kb")
if(target.name == "libmvvmdemo.so"){
target.delete()
}
}
}
}
} else {
print("nil")
}
}
project.afterEvaluate {
println("dynamicSo task start ")
def customer = tasks.findByName("dynamicSo")
def merge = tasks.findByName("mergeDebugNativeLibs")
def strip = tasks.findByName("stripDebugDebugSymbols")
if (merge != null && strip != null) {
println("xianyu ===> dynamicSo task config")
customer.mustRunAfter(merge)
strip.dependsOn(customer)
}else{
println("xianyu ===> task not find")
}
}
2. 修改system.loadLibrary默认加载路径
- 参考thinker方案,反射获取classLoader的pathList,再获取nativeLibraryDirectories,这是一个List保存了查找so库的目录,在这个List首部添加自定义文件路径。
- 不同android版本的兼容方案不一样,反射的方法可能有调整,需要做好兼容性处理。
如上是pathList的对象内容。
// 参考thinker思路,在获取lib的路径列表前添加自定义的路径
final Field pathListField = loader.getClass().getSuperclass().getDeclaredField("pathList");
pathListField.setAccessible(true);
Object pathList = pathListField.get(loader);
Field nativeLibraryDirectories = pathList.getClass().getDeclaredField("nativeLibraryDirectories");
nativeLibraryDirectories.setAccessible(true);
ArrayList list = (ArrayList) nativeLibraryDirectories.get(pathList);
File file = getExternalFilesDir("soLib");//自定义的目录
list.add(0,file);//添加自定义的目录到列表头部
3. so库版本控制
版本控制需要考虑的问题:
- 不同手机的abi架构下发的so版本要和架构版本保持一致
- 不同apk版本下发的so版本要保持一致
- apk版本升级后要删除旧so,使用新的so
需要做的是每次release打包后将so库上传服务端,服务端管理每次版本发布时的so库存储,包括so对应的abi架构版本,apk版本,接口中通过参数来获取指定的so,客户端依据apk版本号为目录存储so,并在每次存储同时删除旧的so。
4. 多级依赖so库加载
Android Native 用来链接 so 库的 Linker.cpp dlopen 函数 的具体实现变化比较大(主要是引入了 Namespace 机制):以往的实现里,Linker 会在 ClassLoder 实例的 nativeLibraryDirectories 里的所有路径查找相应的 so 文件;更新之后,Linker 里检索的路径在创建 ClassLoader 实例后就被系统通过 Namespace 机制绑定了,当我们注入新的路径之后,虽然 ClassLoader 里的路径增加了,但是 Linker 里 Namespace 已经绑定的路径集合并没有同步更新,所以出现了 libxxx.so 文件能找到,而 liblog.so 找不到的情况。
至于 Namespace 机制的工作原理了,可以简单认为是一个以 ClassLoader 实例 HashCode 为 Key 的 Map,Native 层通过 ClassLoader 实例获取 Map 里存放的 Value(也就是 so 文件路径集合)。
解决方案:
- 自定义 System#load,加载 libxxx.so 前,先解析 libxxx.so 的依赖信息,再递归加载其依赖的 so 文件(推荐参考开源方案 github.com/facebook/So… )。
- 类似 Tinker,在合适的时机替换 ClassLoader 实例。
5. so加载失败的措施
如果网络无法获取到so库,则选择降级措施。
参考: