模块化 - 一个成功的架构
现在我们已经确定模块化是一个非常好的事情,模块化应用程序应该如何?如何连接不同的模块?这如何看待真正的应用程序?
第二部分将探讨一种简单但非常有效的模块化应用程序方法。它将深入介绍不同类型的模块,并展示这种方法的好处。
放弃
这绝不是模块化应用程序的唯一方法,但它确实提供了一些我们稍后会涉及的关键优势。
应用结构
让我们从查看您正在处理的应用程序开始:
- 它是否包含带有多个标签/可点击元素的主屏幕?
- 当用户点击这些元素时会发生什么?
机会很高,将打开应用程序的新全屏部分,通常由几个子屏幕组成,以执行特定操作。
比如看看gmail:
模块化架构简化后,它包含一个带有应用程序抽屉的主屏幕(收件箱),一个撰写按钮和收件箱中的电子邮件项目。单击其中一个元素可引导您进入新的全屏“功能”:
- 点击电子邮件 - >阅读电子邮件功能(一个屏幕)
- 点击撰写 - >编写电子邮件功能(多个屏幕)
- 点击设置(在抽屉里) - >设置(几个屏幕)
高度简化的应用程序只是一个(全屏)屏幕的树,其中多个屏幕通常形成用户流。
让我们调用所有这些“用户流”功能。
现在让我们考虑Android操作系统的设计方式:多个应用程序可以通过意图相互交互。这实际上非常酷,因为任何应用都可以请求执行操作(例如拍照),而无需知道谁将处理该请求以及如何处理该请求。
Android系统只是通过隐式意图系统将多个应用程序链接在一起。
如果我们利用这些观察结果并将我们的应用程序分成几个完全独立的功能模块,该怎么办?使用简单的“startActivityForResult”合约将每个功能分离的位置?
模块化架构
在将您的应用分成多个功能时,所有这些功能可能都取决于一些常见的业务逻辑或UI组件。因此,我们需要引入第三级“库模块”。
将所有这些结合在一起产生:
模块化架构,带有一个App模块,多个功能模块和多个库模块这种架构基本上将应用程序拆分为三个级别的模块:
- App:链接功能模块(通常只有一个)
- 功能:自包含,全屏UI级别功能,包括Espresso测试。每个功能至少包含一个活动和可选的导航图。功能模块永远不会直接相互依赖。
- 库:跨多个功能共享的功能。不同的库可以相互依赖
让我们深入研究这三个层面。
功能模块
可能最重要的模块是功能模块。它们具有以下特征:
- 一个android-library模块
- 具有(可选)导航图的单个活动(
允许多个活动) - 响应隐式意图并传回结果
- 从不依赖于其他功能或应用程序
- 依赖于几个库模块
功能模块与应用中的全屏,连贯的面向用户功能相对应:例如用户登录,应用设置,图片裁剪,......
导航
第一个关键优势是功能模块使应用程序内的导航变得更加容易。这是因为他们将导航问题分成了更小的部分:
- 功能中的导航 - >由功能本身处理
- 功能之间的导航 - >由app模块处理
因此,不需要非常大而复杂的导航控制器!功能简单地将应用程序拆分为逻辑,连贯的流程。
更重要的是,导航组件为每个功能提供了其UI流的清晰可视化表示。这样可以快速找出功能的作用。例如,游戏功能有什么作用?
在功能中导航最后不再猜测特定屏幕是如何命名的,只需跳转到正确的功能,查找屏幕,您就可以找到片段/视图,而无需猜测/记住他们的名字。
Scaling
其次,使这些独立的功能完全脱离其实现。因此通过设计消除不同功能团队之间的合并冲突!
尝试新技术也变得更加容易:您可以轻松地从单一功能中的新技术端到端获益。评估它是否对您的团队有益,如果选择不当,所有效果都包含在一个模块中!
如果您决定启动第二个应用程序(或SDK),您可以在新的应用程序模块中将现有功能与新功能一起打包。
测试
由于所有功能都可以使用意图直接启动,因此Espresso无需单步执行应用程序的其他部分即可获得要测试的功能。
这不仅使测试更简单,更快速,而且更少的步骤也使它们更可靠,并且测试不再因其他功能中的错误而中断!
图书馆模块
库提供共享管道,可在多个或所有功能中重复使用。他们的特点是:
- android库,纯Java或纯Kotlin模块
- 从不依赖于功能或应用程序
- 可以(但不必)依赖于其他库
因此,库可以非常多样化:例如UI组件,数据存储,网络通信,标准库,...
如果功能是应用程序的“垂直切片”,则库是“水平切片”,为其他几个模块提供功能。
应用模块
为了将应用程序发送给用户,必须将所有功能链接在一起:app模块。
在这样做时,app模块在功能之间协调导航。它使用功能切换来确定应该启用什么和不启用什么。
这些功能切换非常强大,因为通过在一个应用程序中运送同一功能的多个版本(例如旧版和重写版),应用程序模块允许逐步将重写的功能推广给用户。
if (isRewriteFeatureEnabled) {
startActivityForResult(Intent("rewritten_feature"))
} else {
startActivityForResult(Intent("feature"))
}
最后,启动多个应用程序并在它们之间共享功能就像创建新的应用程序模块一样简单。
包起来
重新安装,这个简单的三层应用程序,功能和库架构具有以下优点:
- 通过拆分功能内和跨功能导航来简化导航
- 可以轻松找到屏幕并了解功能(特别是在使用导航图时)
- 支持扩展团队:功能团队之间的合并冲突更少,因为功能已解耦
- 使测试自动化更容易:功能可以直接启动,无需首先通过应用程序到该功能
- 简化了新技术的实验:快速实现功能内的端到端优势+不良技术选择的低成本(与应用程序的其他部分隔离)
-
允许使用功能切换分阶段推出重写功能
image