支付宝客户端架构解析:Android 容器化框架初探

简介: 1. 前言 由本章节开始,我们将从支付宝客户端的架构设计方案入手,细分拆解客户端在“容器化框架设计”、“网络优化”、“性能启动优化”、“自动化日志收集”、“RPC 组件设计”、“移动应用监控、诊断、定位”等具体实现,带领大家进一步了解支付宝在客户端架构上的迭代与优化历程。

1. 前言

由本章节开始,我们将从支付宝客户端的架构设计方案入手,细分拆解客户端在“容器化框架设计”、“网络优化”、“性能启动优化”、“自动化日志收集”、“RPC 组件设计”、“移动应用监控、诊断、定位”等具体实现,带领大家进一步了解支付宝在客户端架构上的迭代与优化历程。

本节将介绍支付宝 Android 容器化框架设计的基本思路。

1.1 开发背景

随着 Android 应用程序所能实现的功能越来越强大和复杂,随之而来的是:

xAndroid 程序的的代码和资源越来越多,APK 文件的 size 越来越大,Android 程序也越来越复杂;

x随着应用的迭代、项目的扩张,团队数量以及团队人数的同时增多,基于传统架构模式的并行开发也变得愈加困难。

此外,移动客户端通常需要面对动态化开发的挑战;Bug 紧急修复等运维需求;同时也有一些在线运营的需求,如动态下发广告,推送接入活动等。如果每次有运维、运营需求,都需要一次客户端发版,那将是传统的开发人员的梦魇。

Android 开发者们深切体会到一个稳健可靠、可扩展的、支持大规模并行开发的客户端开发框架对于平台级别的客户端 App 的重要性。事实上,客户端框架设计的健壮性和扩展性,在面对上述需求和解决困难上,往往能达到事半功倍的效果,尤其是 Android 客户端开发人员将深受其利。

那么,作为平台级别的 Android 客户端 App 究竟该如何的进行框架设计,才能满足千变万化的移动互联网时代的困难和需求?

1.2 平台级客户端框架面临的问题

No. 问题 描述
1 项目工程复杂度高,开发、编译、测试、集成都非常困难 支付宝 App 代码 200W 行+
2 平台级App的内部微应用(团队)非常多,并行开发要求高 内部多达几十个应用
3 APK size 庞大 支付宝 App60+M,导致在某些厂商 Rom 中安装不上
4 线上版本出现各种问题 如:发版后,UCSDK 被乌云平台暴露出安全漏洞
5 线上活动运营需求 春节红包扫福活动,预案要动态推送新 so 文件到客户端

我们可以归纳为:平台级客户端框架必须要解决的是模块化和 动态化这两大核心问题。

(本篇文章我们着重关注模块化相关内容,后续我们通过其他文章分析 动态化的能力。)

1.3 框架设计原则

为了解决上述模块化的问题,我们要遵循以下原则去设计客户端框架:

x根据基础技术层级、客户端的业务线等原则,对客户端应用程序进行模块化拆分。

x每一个模块由独立的小团队或者个人来进行开发、维护、测试、集成。

x模块与模块之间要做到彻底解耦,模块之间可以通过接口进行依赖。

x每一个模块可以进行热插拔,单个模块的插拔不影响整体的工程的编译运行。

2. Quinox 简介

Quinox 客户端框架是类 OSGi( like-as)框架的实现。Quinox 一词来源于著名的 OSGi 框架的实现 Equinox。

基于此框架的客户端 App,都是由一个个的积木搭建而成,这些积木被称之为:Bundle。

47f70622dfbb1e359d87e46c52a38e71ef90759e

bundle

3. Bundle 介绍3.1 什么是 Bundle

Bundle 是 OSGi 规范的模块化基本单位,与 Android 里的 android.os.Bundle 是两个完全不同的概念。
OSGi 里的 Bundle 指的是 Java 应用程序的基本单位,它是一个模块单元(Jar 格式),也是上文 Quinox 简介里提到的积木。
基于 Quinox 容器框架开发的应用程序也是由众多的 Bundle(APK 格式)构成。

本章节将从项目开发的三个不同的时期对 Bundle 的形态进行阐述:

时期 形态
开发期 Bundle 工程
构建期 Bundle 包
集成期 集成客户端的 Bundle 基线

3.2 Bundle 工程

常规的 Android 项目开发,代码工程通常有两种(两级)类型

工程类型 Library Application
工程输出 Aar Apk

基于 Quinox 容器框架开发的 Android 项目,代码工程则有三种(三级)类型

工程类型 Library Bundle(工程包) Application(测试包_安装包_Final APK)
工程输出 Aar Apk(.jar) Apk

关于 Bundle 工程,我们需要了解以下三点:

xBundle 工程跟常规的 Android Application 工程非常的类似:它内部也会有多个 Library(Android Module);它的输出形式也是 APK 格式。

x虽然 Bundle 包文件本质上是 APK 格式,但是该 APK 是无法运行的。同时,Bundle 工程被 deploy 到 mvn 仓库里时,它的后缀名是会改为.jar。

x基于 Quinox 容器的 Application 工程(可称之为 Portal 工程)则是将众多 Bundle(APK)合并成一个 APK(Final)的过程。这里是合并,而不是编译,所以生成最终 APK 的速度将会非常快,因为编译已经被分布式的进行在各个 Bundle 中了。基于 Quinox 容器开发的客户端程序,需使用 mPaaS 定制的构建工具(即打包插件)。

关于 Bundle 工程的结构图请参考:

6eef57f319b160d12fba6c6b29bec3714a4bca98

bundle_project 3.3 Bundle 包

如上所述,Bundle 工程的输出也是 APK 文件。

在 OSGi 规范中,Bundle 是有很多属性的。Bundle 工程输出的 APK 与常规的 APK 有一个不同点,mPaaS 插件会将 Bundle 的所有属性生成一个特殊的文件放在这个 APK 中,供容器去解读。

除了 APK 文件之外,Bundle 工程的构建结果还包含:

xAndroidMannifest.xml

xBundle 接口包(可以理解为一个 jar 包,它包含且暴露该 Bundle 提供的接口类)

xmapping.txt

Bundle 包文件,在构建完成之后,通常要 deploy 到本地/远程的 mvn 仓库中,以供其他 Bundle 工程引用,或是被 Portal 工程集成。

3.4 Bundle 基线

前面已经讲述过,构建 Final APK 其实主要就是将很多的 Budnle APK 合并成最终的 APK 的过程,而这些众多的 Bundle APK 们都存放于 mvn 仓库中。

因此我们将这些 Bundle 的 GAV(GroupId,ArtifactId,Vesion)的集合,称之为基线。

当某一个团队/个人开完一个 Bundle 工程的新功能,并经过测试达到可发布状态,就可以更新基线里的版本号。我们将这个过程称之为进基线。我们认为:基线里打出来的 APK 是稳定可运行的;没有稳定 Bundle 工程包不应该进基线。

Bundle 基线机制可以很好的隔离了模块之间的相互影响,保障了不同团队间开发环境的和谐与稳定,达到了我们之前的设计的初衷,因此可以很好的支持多团队并行开发。

3.5 Bundle 包属性及配置办法

关于 Bundle 属性,我们可以参考 OSGi 的 Bundle 属性。Quinox 容器框下定义的 Bundle 属性要简单的多。

下表将列举 Bundle 的所有属性以及配置方法:

名称 说明 配置办法
Bundle-Name Bundle 的名称,作为 key 值存在。同一个客户端 apk 中,不允许同名的 Bundle 存在 由 mPaaS 插件根据 Bundle 工程的 GAV 的 GroupId、ArtifactId ,以一定的规则生成而来。
Bundle-Version Bundle 的版本号,各个 Bundle 的接口包必须做到 API 版本向下兼容。 由 mPaaS 插件根据 Bundle 工程的 GAV 中的 Version 生成而来
Init-Level 已废弃 配置为 1 即可
Package-Name 已废弃 配置为 '' 即可
Component-Name Bundle 中声明的 Android Component。它跟 Export-Pacakges 属性一样,是 Bundle 的入口类。 由 mPaaS 插件根据 AndroidManifest.xml 文件中定义的 Activity,Service,BroadcastReceiver,ContentProvider 等生成
Package-Id Bundle 工程的资源的 packageid,具体技术细节请参考4.2章节 必须由开发同学在 Bundle 工程中设置属性 packageId,其值的设置区间为【27, 127】,如果没有资源,则设置为 127,如果 Bundle 为 Bundle 依赖关系树上根节点的 Bundle,则设置为27。
Contains-Dex 此 Bundle 中是否包含代码(classes.dex) 由 mPaaS 插件根据 Bundle 文件中是否包含 classes.dex 节点判断得来。备注:静态链接的 Bundle 由于 classes.dex merge 到了主 apk 中,所以该属性会被修正为 false
Contains-Res 此 Bundle 中是否包含资源(resources.arsc) 由 mPaaS 插件根据 Bundle 文件中是否包含 resources.arsc 文件判断得来。
Native-Library 此 Bundle 中是否包含 native so(lib_xxx_libxxx.so) 由 mPaaS 插件根据 Bundle 文件中 native so 文件判断得来。备注:在构建最终 apk 时,Bundle 中所有的 so 文件在构建 Final apk 时,会 merge 到 Final apk 中,所以该属性会被修正为 null
Required-Bundle 此 Bundle 依赖的 Bundle 列表:为 Budnle-Name@Bundle-Version 的格式。 由 mPaaS 插件根据Bundle项目工程依赖其他Bundle接口包,来生成此属性。
Export-Pacakges Bundle 导出包(请参考 OSGi 的导出包概念)。Quinox 容器将根据导出包,从对应的 Bundle 中加载类。 必须由开发同学在 Bundle 工程中设置属性 exportPackages。例如:某个非静态连接的 Bundle 提供了类: com.alipay.android.phone.framework.api.A作为接口给其他 Bundle 使用,则须将com.alipay.android.phone.framework.api配置为导出包。(反射被使用的类也应纳入导出包)。有多个导出包的用','隔开。为了性能考虑,导出包不应设置太多,或者范围太广。

4. 资源管理

4.1 资源管理器

作为 Android 开发人员,我们知道通过 android.content.res.Resources 对象可以获取字符串、布局、图片、动画等资源。

在 Quinox 容器化的框架内,原生的资源管理肯定无法实现多 Bundle 的资源管理,这时候,我们就构建了 Bundle 资源管理器,来专门处理各个 Bundle 的资源的加载、调用等工作,替代了 Android 原生的资源管理逻辑。

但是,由于所有 Bundle 包都是独立编译的,它们中的资源极可能存在着相同的资源 id。因此,当存在相同资源时,就可能存在冲突,那么如何解决资源 id 的冲突呢?

4.2 资源 id

作为 Android 程序员,我们都知道资源 id 是一个 int 值,它包含4个 byte。它是在构建 APK 工程时,由 aapt 工具生成,定义在 R 文件中。

示例代码:


public final class R {
public static final class drawable {
public static int xxx_bg=0x1e020000;
}
public static final class id {
public static int xxx_id=0x1e050001;
}
public static final class layout {
public static int xxx_layout=0x1e030000;
}
public static final class string {
public static int xxx_str=0x1e040001;
}
}

这四个 byte 含义如下:

x第一个字节为:pacakgeId。

x第二个字节为:typeId。它表示的是不同的资源类型,如字符串,布局,图片,动画等。

x第三第四两个字节合起来为:资源名称的 id

如下图所示:

fbad88511d8613cd8ba7e66711a21d0b7e0a0546

packageid

到这里,很多读者应该已经理解到了,Quinox 容器框架关于资源 id 冲突的解决方案是,让 mPaaS 打包插件使用改造过 aapt 工具,对每一个 Bundle 工程都指定不同 packageId,进行分区隔离,从而确保不同的 Bundle 之间资源 id 是不会重复的。这也是为什么 Bundle 工程里需要指定 packageId 的缘故。

5.容器化

关于 Quinox 容器化这里,由于目前为止,Quinox 暂未开源,所以本章节内,我们暂时不涉及到源码分析。

上面我们聊了很多关于 Bundle 的话题,那么整个容器化的核心,也是如何管理各个 Bundle。这时就要引出我们的容器管理器了,容器管理器的主要工作就是协调各个 Bundle,对各种信息进行增删改查。

在应用启动后,我们的容器管理器会读取配置信息,生成各 Bundle 的信息实例。

5.1 容器管理器:增、删

Quinox 容器框架的目标是解决 Android 客户端 App 模块化和动态化这两大核心问题。增、删这两项能力,更多的是用来实现动态化能力的,方便容器对各个 Bundle 进行动态添加、删除。由于本文着重描写模块化的能力,所以这部分,我们后续单开专题来分析容器的动态化能力。

5.2 容器管理器:改

关于容器管理器的改的能力,Quinox 主要是利用改的能力,做一些启动性能的优化,以及机型适配上的工作,这里涉及源码较多,我们不做过多分析。

5.3 容器管理器:查

容器管理器使用最频繁的功能接口应该是查询接口:

x根据 BundleName 进行查询(还记得 Bundle 的 Require-Bundle 属性么?)

x根据 packageId 进行查询(非27的 Bundle)

x根据 Android Component 类名进行查询(还记得 Bundle 的 Component-Name 属性么?)

通过管理器的查询接口,我们进行各个 Bundle 之间的协调、通信,完成容器化的功能。

5.4 组件的启动

除了容器管理器,还有一个重要的点就是组件的启动器。Quinox 容器定制类原生 Android Activity 的启动流程,从而自主管理 Activity 的创建以及生命周期。

同时,由于 Activity 是我们自主的启动器进行的创建,我们还可以对 Activity 进行一些定制化的改造,方便其更好的适配容器这套体系。比如说给 Activity 赋予我们自定义的资源管理器,管理 Activity 堆栈并对外提供接口,对 Activity 各生命周期做一些切面工作等等。

定制化的组件启动器,还有一个好处就是可以做到 Activity 的动态运行。所谓动态运行,是指运行出厂未注册在 Manifest 中的 Activity。这块功能,更多是为了支持容器动态化的能力。

由于 Activity 动态运行的实现逻辑涉及较多的核心技术点,所以我们暂时不进行具体实现的剖析。

6. 小结

通过本节内容,我们已经初步了解了 mPaaS 在安卓端容器化框架的设计思路和相应模块。由于篇幅限制,很多技术要点我们无法一一展开。


原文发布时间为:2018-11-26

本文作者:刺胃、辛文

本文来自云栖社区合作伙伴“安卓巴士Android开发者门户”,了解相关信息可以关注“安卓巴士Android开发者门户”。

相关文章
|
Kubernetes Cloud Native 微服务
探索云原生技术:容器化与微服务架构的融合之旅
本文将带领读者深入了解云原生技术的核心概念,特别是容器化和微服务架构如何相辅相成,共同构建现代软件系统。我们将通过实际代码示例,探讨如何在云平台上部署和管理微服务,以及如何使用容器编排工具来自动化这一过程。文章旨在为开发者和技术决策者提供实用的指导,帮助他们在云原生时代中更好地设计、部署和维护应用。
703 59
|
运维 Kubernetes Cloud Native
云原生技术:容器化与微服务架构的完美结合
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术以其灵活性和高效性成为企业的新宠。本文将深入探讨云原生的核心概念,包括容器化技术和微服务架构,以及它们如何共同推动现代应用的发展。我们将通过实际代码示例,展示如何在Kubernetes集群上部署一个简单的微服务,揭示云原生技术的强大能力和未来潜力。
|
Kubernetes Cloud Native 持续交付
容器化、Kubernetes与微服务架构的融合
容器化、Kubernetes与微服务架构的融合
739 82
|
监控 Kubernetes Cloud Native
基于阿里云容器服务Kubernetes版(ACK)的微服务架构设计与实践
本文介绍了如何基于阿里云容器服务Kubernetes版(ACK)设计和实现微服务架构。首先概述了微服务架构的优势与挑战,如模块化、可扩展性及技术多样性。接着详细描述了ACK的核心功能,包括集群管理、应用管理、网络与安全、监控与日志等。在设计基于ACK的微服务架构时,需考虑服务拆分、通信、发现与负载均衡、配置管理、监控与日志以及CI/CD等方面。通过一个电商应用案例,展示了用户服务、商品服务、订单服务和支付服务的具体部署步骤。最后总结了ACK为微服务架构提供的强大支持,帮助应对各种挑战,构建高效可靠的云原生应用。
|
前端开发 API 开发工具
一年撸完百万行代码,企业微信的全新鸿蒙NEXT客户端架构演进之路
本文将要分享的是企业微信的鸿蒙Next客户端架构的演进过程,面对代码移植和API不稳定的挑战,提出了DataList框架解决方案。通过结构化、动态和认知三重熵减机制,将业务逻辑与UI解耦,实现数据驱动开发。采用MVDM分层架构(业务实体层、逻辑层、UI数据层、表示层),屏蔽系统差异,确保业务代码稳定。
666 0
|
Kubernetes Linux 虚拟化
入门级容器技术解析:Docker和K8s的区别与关系
本文介绍了容器技术的发展历程及其重要组成部分Docker和Kubernetes。从传统物理机到虚拟机,再到容器化,每一步都旨在更高效地利用服务器资源并简化应用部署。容器技术通过隔离环境、减少依赖冲突和提高可移植性,解决了传统部署方式中的诸多问题。Docker作为容器化平台,专注于创建和管理容器;而Kubernetes则是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。两者相辅相成,共同推动了现代云原生应用的快速发展。
4579 11
|
Kubernetes 安全 数据安全/隐私保护
云卓越架构:容器安全最佳实践
本次分享由阿里云智能集团解决方案架构师张玉峰主讲,主题为“云卓越架构:容器安全最佳实践”。内容涵盖容器安全的挑战、云原生容器安全架构及典型场景。首先分析了容器安全面临的问题,如镜像漏洞和权限管理。接着介绍了容器安全架构的五个维度:身份权限管理、配置安全检查、运行时防护、镜像安全检测及发布的安全管控。最后通过具体场景展示了容器身份与权限管理、密钥管理、运行时防入侵等最佳实践,强调了安全左移的重要性,确保从开发到运行的全生命周期安全覆盖。
|
监控 Cloud Native Java
基于阿里云容器服务(ACK)的微服务架构设计与实践
本文介绍如何利用阿里云容器服务Kubernetes版(ACK)构建高可用、可扩展的微服务架构。通过电商平台案例,展示基于Java(Spring Boot)、Docker、Nacos等技术的开发、容器化、部署流程,涵盖服务注册、API网关、监控日志及性能优化实践,帮助企业实现云原生转型。
|
Kubernetes Cloud Native Docker
云原生之旅:从传统架构到容器化服务的演变
随着技术的快速发展,云计算已经从简单的虚拟化服务演进到了更加灵活和高效的云原生时代。本文将带你了解云原生的概念、优势以及如何通过容器化技术实现应用的快速部署和扩展。我们将以一个简单的Python Web应用为例,展示如何利用Docker容器进行打包和部署,进而探索Kubernetes如何管理这些容器,确保服务的高可用性和弹性伸缩。
|
负载均衡 网络协议 算法
Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式
本文探讨了Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式,以及软件负载均衡器、云服务负载均衡、容器编排工具等实现手段,强调两者结合的重要性及面临挑战的应对措施。
609 4

推荐镜像

更多
  • DNS