Atlas框架浅析

Atlas框架浅析

简介

Atlas由来

Atlas 是希腊神话中擎天的巨人。 (寓为脚踏底层框架,为应用撑起一片天)

2013年手淘人员倍增,整个客户端架构重构,形成了今天的 Atlas

作用

  • 工程期,实现工程独立开发,调试的功能,工程模块独立。
  • 运行期,实现完整的组件生命周期的映射,类隔离等机制。
  • 运维期,提供快速增量的更新修复能力,快速升级。

关于组件化

上图是 Google 的 模块化手机,与该框架在许多地方有异曲同工之妙。

Atlas 框架支持大量丰富的业务接入,同时业务之间又能够保持相对的独立性,耦合性低,各自可以灵活的升级维护。

一些底层业务,例如缓冲框架,图片加载框架,网络加载框架等中间件代码又能够全面复用。

上图中,上层是许多独立业务,组件 Bundle 之间又可以通过路由调用其他业务。下层是底层共享框架,由容器统一管理。

框架原理

容器框架

该框架自下而上主要分为5层

  1. Hack 层:一些系统的注入以及工具类的初始化校验等,并在容器启动时进行全局性的兼容校验。
  2. Bundle Framework 层:负责组件的安装、更新、操作以及管理 Bundle 的生命周期。
  3. Runtime 层:主要是版本、清单和代理管理。
  4. Business 层:向业务方暴露一些接口。
  5. 应用接入层:完成 Atlas 的初始化功能,业务代码。

技术细节

Bundle 生命周期

  1. startInstall 开始加载
  2. Installed 加载完毕 注入资源路径,开始创建 class loader
  3. resolved 解析完毕 校验组件的安全性
  4. active 运行组件 Bundle
  5. 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。

动态部署

技术分析

之前的组件化只是解决了耦合性的问题,动态部署实现了随时发包的功能。

  1. 构建 一般的 Apk 更新产物是 apk,动态部署的构建产物是一个后缀为 tpatch 格式的文件。
  2. Merge 下载到 tpatch 文件之后,在后台完成 Merge 到安装的过程,整个过程对用户是透明的。
  3. 生效 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 文件发布到本地仓库

此时点击动态部署:

Demo图片

点击 Go TO BUNDLEACTIVITY

Demo图片

显示的是业务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

点击动态部署

Demo图片

部署成功,重启生效。

其中 ./gradlew clean assembleDebug -DapVersion=1.0.0 -DversionName=1.0.1 版本号需要相对应

重启app之后,主 Bundle 版本号已经改变,动态部署成功。

Demo图片

点击 Go TO BUNDLEACTIVITY 进入业务 bundle 发现版本号也是发现了变化。

Demo图片

无需重新安装app就完成了业务代码的修改,这就是动态部署的魅力。