关于Android组件化深度分析(七)携程+支付宝

后续等组件化更新完,更新:

    Shadow插件化
    ASM其他
    Systrace使用与解析
    字节码相关

 

一丶从携程APP看组件化架构实践

提到了使用数据总线或者 URL 总线实现页面和功能跳转。提到了定制 Gradle 插件来定制化打包
1.携程移动App架构优化之旅

『携程旅行App』作为携程超级App产品,是公司全品类旅行产品的核心售卖入口,过去两年为了更好支撑无线业务的快速发展,携程移动App在产品和技术架构方面也做了大量的优化。

产品

产品方面,携程App从原先的iPhone、iPad、Android Phone、Android Pad和Windows Phone共五个版本精简为Universial iOS和Universial Android两个版本,以便于集中研发和市场资源发布新产品。

无线后端服务架构

无线后端服务架构方面,原有的无线架构(图一)是所有无线服务耦合成一个统一的服务模块,囊括了所有提供给客户端访问的API。

在统一服务模块功能较少的时候,这套架构完全符合业务功能需求,但当功能迭代加快,问题不断出现:

    每个API都是DLL动态库形式(.net)实现,在某些公共逻辑接口发生变化时,不同时期上线的API可能使用了不同版本的逻辑接口定义,会导致运行时出现诡异结果或者进程Crash,影响稳定性;
    每次大版本发布上线,从测试到全面发布完毕,都需要全量回归测试过程,开发和测试进度严重耦合,影响发布效率;
    新增的低优先级API可能稳定性不好,出现问题时会影响到整个服务进程的稳定性。

除了这种完全耦合的弊端之外,还存在其他诸如缺少负载均衡、越少监控、缺少熔断等影响后端稳定性的问题。于是
开始尝试使用一种新的无线架构:Gateway(图二)。

携程基于Netflix的开源项目Zuul开发了无线Gateway(曾在2015年QCon上海会议中分享)。Gateway的职能是负责接收来自无线端的所有API请求,并将他们路由到正确的目标应用服务器,并且提供限流、隔离、熔断等功能,保证了无线服务的长期稳定运行,拥有的弹性容错机制也减少了日常运维工作。同时该Gateway提供了多维度的监控数据,并与报警系统对接,实时监控线上情况,达到运维自动化。

APP工程架构

App工程架构方面,原先的App开发还是单工程单构建的方式,各产品间的代码耦合严重,于是进行了App工程的解耦


解耦后各产品间代码完全独立,相互页面和功能的跳转走数据总线或者URL总线方式来实现,打包时的资源问题可以通过Run Script(iOS)和Gradle(Android)来定制化解决。

在App工程架构解耦后,紧接着是两个问题:

    开发框架是否满足产品开发需求?
    性能和质量是否达到用户体验要求?

为了解决第一个问题,我们梳理了现有的App功能,将通讯、定位、Hybrid框架、数据库、登录、分享、基础库等核心功能实现了SDK化,也提供给公司其他App直接使用;同时将App内部的公共业务组件例如地图、日历、城市、图片、通讯录等实现了统一。

要解决第二个问题『App性能和质量』,首先需要了解App性能的现状,即App端性能的采集。

常规做法有两种:

1.使用第三方性能采集SDK,例如OneAPM、听云等第三方工具;
2. 自建。携程为了完整掌握用户数据采用了自建的方式:App通过日志SDK采集日志,上传至日志服务端,日志消息经Kafka消息队列存入HDFS(RCFile格式)分布式文件存储系统,后期的数据查询均基于Hive系统来实现。

App端的性能数据会在多种纬度(操作系统、App版本、网络状况、位置)下采集网络(网络服务成功率、平均耗时、耗时分布等)、定位(经纬度成功率、城市定位成功率等)、启动时间、内存流量等各类指标。其中像网络服务性能是对于用户体验至关重要的端到端性能,也是优化的核心依据。

性能数据采集后需要采用简单直观的Portal进行展示,携程为无线业务开发了Web端和App端性能展示Portal,下图是网络性能的截屏,数据会每小时进行更新聚合并展示。

基于完备的性能采集数据,很多App性能便可以依据数据进行迭代优化,例如App网络服务方面,携程App采用了以下多种优化手段:

    使用TCP长连接实现网络服务,减少网络连接时间
    根据网络状况2G/3G/4G/WIFI进行调优参数
    根据连接/读/写不同阶段使用重试机制
    使用IP列表避免DNS解析失败或者劫持,无需进行DNS解析
    根据网络延迟选择服务端IP(使用Ping)
    使用ProtocolBuffer+Gzip减少Payload

经过这些优化手段,携程App的端到端网络服务成功率从最初很差的95.32%提升至99.87%,用户体验得到明显提升。

携程App的Hybrid框架也是经过多个版本的迭代,已经支持强大的插件功能,已经做到凡是可以用Native的组件通通使用Native组件来优化Hybrid业务的体验。携程Hybrid框架在设计之初即采用了离线包功能:Hybrid业务整体打包在App中,节省了用户打开页面时的资源加载时间;同时离线包支持查分增量更新,并通过7z压缩方式进一步降低了增量更新包的大小,相对zip压缩减少30%大小。

Native的插件化和HotFix

Native的插件化和HotFix方面,我们在iOS端使用开源的解决方案JSPatch,在Android端采用了自研的解决方案DynamicAPK,可以支持各组件的资源及代码的更新。DynamicAPK方案无需做任何activity/fragment/resource的proxy实现,使得原有的业务代码无需修改即可支持,迁移代码很小,同时可以提升App启动时间,详情请参考GitHub。

其他优化都是针对特定的业务场景展开,例如Android的海外地图没有好的解决方案,iOS的地图控件又存在人在国外看国外和人在国外看国内地图数据精度低的问题,携程基于Google地图开发了Hybrid版地图,用于在特定场景的业务需求;Hybrid网络性能优化,通过Hybrid接口发送Native网络服务,可以避免DNS劫持并且利用Native端的TCP长连接;海外网络性能优化,通过TCP海外加速产品实现链路优化;图片性能优化方面使用了WebP图片格式,可以降低30-40%图片大小,图片分片上传等。

移动端技术发展很快,携程也在积极尝试新技术,例如React Native(已在账户信息部分页面使用),新的网络协议
SPDYHTTP/2.0,Apple/Huawei/Samsung Watch App等都做了大量尝试,以期能够提升产品品质。
二丶从支付宝APP看组件化架构实践

介绍了基于 Bundle 和 mPaaS 打包插件的 Quinox 客户端框架,用于解决模块化和动态化两大问题;

提到了解决资源 id 重复的解法:改造 aapt,为每个 Bundle 指定不同 packageId;

你知道支付宝容器化架构是怎么搭建的吗?
2.1.前言

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

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

开发背景

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

    Android 程序的的代码和资源越来越多,APK 文件的 size 越来越大,Android 程序也越来越复杂;
    随着应用的迭代、项目的扩张,团队数量以及团队人数的同时增多,基于传统架构模式的并行开发也变得愈加困难。

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

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

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

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

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

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

    根据基础技术层级、客户端的业务线等原则,对客户端应用程序进行模块化拆分。
    每一个模块由独立的小团队或者个人来进行开发、维护、测试、集成。
    模块与模块之间要做到彻底解耦,模块之间可以通过接口进行依赖。
    每一个模块可以进行热插拔,单个模块的插拔不影响整体的工程的编译运行。

2.2.Quinox 简介

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

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

什么是Bundle

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

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

Bundle 工程

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

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

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

    Bundle 工程跟常规的 Android Application 工程非常的类似:它内部也会有多个 Library(Android Module);它的输出形式也是 APK 格式。
    虽然 Bundle 包文件本质上是 APK 格式,但是该 APK 是无法运行的。同时,Bundle 工程被 deploy 到 mvn 仓库里时,它的后缀名是会改为.jar。
    基于 Quinox 容器的 Application 工程(可称之为 Portal 工程)则是将众多 Bundle(APK)合并成一个APK(Final)的过程。这里是合并,而不是编译,所以生成最终 APK 的速度将会非常快,因为编译已经被分布式的进行在各个 Bundle 中了。基于 Quinox 容器开发的客户端程序,需使用 mPaaS 定制的构建工具(即打包插件)。

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

Bundle 包

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

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

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

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

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

Bundle 基线

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

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

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

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

Bundle 包属性及配置办法

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

下表将列举 Bundle 的所有属性以及配置方法:
2.4.资源管理
资源管理器

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

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

但是,由于所有 Bundle 包都是独立编译的,它们中的资源极可能存在着相同的资源 id。因此,当存在相同资源时,就可能存在冲突,那么如何解决资源 id 的冲突呢?
资源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 含义如下:

    第一个字节为:pacakgeId。
    第二个字节为:typeId。它表示的是不同的资源类型,如字符串,布局,图片,动画等。
    第三第四两个字节合起来为:资源名称的 id

如下图所示:

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

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

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

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

容器管理器:增、删

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

容器管理器:改

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

容器管理器:查

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

    根据 BundleName 进行查询(还记得 Bundle 的 Require-Bundle 属性么?)
    根据 packageId 进行查询(非27的 Bundle)
    根据 Android Component 类名进行查询(还记得 Bundle 的 Component-Name 属性么?)

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

组件的启动

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

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值