背景阐明

在业务开发过程中常常会进入一些三方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的动作目前是入手实现的,须要做成脚本。