Now in Android 现代应用开发实践(一) - 模块化设计

0 阅读12分钟

截至2026年,Now in Android(以下简称 NiA)依然是由 Google 官方维护、最能代表“现代 Android 应用架构”的标杆项目,GitHub 星标已突破 20K。

它不仅系统性地展示了 Jetpack 全家桶的最佳实践,更关键的是,提供了一整套面向中大型项目的可落地工程方案——涵盖模块化设计分层架构自动化测试性能分析代码提交、风格检查以及团队协作等核心环节,是 Android 应用开发者的必学项目

本系列文章专为 Android 应用开发者打造,将以抓大放小的模式深入解析 Now in Android 的设计精髓,全系列共八章。

  • 《Now in Android 现代应用开发实践(一):模块化设计》
  • 《Now in Android 现代应用开发实践(二):架构设计(data+domain)》
  • 《Now in Android 现代应用开发实践(三):架构设计(UI)》
  • 《Now in Android 现代应用开发实践(四):构建逻辑》
  • 《Now in Android 现代应用开发实践(五):代码规范》
  • 《Now in Android 现代应用开发实践(六):质量保障体系》
  • 《Now in Android 现代应用开发实践(七):Benchmark 性能测试》
  • 《Now in Android 现代应用开发实践(八):Baseline 性能优化》

本文作为开篇,将结合 Android 官方模块化指南,重点解读 NiA 的模块化设计思路,帮助你厘清以下关键问题:

  • 为什么需要模块化?
  • NiA 到底如何划分模块?
  • 它遵循了 哪种 官方推荐的模块化模式?
  • 哪些设计决策值得在实际项目中借鉴?

通过本文,你将获得一套可复用的模块化思维框架,为构建健壮、可维护的现代 Android 应用打下坚实基础。


一、为什么绕不开模块化?

Android官方模块化指南把好处总结得很直接:

  • 构建速度:增量编译、并行执行、缓存命中率大幅提升
  • 可维护性: 关注点分离、降低耦合、修改一处不影响全局
  • 团队协作: 模块可以分配owner,减少代码冲突
  • 可测试性: 模块粒度测试更容易Mock、更快运行
  • 可重用性: 核心功能模块可复用到多个App/变体/Wear/TV
  • 功能按需交付(Dynamic Feature)潜力
  • 封装与可见性控制: 利用internal真正隐藏实现细节

简单总结就是,当项目超过2-3万行代码、超过5-8人团队时,模块化几乎是唯一能把复杂度控制住的手段


二、主流模块化方案分析

在 Android 架构设计中,常见的模块组织模式主要有三种:纯分层纯按功能划分,以及分层与功能混合的模式。

1.纯分层模式

  • 核心思路:按照技术职责纵向划分,典型的层级顺序为 data → domain → feature → app。

  • 优点:这种结构清晰简单,天然契合 MVVM 架构思想,易于上手。

  • 缺点:不同业务功能之间容易产生耦合,因为它们共享同一套分层结构,修改一个模块可能影响其他模块。

  • 适用场景适合中小型项目,团队规模不大、业务复杂度较低时使用效果较好。

2.纯按功能划分模式

  • 核心思路:以业务功能为单位横向组织代码,每个功能模块(如“登录”、“个人中心”、“订单”)几乎形成一个独立闭环,包含自己的数据、领域逻辑和 UI 层(类似组件化)。

  • 优点:这种方式实现了极高的模块独立性,非常适合多团队并行开发,并支持动态下发或独立发布特定功能。

  • 缺点:其代价是容易造成重复代码(比如多个模块各自实现相似的网络请求或工具类),且全局数据层难以统一设计和维护。

  • 适用场景:它主要适用于超大型应用,尤其是需要多团队协作、支持模块化动态交付的场景。

3.分层 + 功能混合模式

  • 核心思路:将架构分为两大部分:底层是高度抽象、通用的核心层(包含共享的数据源、工具类、基础组件等),上层则是按功能划分的独立业务模块。

  • 优点:既保证了通用能力的复用,又赋予各业务模块足够的自治性。它在可维护性、可扩展性和团队协作效率之间取得了良好平衡

  • 缺点:初期架构搭建和理解成本略高,需要团队有一定经验,

  • 适用场景中大型生产项目主流选择

Now in Android 采用的就是第三种“分层 + 功能混合”模式,而且做了非常有特色的细化 - Feature模块内部api / impl 双层分离。


三、NiA 模块化结构解析

:app                       # 应用入口(脚手架 + 导航)
│
├─ :feature
│   ├─ :foryou
│   │   ├─ :api           # 只放导航key和导航扩展函数
│   │   └─ :impl          # 真正的Screen、ViewModel、UI逻辑
│   ├─ :interests
│   ├─ :topic
│   └─ ... (其他业务功能)
│
├─ :core
│   ├─ :data              # 统一对外数据仓库(Repository)
│   ├─ :network           # Retrofit + 数据源
│   ├─ :database          # Room
│   ├─ :datastore         # Preferences DataStore
│   ├─ :model             # 纯Kotlin 数据类(跨模块共享)
│   ├─ :ui                # 复合UI组件(依赖model和data)
│   ├─ :designsystem      # 基础UI组件 + 主题 + 图标(纯Compose)
│   ├─ :common            # 通用工具、Dispatchers、Result封装等
│   └─ :testing           # 测试桩、TestRunner等
│   └─ ...
│
├─ :sync                  # 工作管理器同步任务(同样属于应用的业务逻辑)
├─ :benchmark             # 宏基准测试
└─ :app-NiA-catalog       # 静态展示App内的组件,是一个独立应用

1.App 模块:应用的 “粘合剂”

App 模块是整个应用的入口,属于android-application类型,核心职责是整合所有模块,提供应用级脚手架,不承担具体业务逻辑。

核心内容:

  • 包含MainActivityNiAApp等应用级入口类,以及全局导航配置(如NiANavHost实现应用级导航、TopLevelNavItem配置底部导航栏);

依赖规则:

  • 依赖所有功能(Feature)模块和所需的核心(Core)模块,是依赖链的 “顶端”。

2.功能(Feature)模块:业务逻辑的载体

Feature 模块是应用的业务核心,每个模块聚焦一个具体功能(如 “为你推荐”“兴趣管理”“主题详情”),遵循 “单一职责” 设计。

NiA 对 Feature 模块做了精细化拆分 —— 每个 Feature 模块拆分为两个子模块:

  • api 子模块:仅包含导航键(如TopicNavKey)和导航函数(如Navigator.navigateToTopic),用于对外暴露跳转能力;

  • impl 子模块:包含功能的所有核心逻辑(UI 界面、ViewModel、业务处理等),是功能的实际实现层。

依赖规则:

  • api 子模块:不依赖其他 Feature 的api/impl子模块,仅依赖 Core 模块;

  • impl 子模块:仅可依赖其他 Feature 的api子模块(用于跨功能跳转),且仅依赖所需的 Core 模块;

  • 核心原则:避免 Feature 模块间的直接耦合,通过api层实现 “最小化依赖”。

3.核心(Core)模块:通用能力的封装

核心模块是通用的库模块,包含辅助代码和需要在应用其他模块间共享的特定依赖项。这些模块可以依赖于其他核心模块,但不应依赖于功能模块或app模块。

模块路径主要功能 / 职责典型类 / 文件示例备注说明
:core:model存放应用中通用的纯Kotlin数据模型类(无Android依赖)NewsResource, Topic, Episode, Author纯jvm module,最底层模型
:core:database使用Room实现本地数据库,包括数据库定义、Entity、Dao和迁移NiADatabase, NewsResourceDao, DatabaseMigrationsRoom数据库核心
:core:datastore使用Preferences DataStore存储用户偏好设置(轻量级KV存储)NiAPreferences, UserPreferencesSerializer常用于用户偏好、首次启动标志等
:core:datastore-protoDataStore的protobuf定义文件(独立出来方便多平台复用).proto 文件(如user_preferences.proto)一般不直接使用,仅供datastore依赖
:core:network网络层实现,包括Retrofit接口定义、数据源、拦截器等RetrofitNiANetworkApi, NiANetworkDataSource远程数据源核心
:core:data数据层统一对外接口(Repository模式),聚合本地+远程数据源,提供业务所需数据TopicsRepository, NewsRepository, UserDataRepository最常用的数据仓库入口
:core:data-test数据层相关的测试桩(fake实现、测试双重实现)FakeNewsRepository, TestNiANetworkDataSource专供单元测试/集成测试使用
:core:ui业务相关的复合UI组件(依赖model和data层,能展示真实业务数据)NewsResourceCard, NewsFeed, TopicGridItem区别于designsystem,更偏业务
:core:designsystem基础设计系统:主题、颜色、排版、图标、基础Compose组件NiATheme, NiAButton, NiAIcons, NiATopAppBar类似组件库,可单独预览
:core:common通用工具类、扩展函数、跨模块共享的小型工具NiADispatchers, Result, Async, DispatcherProvider放那些到处都要用的小工具
:core:analytics埋点/分析相关接口与实现(目前主要对接Firebase Analytics)AnalyticsHelper, StubAnalyticsHelper可切换真实/空实现,方便测试
:core:testing通用测试工具、规则、runner、Hilt测试模块等NiATestRunner, TestDispatcherRule, HiltTest几乎所有测试模块都会依赖它

4.数据同步(Sync)模块

sync: 负责处理后台同步逻辑。它利用 WorkManager 定时或根据触发条件从网络更新数据,并将其持久化到本地数据库中。

NiA采用本地数据优先的架构设计方式,有关架构设计的分析下一章会详细介绍。

5.工程治理与构建逻辑类

这类模块不直接包含业务代码,而是为了管理日益复杂的 Gradle 构建配置。

  • build-logic: 这是 NiA 最核心的构建管理模块。随着模块数量增加,NiA 提取了通用的配置插件(Convention Plugins),用于统一管理各模块的编译 SDK 版本、Compose 配置、Lint 规则和 Hilt 依赖。这样可以避免在几十个模块的 build.gradle 中重复写相同的配置。

  • spotless: 这是一个代码格式化工具模块。它通过配置特定的规则(如 Kotlin 编码规范、License 头部信息),确保所有开发者提交的代码风格高度统一。在 NiA 中,它通常结合 Git Hooks 或 CI 流程使用。

  • lint: 包含自定义的 Lint 规则。除了 Android 默认的检查外,NiA 开发团队编写了特定的静态代码检查规则,以确保项目遵循特定的架构规范(例如:禁止在某些层级直接引用特定库)。

6.测试与性能分析类

这类模块用于保障应用的质量和运行效率。

  • benchmark: 包含 Macrobenchmark 测试代码。它专门用于测量应用的启动性能和滚动帧率(Jank)。通过这个模块,开发者可以生成 Baseline Profiles 以优化应用的首次运行速度。

  • ui-test-hilt-manifest: 这是一个特殊的辅助模块。由于 Hilt 在进行集成测试时需要一个空的 ComponentActivity 作为容器,该模块专门提供所需的 Android Manifest 和 Activity 声明,确保测试代码能正常注入依赖,同时不污染生产代码。

7.基础设施与工具类

  • tools: 存放开发辅助脚本。
  • kokoro: 这是 Google 内部使用的持续集成(CI)配置文件路径。它包含用于在 Google 基础设施上运行构建、测试和发布流程的脚本(类似于公共领域的 GitHub Actions 配置)。
  • app-NiA-catalog: 这是一个独立的“设计系统目录”APP。它不具备新闻浏览功能,而是专门用于展示 core:designsystem 中定义的所有 UI 组件(如按钮、图标、颜色)。开发者可以通过运行这个 App 快速预览和调试 UI 组件,而无需启动整个主应用。

四、值得借鉴的几个关键决策点

1.什么时候把功能拆成 api / impl 双模块?

当这个功能会被多个其他页面跳转时(例如 topic、bookmark、search 都是常见被跳转目标)

2.core 模块要拆多细?

NiA给出了比较务实的参考:

  • 数据层较小时,可以只保留 :core:data

  • 数据源/仓库变多时,再拆 :network:database:datastore

  • UI组件较多时,拆 :designsystem(纯公共UI组件) 和 :ui(业务UI组件)

3.要不要追求极致模块化?

根据NiA官方的说法: “我们在过度模块化一个小应用 和 展示适用于生产级大型代码库的模式 之间寻找平衡。 ”所以适度模块化才是王道——粒度太细会显著增加构建配置和维护成本


五、总结与建议

Now in Android 的模块化设计是目前(2026年)Android社区里最值得参考的生产级实践之一。相比其他 Android 示例(如 Todoist 或 Sunflower),NiA 更注重 Compose UI 和模块化架构的深度集成。

NiA采用分层 + 功能混合模式,并通过功能模块 api/impl 分离巧妙地解决了功能间跳转耦合问题,同时保持了核心代码的高度可重用性。

如果你正在/准备做一个中大型项目(预计>10个主要页面、>5人团队协作、生命周期>2年),强烈建议你:

1.先画出模块依赖图(Mermaid/PlantUML/手绘都行)

2.决定核心模块的拆分粒度

3.对于高频被跳转的功能模块,考虑采用 api / impl 分离

4.所有模块都写README + 依赖图(NiA中的依赖关系图由工程脚本自动生成,后续将讲解如何实现)

下一篇文章我们将深入剖析 NiA 的架构设计,敬请期待~


参考资料

本文写作时使用的辅助AI模型:Gemini Pro + Qwen3 Max