Android应用模块化开发指南

news2024/11/26 16:25:47

Android应用模块化开发指南

包含多个Gradle模块的项目称为多模块项目。本文包含多模块应用项目的最佳实践和推荐模式。

代码规模变大带来的问题

可扩缩性、可读性和整体代码质量会随着时间的推移而降低,代码维护者未采取积极的措施来保持易于维护的结构。模块化是一种行之有效的代码库构建方法,可帮助改善可维护性并避免此类问题。

什么是模块化?

将代码库组织为多个松散耦合的独立部分的做法。每个部分都是一个模块。每个模块都是独立的,并且都有明确的用途。通过将问题划分为更小、更易于解决的子问题,您可以降低设计和维护大型系统的复杂性。

示例多模块代码库的依赖关系图

模块化优势

模块化具有众多优势,但核心都是提高代码库的可维护性和整体质量。

优势摘要
可重用性模块化可支持在同一基础上共享代码并构建多个应用。模块实际上就是构建块。应用应为其各项功能的总和,而这些功能按单独的模块进行划分。特定模块提供的功能不一定会在特定应用中启用。例如,:feature:news 可以是完整版本变种和 Wear 应用的一部分,但不是演示版本变种的一部分。
严格控制可见性借助模块,您可以轻松控制向代码库的其他部分公开哪些内容。您可以将除公共接口以外的所有内容标记为 internal 或 private,以防止在模块外部使用这些内容。
自定义分发Play Feature Delivery 使用了 app bundle 的多种高级功能,让您可以按条件或按需分发应用的某些功能。
可伸缩性在紧密耦合的代码库中,单项更改可能会触发看似不相关的代码部分的级联更改。适当模块化的项目将遵循分离关注点原则,因此可限制耦合。这样一来,贡献者将拥有更大的自主权。
所有权除了实现自主权外,模块还可用于实现问责制原则。模块可以由专门的所有者来负责维护代码、修复 bug、添加测试以及查看更改。
封装封装意味着代码的每个部分都应尽可能少地了解其他部分。隔离的代码更易于阅读和理解。
可测试性可测试性是指可以轻松测试代码。可测试代码是指可以轻松单独测试组件的代码。
构建时间某些 Gradle 功能(如增量构建、构建缓存或并行构建)可以利用模块化来提高 build 性能。

常见误区

代码库的粒度是指其模块化程度。粒度不是越大越好,也不是越小越好。过于精细化的设计会增加开销负担,而过于粗略又会降低模块化的优势。

应用示例

Now InAndroid

模块化遵循的原则—高内聚低耦合

表征模块化代码库的一种方式是使用耦合内聚属性。耦合用于衡量模块相互依赖的程度。

  • • 低耦合是指模块应尽可能相互独立,这样一来,对一个模块所做的更改将对其他模块产生零影响或极小的影响。模块相互之间不应了解对方的内部运行原理。
  • • 高内聚是指多个模块的代码集合应当像一个系统一样运行。它们应具有明确定义的职责,并始终位于特定领域知识范围以内。假设有一个电子书应用示例。在同一个模块中融合图书相关代码和付款相关代码是不合适的,因为图书和付款是两个不同的功能领域。

模块类型

在遵循应用架构指南的前提下,应用中应该引入的一些通用模块类型。

数据模块

数据模块通常包含存储库、数据源和模型类。包含三职责:

  1. 1. 封装特定领域的所有数据和业务逻辑:每个数据模块都应负责处理表示特定领域的数据。它可以处理许多类型的数据,只要这些数据相关即可。
  2. 2. 将存储库公开为外部 API:数据模块的公共 API 应为存储库,因为它们负责向应用的其余部分公开数据。
  3. 3. 对外部隐藏所有实现细节和数据源:只能由同一模块中的存储库访问数据源。它们对外部始终处于隐藏状态。您可以使用 Kotlin 的 private 或 internal 可见性关键字来实现此操作。

示例数据模块及其内容

功能模块

功能是应用功能的独立部分,通常对应于一个屏幕或一系列密切相关的屏幕,例如注册或结账流程。如果您的应用具有底部导航栏,则每个目标位置都可能是一项功能。

此应用的每个标签页均可定义为功能

功能与应用中的屏幕或目标位置相关联。因此,它们可能具有相关联的界面和 ViewModel,用于处理其逻辑和状态。一项功能并不一定仅限于单一视图或导航目标位置。功能模块依赖于数据模块

功能模块及其内容示例

应用模块

应用模块是应用的入口点。它们依赖于功能模块,并且通常提供根导航。由于支持 build 变体,因此单个应用模块可以编译为许多不同的二进制文件。

“演示版”和“完整版”产品变种模块依赖关系图

如果您的应用以多种设备类型(例如汽车、穿戴式设备或电视)为目标平台,您可以考虑为每个设备类型定义一个应用模块。这有助于分离特定于平台的依赖项。

Wear 应用依赖关系图

通用模块

通用模块(也称为核心模块)包含其他模块经常使用的代码。它们可减少冗余,并且不代表应用架构中的任何特定层。下面列出了通用模块的一些示例:

  • • 界面模块:如果您使用自定义界面元素或在应用中精心设计品牌元素,则应考虑将 widget 集合封装到一个模块中,以便重复使用所有功能。这有助于确保您的界面在不同功能之间保持一致。例如,如果您采用集中式主题,则在更换品牌名称时可以避免痛苦的重构过程。
  • • 分析模块:跟踪通常取决于业务需求,而几乎不用考虑软件架构。分析跟踪器经常应用于许多不相关的组件。如果是这种情况,最好创建一个专用分析模块。
  • • 网络模块:当许多模块需要网络连接时,您可以考虑创建一个专用于提供 http 客户端的模块。当客户端需要自定义配置,该模块尤为实用。
  • • 实用程序模块:实用程序(也称为辅助程序)通常是在应用中重复使用的小段代码。实用程序的示例包括测试辅助程序、货币格式设置函数、电子邮件验证程序和自定义运算符。

模块间通信

很少有模块是完全隔离的。模块之间通常相互依赖并相互通信。即使多个模块协同运行并频繁交换信息,也务必要保持低耦合。有时,与架构约束一样,两个模块之间进行直接通信是不可取的方式。此外,在使用循环依赖项等情况下,两个模块直接进行通信也是不可行的。

使用循环依赖项时,在模块之间进行直接双向通信是不可行的。需要通过一个中间模块来协调两个其他独立模块之间的数据流

使用循环依赖项时,在模块之间进行直接双向通信是不可行的。需要通过一个中间模块来协调两个其他独立模块之间的数据流

为了克服此问题,您可以在两个模块之间使用第三个中间模块。中间模块可以监听来自这两个模块的消息,并根据需要转发消息。在我们的示例应用中,即使事件源自属于不同功能的单独屏幕,结账屏幕也需要知道要购买哪本图书。在这种情况下,拥有导航图的模块将充当中间模块(通常是应用模块)。在此示例中,我们使用导航组件将数据从主屏幕功能传递至结账功能。

navController.navigate("checkout/$bookId")

结账目标会接收图书 ID 作为参数,用于获取图书的相关信息。您可以使用已保存的状态句柄来检索目标功能的 ViewModel 内的导航参数。

class CheckoutViewModel(savedStateHandle: SavedStateHandle, …) : ViewModel() {

   val uiState: StateFlow<CheckoutUiState> =
      savedStateHandle.getStateFlow<String>("bookId", "").map { bookId ->
          // produce UI state calling bookRepository.getBook(bookId)
      }
      …
}

您通常不应该将对象作为导航参数传递,而应该使用ID。

在以下示例中,两个功能模块均依赖于相同的数据模块。这样可以尽可能减少中间模块需要转发的数据量,并在模块之间保持低耦合。模块应传递基元 ID 并从共享数据模块加载资源,而不是传递对象。

两个功能模块依赖于一个共享数据模块

常见最佳实践

没有最好的架构,但通常建议遵循代码的可读性、可维护性和可测试性。

  • • 版本目录是 Gradle 在同步期间生成的类型安全的依赖项列表。您可以在其中集中声明所有依赖项,并且可供项目中的所有模块使用。
  • • 使用惯例插件在模块之间共享 build 逻辑。

保持配置一致

每个模块都会引入配置开销。如果模块数量达到特定阈值,保持一致的配置将成为一项挑战。

尽可能少公开信息

应尽量减少模块的公共接口,并且仅公开基本信息。不应向外部泄露任何实现细节。请尽可能缩小范围。使用 Kotlin 的 private 或 internal 可见性范围将模块声明设为私有模块。在模块中声明依赖项时,请优先使用 implementation 而不是 api。后者会向模块的使用方公开传递依赖项。使用实现可以缩短构建时间,因为这样可以减少需要重新构建的模块数量。

首选 Kotlin 和 Java 模块

AS支持应用模块(APK)、库模块(AAR)及Kotlin/Java模块(JAR),APK、AAR模块包含了资源会产生较多的开销,因此您应当优先尽可能多地使用 Kotlin 或 Java 库。

推荐阅读

  • • kotlin依赖注入框架之koin(一)
  • • kotlin依赖注入框架之koin(二)
  • • kotlin依赖注入框架之koin(三)

欢迎关注我的公众号“虎哥Droid”,原创技术文章第一时间推送。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【营销】uplift建模方案-专利总结

之前准备写专利的时候浏览了一下其他公司的专利&#xff0c;对于one model&#xff0c;还是two model&#xff0c;基模型是什么做了简单总结。 浦发银行&#xff08;CN 112446541 A&#xff09;——one model&#xff08;标签转换&#xff09; 基模型&#xff1a;NN分类融合m…

经济学学习(宏观)

--------------------------------------- 第8篇&#xff1a;宏观经济学的数据 --------------------------------------- 23. 一国收入的衡量(GDP&#xff0c;通胀) gdp衡量总收入和总支出&#xff0c;总收入总支出 某一既定时期&#xff0c;一个国家内生产的所有最终商品…

【科研试剂】16-Heptadecynoic acid,93813-16-2,16-庚二酸

【中文名称】16-庚二酸【英文名称】 16-Heptadecynoic acid&#xff0c;16-Heptadecynoic COOH【结 构 式】【CAS】93813-16-2【分子式】C17H30O2【分子量】266.43【纯度标准】95%【包装规格】1g&#xff0c;5g&#xff0c;10g【是否接受定制】可进行定制&#xff0c;定制时间周…

Java日志系统介绍和slf4j的使用

目录1. 日志系统介绍2. slf4j的使用2.1 slf4j的入门2.2 slf4j绑定日志框架1. 日志系统介绍 日志门面位于应用程序和日志框架之间&#xff0c;日志门面提供一个抽象的能力&#xff0c;日志框架进行具体的日志实现。可以很方便的更换日志框架。类似JDBC驱动 日志门面有&#xf…

业务逻辑漏洞

1、容易忽略的低危漏洞以及延伸利用 一、容易忽略的低危漏洞以及延伸利用 在挖洞的过程当中&#xff0c;比如我们碰到信息泄露漏洞&#xff0c;但是我们不知道这个是信息泄露&#xff1b;或者说我们碰到一个xss&#xff0c;我们不会利用&#xff0c;只能弹个窗&#xff0c;比如…

AcWing 4510. 寻宝!大冒险!(暴力枚举)

题目如下&#xff1a; 输入样例1&#xff1a; 5 100 2 0 0 1 1 2 2 3 3 4 4 0 0 1 0 1 0 1 0 0输出样例1&#xff1a; 3样例 111 解释 绿化图上 (0,0)(0,0)(0,0)、(1,1)(1,1)(1,1) 和 (2,2)(2,2)(2,2) 三处均可能埋有宝藏。 输入样例2&#xff1a; 5 4 2 0 0 1 1 2 2 3 3 …

C++入门:命名空间

目录 一.前言 C关键字(C98)总览&#xff1a; 一.作用域 二.命名冲突 三.命名空间 命名空间定义&#xff1a; 命名空间的嵌套定义&#xff1a; 四.命名空间的使用 五.命名空间的本质 一.前言 C是从C语言延伸出来的编程语言&#xff0c;C兼容了C语言百分之九十九的语法…

Lr 12 ACR 15:蒙版

Adobe Camera Raw &#xff08;简称为 ACR&#xff09;与 Lightroom Classic&#xff08;简称为 Lr 或 LrC&#xff09;使用同一引擎&#xff0c;其中的蒙版 Mask功能变得日益强大。基于人工智能技术&#xff08;AI 驱动&#xff09;&#xff0c;可快速而精准地选择主体、天空、…

Redis - Redis 6.0 新特性之多线程模型

1. Redis6.0之前的版本真的是单线程么&#xff1f; 否&#xff01;Redis 在处理客户端的请求时&#xff0c;包括获取 (socket 读)、解析、执⾏、内容返回 (socket 写) 等都由⼀个顺序串⾏的主线程处理&#xff0c;这就是所谓的「单线程」。 在执行命令阶段&#xff1a;Redis是…

【jQuery超快速入门教程】上篇

&#x1f340;作者主页&#xff1a;在下周周ovo&#x1f340;系列专栏&#xff1a;从零开始百天学习前端基础&#x1f340;其他平台&#xff1a;博客园1️⃣前言&#xff1a;jQuery必备网站jQuery下载地址jQuery中文文档jQuery插件库1️⃣一、为什么要学习jQuery&#xff1f;jQ…

蓝库云|2023年企业4个数字化转型关键,成功之路近在咫尺

数字化转型&#xff1a;由上而下的过程 企业数字化转型最主要的原因在于企业管理者的决定。数字化转型是由「上」而「下」的过程&#xff0c;如果管理层没有转型的确切目标与规划&#xff0c;与竞争者相比之下&#xff0c;经营模式将会原地踏步、无法超越。蓝库云根据最新客户…

C++:C++全局变量:看完还不懂全局变量来捶我

我们知道&#xff0c;全局变量时C语言语法和语义中一个很重要的知识点&#xff0c;首先它的存在意义需要从三个不同角度去理解。 对于程序员来说&#xff0c;它是一个记录内容的变量&#xff08;variable&#xff09;对于编译/链接器来说&#xff0c;它是一个需要解析的符号 &a…

java使用JSch连接服务器实现命令交互

java使用JSch连接服务器实现命令交互1、通过maven引入jsch2、代码编写&#xff08;1&#xff09;创建MyUserInfo&#xff08;2&#xff09;创建Shell类连接服务器&#xff08;3&#xff09;启动3、测试结果JSch官网 1、通过maven引入jsch <dependency><groupId>co…

万字长文--详解Git(快速入门)

Git基础与扩展Git1、Git概念1.1 关于版本控制1.2 Git基础概念2、Git基础操作2.1 安装并配置Git2.2 Git的基本操作3、Github操作3.1 关于开源3.2 注册账号3.3 远程仓库的使用4、Git分支操作4.1 本地分支操作4.2 远程分支操作Git 1、Git概念 1.1 关于版本控制 文件的版本管理的…

7 种常用的数据挖掘技术分享

有人说&#xff1a;一个人从1岁活到80岁很平凡&#xff0c;但如果从80岁倒着活&#xff0c;那么一半以上的人都可能不凡。 生活没有捷径&#xff0c;我们踩过的坑都成为了生活的经验&#xff0c;这些经验越早知道&#xff0c;你要走的弯路就会越少。 摘要: 随着信息领域的进步…

详解DFS(深度优先搜索)算法+模板+指数+排列+组合型枚举+带分数四道例题

目录 前言&#xff1a; 1.背景 2.图解分析 3.算法思想 4.dfs四大例题 4.1.递归实现指数型枚举 题解&#xff1a; 4.2.递归实现排列型枚举 题解&#xff1a; 字典序: 4.3.递归实现组合型枚举 题解: 4.4.带分数 题解&#xff1a; 5.最后&#xff1a; 前言&#xff1a;…

来了解一下ASN.1?

想要了解证书&#xff0c;必须先了解ASN.1和编码规则。这篇文章简单介绍ASN.1&#xff0c;不过分探讨细节&#xff0c;大家如果有兴趣可以继续深入研究。 一、ASN.1 ASN.1是Abstract Syntax Notation One&#xff08;抽象文法描述语言&#xff09;的缩写。计算机系统之间交换…

Android Studio 阅读 frameworks/base 下的代码

从网上搜的方案都是生成 android.ipr&#xff0c;但是这个需要整编&#xff0c;整编一次比较费时费劲&#xff0c;所以想了个巧招 首先用 Android Studio 打开 frameworks/base&#xff0c;其文件夹目录大概形如下&#xff1a; ├── Android.bp├── Android.mk├── api …

入门深度学习——基础知识总结(python代码实现)

入门深度学习——基础知识总结&#xff08;python代码实现&#xff09; 目前&#xff0c;AI基本上可以说是烂大街了。几乎什么都可以说使用了AI技术&#xff0c;听起来很拉风&#xff0c;很nb的样子。而其中目前最为火热的非深度学习&#xff08;Deep Learning&#xff09;莫属…

VisionPro (R) QuickBuild 工具使用问题解决 自用

右击我的电脑选择属性。搜索“安全中心” 点击病毒和威胁保护 在病毒和威胁保护中选择威胁信息&#xff08;当前威胁-保护历史记录&#xff09; 受影响的项目 file: C:\WINDOWS\sysWOW64\cognex.dll 相机和光源不能同时触发&#xff0c;光源要先于相机触发并且持续相机采集…