Atlas框架浅析
简介
Atlas由来
Atlas 是希腊神话中擎天的巨人。 (寓为脚踏底层框架,为应用撑起一片天)
2013年手淘人员倍增,整个客户端架构重构,形成了今天的 Atlas。
作用
- 工程期,实现工程独立开发,调试的功能,工程模块独立。
- 运行期,实现完整的组件生命周期的映射,类隔离等机制。
- 运维期,提供快速增量的更新修复能力,快速升级。
关于组件化
上图是 Google 的 模块化手机,与该框架在许多地方有异曲同工之妙。
Atlas 框架支持大量丰富的业务接入,同时业务之间又能够保持相对的独立性,耦合性低,各自可以灵活的升级维护。
一些底层业务,例如缓冲框架,图片加载框架,网络加载框架等中间件代码又能够全面复用。
上图中,上层是许多独立业务,组件 Bundle 之间又可以通过路由调用其他业务。下层是底层共享框架,由容器统一管理。
框架原理
容器框架
该框架自下而上主要分为5层
- Hack 层:一些系统的注入以及工具类的初始化校验等,并在容器启动时进行全局性的兼容校验。
- Bundle Framework 层:负责组件的安装、更新、操作以及管理 Bundle 的生命周期。
- Runtime 层:主要是版本、清单和代理管理。
- Business 层:向业务方暴露一些接口。
- 应用接入层:完成 Atlas 的初始化功能,业务代码。
技术细节
Bundle 生命周期
- startInstall 开始加载
- Installed 加载完毕 注入资源路径,开始创建
class loader
- resolved 解析完毕 校验组件的安全性
- active 运行组件 Bundle
- started 运行成功
Runtime层详解
清单
OSGI 规范中每个组件通常通过 OSGI.MF 来暴露自身的 component ,这是与 Atlas 容器所不同的地方。在 Android 设备上,多文件的形式很容易受IO异常的影响干扰 bundle 正常运行,所以我们采用了构建期间集中生成清单的方式,清单里面记录 bundle 所有的 component (Android 四大组件),依赖、packagename 等内容。
版本
每个组件在构建期间就由构建插件分配自己的版本号,同时安装期间也有各自的版本目录,每个 bundle 的启动加载都需要经过版本的校验,组件发生更新同时也下发最新的版本信息。依托版本管理机制组件的热更新能力水到渠成。
代理
最核心的两个代理点:一个是 DelegateClassLoader
:负责路由 class
加载到各个 bundle 内部,第二个是 DelegateResource
:负责资源查找时能够找到 bundle 内的资源;这是 bundle 能够真正运行起来的根本;其余的代理点均是为了保证在必要的时机按需加载起来目标 bundle,让其可以被 DelegateClassloader
和 DelegateResource 使用。
类加载机制
正因为 classloader
的存在,组件的独立性有了充分的保证。利用 Delegate ClassLoader
来动态加载组件的类。Delegate ClassLoader
先查找宿主 Bundle 的 PathClassLoader
,然后根据前面的 BundleList 找到对应的 BundleClassLoader
.
资源加载机制
所有的 Bundle 资源会按顺序被添加到 AssertPath 中,DelegateResource 会替换系统的 resource。
动态部署
技术分析
之前的组件化只是解决了耦合性的问题,动态部署实现了随时发包的功能。
- 构建 一般的 Apk 更新产物是 apk,动态部署的构建产物是一个后缀为 tpatch 格式的文件。
- Merge 下载到 tpatch 文件之后,在后台完成 Merge 到安装的过程,整个过程对用户是透明的。
- 生效 Merge 完成后,会在 App 下次重新打开的时候完成此次的部署。
构建
主 Apk 和 bundle 的差量采用的是不同的策略,主Apk始终是与基线版本做差量,而 bundle 是与上一个版本做差量。
例如 1.0 动态部署到 1.1 再到 1.2,主 Apk 在 1.1 升 1.2 的时候是将 1.2 与 1.0 (也就是基线)做差量而不是1.1。而 bundle 则是逐个版本做差量分析。
Merge
基于手淘自主研发的差量算法,主 Bundle (也就是主 Apk )基于 ClassLoader
机制,业务 Bundle 基于差量 merge。
主 Bundle 的 Merge 在 Dalvik 和 [Art] (http://baike.baidu.com/item/Android%20runtime?sefr=cr) ( Android Runtime )中是不同的。
Dalvik 设备上没有任何 Merge 过程,直接把 libcom_taobao_maindex.so 以 bundle 的形式安装到 com.taobao.maindex 目录下。
Art 设备上我们会根据 source 的 apk(主 apk 的 merge 永远是基于基线版本)把 classes.dex 提取出来以多 dex 的方式追加到 libcom_taobao_maindex.so 中,如果本身是多 dex 机制的,那么会将多个子 dex 全部追加进去,patch 里面的 classes.dex 保持不变,source 里面的 dex 的序列号往后偏移一位。关于 preVerify 标签问题,QQ 空间的热修复方案是在类中的构造函数里插入代码。
业务 Bundle 的 Merge 相对简单一些。
patch 信息来自于 bundle 在下载的 tpatch 包中的内容,客户端的 merge 过程会根据 patch 信息,取到 source 组件,source dex 和 patch dex 进行 dex 合并,合成新的 dex,同时变化的资源覆盖老的资源最终形成新的组件。
AtlasDemo
项目运行
安装运行 Demo 配置如下:
app 的 build.gradle 文件中
version = getEnvValue("versionName","1.0.0");
AndroidMainfest.xml 文件中
android:versionName="1.0.0"
bundle 中的 build.gradle文件中
version = '1.0.0'
接着运行 shell 脚本 buildApk.sh
构建app,生成 apk 和 ap,同时把 ap 文件发布到本地仓库
此时点击动态部署:
点击 Go TO BUNDLEACTIVITY
显示的是业务bundle版本号
–
修改配置以及 项目代码(可选):
app 的 build.gradle 文件中
version = getEnvValue("versionName","1.0.1");
AndroidMainfest.xml 文件中
android:versionName="1.0.1"
bundle 中的 build.gradle文件中
version = '1.0.1'
再次执行 shell 脚本,构建app,生成 apk 和 ap,同时把 ap 文件发布到本地仓库 again
–
最后执行 buildTpatch.sh 脚本,重构 apk,生成 patch 包,上传 tpatch
点击动态部署
部署成功,重启生效。
其中 ./gradlew clean assembleDebug -DapVersion=1.0.0 -DversionName=1.0.1
版本号需要相对应
–
重启app之后,主 Bundle 版本号已经改变,动态部署成功。
点击 Go TO BUNDLEACTIVITY 进入业务 bundle 发现版本号也是发现了变化。
无需重新安装app就完成了业务代码的修改,这就是动态部署的魅力。