博客
关于我
「 51信用卡管家」Android 架构背后的演进与实践过程
阅读量:133 次
发布时间:2019-02-27

本文共 5135 字,大约阅读时间需要 17 分钟。

随着业务的快速扩张,原本小作坊式的单个工程的开发模式越来越难以满足实际需求。早在两年多以前,51信用卡管家就向下沉淀出了单独的公用基础库,以及一些通用的功能组件和个别独立的业务被拆分成 SDK,形成了一套中型项目、多人并行的开发模式,也为未来组件化拆分做了准备。

随着单应用内业务需求的增加、开发人员数量的增多以及基础库数量的膨胀,原有的开发模式逐渐暴露出一系列问题:

  • 耦合性严重:牵一发而动全身,难以独立维护和升级。
  • 需求测试影响面大:难以聚焦单一业务模块。
  • 主工程代码膨胀:编译耗时明显。
  • 依赖倒置问题:业务代码依赖 App 工程。
  • 界限模糊:基础库与业务库的界限不明确。
  • 依赖规则不清:业务模块间可以任意依赖调用。
  • 类库管理困难:类库数量越来越多,管理难度加大。

这些问题推动我们更进一步升级开发架构,选择了组件化或插件化的方向。

动态化探索

近两年,插件化框架层出不穷,各大厂都推出了自家开源的插件化框架。51信用卡选择了 Native 动态化与性能兼顾的插件化方案。动态性主要体现在两方面:一是动态热修复,二是动态下发业务插件。虽然插件化在性能上有所欠缺,但完全切换到 Native 的方式显得有些推倒重车。通过与业界的交流,对于动态下发业务插件的实际需求并不多,业务更新主要依然依靠 App 升级。技术方案没有最优解,选择适合自己的才是最好的。

插件化也存在一些弊端,如不可避免的 hook framework、修改 aapt、包装 Gradle Plugin、代理组件等不常规操作,日常维护成本较高,稳定性、兼容性、新版本适配等问题都需要考虑进去。经过内部讨论,公司决定不急于采用插件化,边走边看,先把业务组件拆分出来再说。

随着 Android P 的发布,限制 hook framework 后,插件化逐渐式微,维护成本越来越高,成本收益比逐渐降低,最终被弃坑不用。

动态化选择:拥抱大前端

除了插件化外,动态化方案近两年比较火的是以 ReactNative、Weex 为代表的大前端方向。51信用卡结合实际情况,最终选择拥抱大前端,Weex 作为动态化方案,以 Native 为主,Hybrid 离线化方案为辅。Weex 逐步迭代的架构开发模式。

Weex 的基础建设和前端同学的合作历经大半年时间,目前已经稳定应用于51信用卡各个 App 上,Weex 作为动态化页面的首选方案,已经完成了线上数百个页面的开发需求。配合离线化方案,各项性能指标也都达到了要求。

组件化拆分

代码解耦与代码隔离,最有效的方案是工程隔离。审视我们最初的方案,每个 SDK 对应单独的仓库,通过 Maven 依赖,通过工程分离隔离代码,这种方案没有问题,只不过需要进一步拓展,各个业务模块也需要独立主工程,拆分成独立的业务组件。

同时,划分清楚代码边界,控制依赖关系,梳理清楚层次结构,形成如图的架构。整体架构上提供三种容器:

  • Native 容器:采用组件化架构,用于原生业务开发。
  • Hybrid 容器:webview 加载 H5,配合离线化方案。
  • Weex 容器:用于编写常规页面,JS 动态转化成 Native 控件,天然具有动态化特性,配合离线化方案,达到页面秒开的效果。
  • 组件化实践

    Native 容器对应上图中各个层级的定义:

    • 工程 App:各个应用工程,目前已有十多个应用并行开发,51信用卡管家作为平台应用,其余应用为独立的业务工程应用。
    • 业务组件:独立的业务组件,一般为复合业务组件,API 与实现分离,相互之间依赖隔离。
    • 基础业务 SDK:独立的小单功能模块,提供基础功能,目前这一层级中还包含遗留未改造的部分业务组件。
    • 基础 Lib:业务无关的基础组件。

    组件化拆分的核心诉求是解耦合,提高内聚性。应该从诉求出发,在沿用当下开发模式的同时,逐渐进行组件化拆分。

    通过工程隔离进而进行组件化拆分后,基本可以解决上述提到的问题:

    • 高内聚,低耦合:代码边界清晰,代码变动影响面可以准确评估。
    • 提高开发效率:每个组件可以独立打包,单独调试,打包时间最多几十秒。
    • 降低 App 工程编译时间:理想情况下,App 工程仅仅是一个空壳,用于加载各个组件。
    • 依赖隔离:避免直接依赖,转为间接依赖,通过中间层进行集中式管理。

    组件化拆分的基础模块

    • 页面路由:采用分总分结构,在编译时生成汇总代码,调用时进行管理分发。

    • 模块间调用:采用 ServiceLoader 模式,组件工程目录如下:

      <组件工程目录>
      ├── api
      ├── imp
      └── app

      每个组件内一般声明三个 module:

      • api module:声明对外暴露的服务接口和对外暴露的实体类及 Event 事件。
      • imp module:依赖 api module,是 api module 的具体实现,不对外暴露细节,不允许其他组件对 imp module 进行直接依赖。
      • app module:是工程的壳,可以直接运行调试,通过 SDKTemplate 创建生成,包含各种运行时所需环境。

      业务组件之间依赖 api 库的服务接口,imp 库作为实现动态查找。版本发布时,同时发布 api 和 imp 两个库,并且保证 api 和 imp 具有相同版本号。

    • 消息总线:基于实现的跨三端(Native、Hybrid、Weex)事件管理分发组件。

    • 数据总线:支持按模块进行存取,每个业务组件都可以定义自有 tag,避免字段冲突问题。

    跨平台混合开发实践

    无论是早期的 PhoneGap、Cordova,还是近年来比较火的 ReactNative、Weex,到最近两年崛起的 Flutter,跨平台混合开发一直深受开发者青睐。其跨平台和动态化是原生开发所不具备的特性。

    Hybrid 容器实践

    Native 和 H5 混合开发一般是比较常见的混合开发模式,H5 开发效率高、迭代快速、不依赖 App 发版。51信用卡众多 App 产品中,有很多页面都是用 H5 来开发,嵌入原生 App 中使用 webview 进行加载显示。

    早期 H5 容器在各个 App 中分别独立实现,没有统一的架构和规范,导致对 H5 的支持效率较低,PG 方法(来源于 PhoneGap)的开发、测试和维护都相当混乱,重复性工作太多。Native 层提供一套通用性强、功能丰富、稳定性高的 H5 容器对业务的高速发展至关重要。

    插件管理

    由于 H5 不具备直接调用原生方法,所以原生壳要提供一套通用的通信方式,一般为 JsBridge。在 Android 端,实现 JsBridge 通信的通道一般有以下几种:

    • shouldOverrideUrlLoading
    • addJavascriptInterface
    • onJsPrompt/onJsAlert

    插件管理也是采用分总分结构,在各个业务组件中分别注册,编译是通过 APT 生成汇总代码,运行时进行插件汇总,最后调用通过 PluginManager 查找分发逻辑。

    插件注册代码如下:

    @JsPlugin(name = TestPlugin.PLUGIN_NAME, loadOnInit = false, version = 1)
    public class TestPlugin extends EnNiuJsPlugin {
    public static final String PLUGIN_NAME = "TestPlugin";
    @Override
    public String getPluginName() {
    return PLUGIN_NAME;
    }
    ...
    @Override
    public boolean onExecute(String args) {
    doSomething();
    callbackContext.callback(...);
    return true;
    }
    }

    插件的生命周期如下:

    • H5 容器和插件具有 Activity 生命周期感知能力,插件的生命周期如图所示。

    离线加载

    Hybrid 混合开发的一大劣势就是性能比较差,打开页面较慢,特别是在弱网情况下。由于 51信用卡业务大部分都是静态资源请求,参考业界做法,我们实现了动态下发离线包的方式来提升 H5 页面打开速度。

    Weex 容器实践

    在 Hybrid 已有配套基础上,51信用卡选择了 Weex 作为跨平台方案,经过一年的踩坑填坑过程,目前已经有 20+ 个项目、数百个 Weex 页面在线上稳定运行。Weex 方案趋于成熟,已经作为 51信用卡端内首选业务方案。

    共享插件

    由于 Hybrid 良好的面向接口编程特性,在进行 Weex 基础建设过程中,很方便地将已有的插件集成进来,并且共享已沉淀的配套设施。

    public class ENBridgeModule extends WXModule {
    @JSMethod
    public synchronized void send(String method, String args, JSCallback jsCallback) {
    ...
    weexWebView = weexEngine.getWeexVirtualWebView();
    EnNiuJsBridge enNiuJsBridge = weexWebView.getEnNiuJsBridge();
    enNiuJsBridge.notify(pg);
    }
    }

    注册 Weex 的 Module,并且每个 Weex Engine 中会新建出一个虚拟 webview,用于桥接 JsBridge 进而调用 PluginManager 进行插件逻辑分发。

    工程化实践

    工程化本质上是为了提高研发效率。51信用卡自研的大风车管理平台,用于 App 管理、持续集成、类库管理、发版管理等,围绕客户端研发上下游流程,建立统一的管理入口。

    类库管理

    51信用卡目前有 100 多个 Android 类库,每个类库对应一个独立的 Gitlab 仓库。过多的独立组件及独立仓库,管理起来有些麻烦。依托于大风车平台,所有类库的名称、最新版本及标签类型都会展示在列表页,标签类型对应组件化架构的层次结构,包括:基础组件、单业务功能组件、多业务功能组件。

    版本管理

    类库之间是仓库隔离,所以它们的依赖关系是 Maven 依赖,所有类库的 aar 包都需要发布到内部 Maven 服务器上,上传工作由 PublishMavenPlugin 完成。

    依赖管理

    • 依赖传递:App 工程下采用 compile 依赖,compile 会解析类库 Maven 包中的 pom 文件,进而间接依赖 pom 文件中声明的其他类库。
    • 多 module 发布:在多业务组件库工程中会有多个 module,一个 api module,一个 imp module,在使用 DemoApp 编译调试时采用源码依赖,imp module 依赖 api module,App 依赖 imp module。

    一键创建项目

    为了提高效率,我们创建了组件库的模板工程,只需要 clone 下来模板仓库,然后修改一些代码即可开发需求代码。新建类库时,填入必要的参数,一键就可以创建出可用的类库项目。

    总结

    好的架构不是设计出来的,而是演进出来的。本文简单阐述了 51信用卡 Android 架构演进的一些实践经验。我们坚信技术方案没有最优解,重要的是要选择适合自己的。脱离所处环境和问题本身谈技术方案,都将不能得到适合自身的开发架构。同时,我们也应当吸取和借鉴业界优秀的架构和设计理念,并将其根据自身适用场景加以改造,在理论和实践中逐渐交替探索演进。

    当然,我们目前所使用的架构依然存在一些问题,比如组件拆分不完全、主工程业务仍然很多、CodeReview 机制不健全、代码扫描不够严格、一些组件库没有严格按照 api 工程来改造、一些老的组件依然没有 api module 等等问题。我们也应该看到,正是因为这些实际的问题在推动我们进行技术改造,架构升级。同时,我们也要审视行业内大的方向,紧跟技术趋势,主动拥抱变化,毕竟技术世界唯一不变的,便是变化。

    转载地址:http://pezb.baihongyu.com/

    你可能感兴趣的文章
    nodejs连接mysql
    查看>>
    NodeJs连接Oracle数据库
    查看>>
    nodejs配置express服务器,运行自动打开浏览器
    查看>>
    NodeMCU教程 http请求获取Json中文乱码解决方案
    查看>>
    Nodemon 深入解析与使用
    查看>>
    NodeSession:高效且灵活的Node.js会话管理工具
    查看>>
    node~ http缓存
    查看>>
    node不是内部命令时配置node环境变量
    查看>>
    node中fs模块之文件操作
    查看>>
    Node中同步与异步的方式读取文件
    查看>>
    node中的get请求和post请求的不同操作【node学习第五篇】
    查看>>
    Node中的Http模块和Url模块的使用
    查看>>
    Node中自启动工具supervisor的使用
    查看>>
    Node入门之创建第一个HelloNode
    查看>>
    node全局对象 文件系统
    查看>>
    Node出错导致运行崩溃的解决方案
    查看>>
    Node响应中文时解决乱码问题
    查看>>
    node基础(二)_模块以及处理乱码问题
    查看>>
    node安装卸载linux,Linux运维知识之linux 卸载安装node npm
    查看>>
    node安装及配置之windows版
    查看>>