关于前端:Android解耦式so库加载方案

85次阅读

共计 1635 个字符,预计需要花费 5 分钟才能阅读完成。

背景阐明

在业务开发过程中常常会进入一些三方 sdk, 这些三方的 sdk 引入 so 库, 有些 so 库文件还比拟大, 这时候咱们就须要思考 so 库从网络获取异步加载, 缩小公布包的体积

传统计划

对于 so 异步加载计划, 网上的材料轻易搜下大把, 核心思想
so 库文件放到网络 -> 下载到本地沙盒 -> 通过 System.load 载入
看起来挺简略的 然而挺多材料没提到的是

so 库存在依赖关系

比方当你把友链交易 libunity.so 下载下来通过
System.load(“/data/data/com.example.soload/files/libunity.so”)
加载的时候会失去如下异样

java.lang.UnsatisfiedLinkError: dlopen failed: library“libmain.so”not found
at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
at java.lang.System.loadLibrary(System.java:1667)

解决方案

载入 libunity.so 之前须要先载入 libmain.so

以上 2 个开源库 都有解决相干逻辑
核心思想就是解析 so 库 ELF 格局, 剖析依赖并递归, 直到依赖库都加载实现再载入本身
这里不扩大开来 有趣味的搭档能够看源码钻研下

text relocations 问题

当你把 liblbs.so 下载下来通过

System.load(“/data/data/com.example.soload/files/liblbs.so”)的时候又出现意外了
java.lang.UnsatisfiedLinkError: dlopen failed:“/data/data/com.example.soload/files/liblbs.so”has
at java.lang.Runtime.load0(Runtime.java:938)
at java.lang.System.load(System.java:1631)==

扩大思考:

咱们能够通过命令进行自查

有源码的能够从新编译, 没有源码的只能通过升高 targetSdkVersion 解决(然而也是治标不治本)

进阶计划

除了 System.load 计划 还有没有其余计划呢
答案当然是必定的
外围思路: 通过反射把自定义的 native 库 path 插入 nativeLibraryDirect ories 最后面,即便安装包 libs 目录外面有同名的 so,也优先加载指定门路的内部 so
这里截取 tinker 局部代码
com.tencent.tinker.lib.library.TinkerLoadLibrary.java

这个计划是不是看起来更简略了, 因为这个计划是非常规伎俩, 存在兼容危险, 咱们须要验证该计划的兼容性, 写个 helloword 的 so, 通过该计划加载, 提前找个版本线上带上去, 进行埋点统计
截至目前最新版上线 2 天 统计到数据如下

其中失败的 1 例是上线前自测成心让校验失败产生的
也就是说截至目前能够认为改计划是靠谱的
当然了如果后续有新的 Android 零碎版本更新
咱们还须要关注新版本的兼容状况

我的项目利用

下面铺垫了那么多, 当初进入正题
技术计划是一回事, 落实到理论我的项目是另一回事
当初开始咱们思考下如下几个问题

我的项目中援用的 so 库都是那些性能再用何时载入

三方 jar(aar)引入的 so 库加载机会不受咱们代码逻辑管制

应用的 so 库那些适宜做异步加载

so 库文件没有下载实现之前 用户用相干性能如何解决

版本迭代 so 文件降级如何解决

打包阶段如何把 so 文件进行剔除

解决问题 1、2、3

咱们能够通过切面编程的思路解决, 这里用到了开源因为 so 库的加载都是通过调用零碎函数 System.loadLibrary 进行,
那么咱们全局拦挡该函数的调用并打印调用链,
运行 app, 配合日志, 就能剖析 so 库的具体应用状况

正文完
 0