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

52次阅读

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

背景阐明

在业务开发过程中常常会进入一些三方 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

扩大思考

咱们写代码的时候怎么管制加载程序呢
https://github.com/facebook/S…
https://github.com/KeepSafe/R…
以上 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 text relocations (https://android.googlesource….
at java.lang.Runtime.load0(Runtime.java:938)
at java.lang.System.load(System.java:1631)==

起因剖析:

https://blog.csdn.net/chjqxxx…

扩大思考:

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

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

进阶计划

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

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

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

我的项目利用

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

  1. 我的项目中援用的 so 库都是那些性能再用何时载入
  2. 三方 jar(aar)引入的 so 库加载机会不受咱们代码逻辑管制
  3. 应用的 so 库那些适宜做异步加载
  4. so 库文件没有下载实现之前 用户用相干性能如何解决
  5. 版本迭代 so 文件降级如何解决
  6. 打包阶段如何把 so 文件进行剔除
解决问题 1、2、3

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


这里咱们须要依据本人业务场景剖析, 比方 app 进入到首页依赖的库不倡议进行异步加载, 须要异步加载的库最好是实现一个性能(咱们须要依照教训进行逻辑分组 参考文章结尾的图)

解决问题 4、5

Android 是根据 Activity 作为流动单位, 启动 Activity 是通过 startActivity 函数调用进行的, 那么咱们就能够拦挡织入本人的逻辑
大抵思路如下

对应实现局部代码截图


通过规定配置化 + 字节码拦挡的逻辑 对原有业务无侵入即可实现动静加载
研发关注失常的业务逻辑 不须要针对性编写代码
后续引入新的 so 库, 批改配置文件即可
在实现的过程中有些细节是须要思考的
比方校验下载文件的完整性, 下载要不要反对断点续传,so 文件须要更新如何解决等

解决问题 6

通过下面的介绍, 咱们实现了 so 文件的异步加载
然而打包的时候 咱们如何剔除 so 缩小最终公布包的大小
能够在 build.gradle 进行配置

然而下面提到了咱们是通过 json 配置的规定
这样再反复配置一遍
而且还有可能 2 处配置不统一
而且咱们的大准则是解耦
那么咱们就持续 hook 打包流程

汇总小结

通过下面的介绍大抵阐明了原理

有趣味的搭档能够评论区留言,提出本人的想法

目前还在灰度验证阶段,等线上验证稳固后在评论区放出相干代码

so 异步加载计划大同小异,该计划跟其余计划相比

个人感觉特色就是侵入性低

接入只须要 3 个步骤


未完待续

目前该计划还存在几个点有待优化

====================

问题 1

目前用的是进阶计划实现的 so 库加载 比拟现实的形式是传统计划和进阶计划都应该反对 一开始其实我想用传统计划去实现 然而该计划在 unity 那块有问题(unity 相干 so 的加载逻辑有点非凡 存在通过 jni 间接加载 so 的状况 因为没有源码 外部逻辑不太分明) 后续还须要钻研欠缺 毕竟官网 System.load 是官网提供的 api 更牢靠

问题 2

进度加载的 dialog 是依附于调用 startActivity 的窗口
如果 startActivuty 之后外面 立马调用 finish 会导致 dialog 泄露
这个是须要留神的一个点 也就意味着对原有逻辑存在肯定依赖
这个能够通过启动一个 FLAG_ACTIVITY_NEW_TASK 类型的 activity
代理解决 然而感觉这样有点重 目前还有点纠结

问题 3

目前文件只是放到了阿里云, 用户的网络环境复杂多变, 须要思考反对腾讯云, 七牛云等更多的服务商。

问题 4

上传到 oss 的动作目前是入手实现的, 须要做成脚本。

正文完
 0