首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Now in Android 模块化学习之旅:`app` / `feature` / `core` 三层 Gradle 模块划分实战解析
📅 2026/9/13 19:43:15
✍️ 爱科研究院
👁 阅读 3,247
Now in Android 模块化学习之旅app/feature/core三层 Gradle 模块划分实战解析【免费下载链接】nowinandroidA fully functional Android app built entirely with Kotlin and Jetpack Compose项目地址: https://gitcode.com/GitHub_Trending/no/nowinandroid本文以 Now in AndroidNiA官方仓库的 ModularizationLearningJourney.md 为主线系统讲解该应用如何将功能拆分为app、featureapi/impl 双子模块与core三类 Gradle 模块并结合settings.gradle.kts、TopicNavKey等源码证据帮助读者掌握模块依赖规则、导航解耦方式与依赖图维护流程可直接迁移到自己的多模块 Android 项目中实践。学习路径概览为什么 NiA 要这样拆分模块模块化Modularization是把大型应用拆分为多个独立、可复用模块的工程实践。Now in Android 是一个完全基于 Kotlin 与 Jetpack Compose 构建的完整功能型示例应用其仓库中每个模块的 README 都附带一张模块依赖图例如:app模块依赖图可以用来快速理解整个项目的结构。有关模块化的完整官方理论可参考 Android 官方模块化指南。本文档的核心价值在于它不止给出了 NiA 的模块清单更明确了三类模块各自的边界、它们之间的依赖规则谁可以依赖谁、谁绝不能依赖谁以及这些规则在真实源码中的落地形态。下面的 Mermaid 图概括了 NiA 中app、feature、core三类模块的典型关系完整图例参见 docs/ModularizationLearningJourney.md 中的模块类型图图中图例约定如下app通过虚线implementation依赖引用各个feature模块android-library类型的core模块通过实线api依赖向jvm-library模块暴露 API。Top tip在模块化规划阶段先画出一张模块图来可视化各模块之间的依赖关系是非常有效的规划手段——本仓库用 Mermaid 以代码形式维护这张图天然适合评审与自动化校验。NiA 的三类模块及其职责边界Now in Android 将全部模块划分为三类每一类都有明确的职责与依赖约束完整模块清单见 settings.gradle.kts 中的include声明。app模块一切代码的组装者app模块包含应用级与脚手架类负责把其余代码库绑在一起典型实现包括MainActivityapp/src/main/.../MainActivity.kt应用入口 ActivityNiaAppNiaApp.kt根 Compose 组合承载主题、渐变背景、离线 Snackbar 与顶部应用栏应用级导航通过NiaNavHost与TopLevelDestination组织页面跳转。在NiaApp.kt中可以看到app模块如何指挥各 feature 模块它通过entryProvider同时注册了forYouEntry、bookmarksEntry、interestsEntry、topicEntry、searchEntry五个入口并通过 TopLevelNavItem.kt 中的TOP_LEVEL_NAV_ITEMS映射ForYouNavKey、BookmarksNavKey、InterestsNavKey到对应的图标与标题资源——这些 NavKey 全部来自各 feature 的api模块app 只负责把它们组合进导航体系这正是应用级脚手架的体现。app模块依赖所有feature模块以及必需的core模块。从 app/README.md 的完整依赖图可以看到:app以implementation方式指向:feature:*:api、:feature:*:impl、:sync:work以及:core:analytics、:core:common、:core:data、:core:designsystem等还与:benchmarksBaseline Profile 测试 APK存在双向虚线关联。feature模块单点职责 api/impl 双子模块feature 模块处理应用中单一职责的功能。例如ForYou功能负责 ForYou 屏幕的全部内容与 UI 状态包括首次运行的引导流程。NiA 中 feature 模块本身不是独立的 Gradle 模块而是被拆成两个子模块api—— 只包含导航键navigation keysimpl—— 包含其余所有内容UI 组件、ViewModel 等。这种拆分带来两个关键能力跨功能导航解耦功能 A 可以通过目标功能 B 的api模块中的导航键跳转到功能 B而不需要了解 B 的任何内部实现。复用与隔离api/impl子模块可以被任何应用使用包括测试或带 flavor 的应用。一个类如果只被某一个 feature 用到就留在该 feature 内如果被多个功能共享则应下沉到合适的core模块。feature 模块的依赖铁律api模块不得依赖其他 feature 的api或impl模块impl模块只能依赖其他 feature 的api模块不得依赖其他 feature 的impl两个子模块都只能依赖它们确实需要的core模块。这条规则在源码中可以得到直接验证。以 feature/topic/impl/build.gradle.kts 为例feature:topic:impl的依赖只有projects.core.data、projects.feature.topic.api以及测试相关的core:testing完全没有触碰其他 feature 的impl。core模块跨模块共享的公共库core模块是包含辅助代码与特定依赖的公共库供应用内其他模块共享。它们可以依赖其他 core 模块但绝不能依赖 feature 模块或 app 模块。NiA 的 core 层非常丰富从 settings.gradle.kts 可以看到至少包括analytics、common、data、data-test、database、datastore、datastore-proto、datastore-test、designsystem、domain、model、navigation、network、notifications、screenshot-testing、testing、ui。注意其中common、model、datastore-proto是纯 JVM 库jvm-library在依赖图中以紫色标记可以显著提升单元测试速度与构建缓存命中率。杂项模块除上述三类外NiA 还有一批特殊模块sync含sync:work与sync:sync-test负责后台同步、benchmarksMacrobenchmark 基准测试、ui-test-hilt-manifest、lint以及app-nia-catalog—— 一个专门用于快速展示设计系统的目录应用可在 IDE 中通过app-nia-catalog运行配置查看 design system。模块示例对照表从文档到源码逐一对号入座原文档用一张对照表说明每个模块的职责与关键类以下结合仓库源码逐一印证模块职责关键类与实例app将应用正常运转所需的一切绑定在一起包括 UI 脚手架与导航NiaApp、MainActivity通过NiaNavHost、NiaAppState、TopLevelDestination当前仓库中对应 TopLevelNavItem.kt 与 NiaAppState.kt实现应用级导航feature:*:api提供其他 feature 可用的导航键与导航函数TopicNavKey见下节源码分析feature:*:impl特定功能或用户旅程的完整实现通常包含 UI 组件与 ViewModel并从其他模块读取数据feature:topic:impl的TopicScreen、TopicViewModelTopicScreen.kt、TopicViewModel.ktfeature:foryou:impl展示用户新闻流与首次运行引导core:data从多个数据源获取应用数据供不同 feature 共享TopicsRepositoryTopicsRepository.kt等仓库接口其默认实现为OfflineFirstTopicsRepositorycore:designsystem设计系统核心 UI 组件多为定制化的 Material 3 组件、应用主题与图标可通过app-nia-catalog运行配置预览NiaIcons、NiaButton、NiaThemecore:ui供 feature 模块使用的复合 UI 组件与资源如新闻流。与designsystem不同它依赖数据层因为要渲染NewsResource等模型NewsFeed、NewsResourceCardExpandedcore:common模块间共享的通用类NiaDispatchers、Resultcore:network发起网络请求并处理来自远程数据源的响应RetrofitNiaNetworkApicore:testing测试依赖、测试仓库与工具类NiaTestRunner、TestDispatcherRulecore:datastore使用 DataStore 存储持久化数据NiaPreferences、UserPreferencesSerializercore:database使用 Room 的本地数据库存储NiaDatabase、DatabaseMigrations、各Dao类数据库 schema 版本见 core/database/schemas/.../NiaDatabasecore:model全应用共享的模型类TopicTopic.kt、Episode、NewsResource一个值得注意的细节core:ui依赖数据层如NewsResource模型而core:designsystem不依赖这说明 NiA 刻意把纯展示组件与绑定了业务模型的复合组件分成两个 core 模块从而让设计系统保持最大程度的可复用性。跨模块导航的解耦范式TopicNavKey实例剖析文档以:topic:api与:interests:impl的交互作为 feature 间导航的标准范式。真实的 TopicNavKey.kt 实现如下Serializable data class TopicNavKey(val id: String) : NavKey fun Navigator.navigateToTopic( topicId: String, ) { navigate(TopicNavKey(topicId)) }可以看到TopicNavKey是一个Serializable的 data class实现androidx.navigation3.runtime.NavKey携带目标话题的id作为导航参数navigateToTopic是Navigator的扩展函数内部直接navigate(TopicNavKey(topicId))。这个范式下:interests:impl只需依赖:topic:api即可在用户点击某个 topic 时调用navigateToTopic(topicId)从InterestsScreen跳到TopicScreen。Navigator与导航状态的定义在 core/navigation并有对应的 NavigatorTest.kt 单元测试验证导航逻辑。整个链路验证了文档的结论feature 间通过 api 模块暴露的导航键通信实现彻底解耦这正是 api/impl 拆分最重要的工程收益。依赖图的维护README 图、Build.yaml 与graphUpdate任务每个模块的README.md都包含一张模块图例如:app模块依赖图。这些图不是手工维护的静态图片而是与代码同步的 Mermaid 文本块。当模块依赖发生变化时图会由 Build.yaml 工作流自动更新。具体机制见该工作流第 81 行与第 93 行./gradlew graphUpdate工作流中运行./gradlew graphUpdate重新生成各模块 README 中的依赖图若图与当前依赖不一致例如开发者改了依赖但没更新图第 93 行会输出错误提示Check Graphs failed, please update graphs with: ./gradlew graphUpdate并使 CI 失败强制开发者提交同步的图。开发者也可以在本地手动运行graphUpdate任务刷新所有 README 图。这一做法把架构图漂移问题变成了 CI 可检查项值得在大型多模块项目中借鉴。模块化程度的权衡过度模块化 vs 面向未来扩展NiA 的模块化方案是结合项目路线图、规划中的工作与新特性反复推敲后确定的。其核心目标是在过度模块化一个小型应用与用这个机会展示适合更大代码库、更接近生产环境真实应用的模块化模式之间找到平衡。原文档docs/ModularizationLearningJourney.md给出的关键建议值得强调模块化没有唯一正确答案应用大小、代码库现状与团队偏好不同方案必然不同。很少有哪一种方式适合所有场景。规划先行动手前先明确目标、要解决的问题、未来工作与潜在障碍是定义最合适结构的关键步骤。可以组织一次头脑风暴把模块与依赖画成图来可视化规划。按需调节粒度granularity粒度指代码库由多少模块组成。数据层规模小的时候放在单一模块完全没问题一旦仓库与数据源数量增长就值得考虑拆分为独立模块。保持演进心态NiA 的方案并非一成不变的结构未来仍可能演进它只是一个最适合本项目的通用指导方针开发者可以在其上继续修改、扩展。例如通过提高代码库粒度来进一步拆分。这也与仓库中core模块的丰富分层相互印证NiA 把数据、数据库、网络、DataStore、设计系统、UI、领域层等都拆成了独立 core 模块正是按需扩展粒度的具体示范。小结把 NiA 的模块化经验移植到自己的项目回顾全文Now in Android 的模块化方案可以提炼为四条可直接落地的经验三层职责划分app负责组装与脚手架feature负责单点业务功能core负责跨模块共享的基础能力三者依赖方向严格单向feature → coreapp → 一切。api/impl 双子模块feature 对外只暴露导航键用Serializable的NavKeyNavigator扩展函数实现无实现依赖的跨功能跳转可测试、可复用、可替换。依赖规则显式化api不依赖其他 featureimpl只依赖其他 feature 的apicore 不依赖 feature/app——这些约束在build.gradle.kts中可审计、可强制。架构图自动化用 Mermaid 在 README 中维护模块图通过./gradlew graphUpdate与 Build.yaml 工作流保证图与代码一致。最后请记住这不是唯一正确的方案而是 NiA 团队在社区反馈与项目实际约束下给出的一个高质量参照系。阅读源码时可以从 settings.gradle.kts 的模块清单出发对照 app/README.md 的完整依赖图再逐个深入各 feature 与 core 模块的源码与测试构建起对多模块 Android 应用架构的完整认知。【免费下载链接】nowinandroidA fully functional Android app built entirely with Kotlin and Jetpack Compose项目地址: https://gitcode.com/GitHub_Trending/no/nowinandroid创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/13 19:43:15
The Dark Query Problem:用 OpenSEO 三角交叉验证重建 Search Console 隐藏的搜索意图
2026/9/13 19:43:15
把游戏库体积砍掉近半:RomM 里 CHD 镜像压缩实操指南
2026/9/13 19:38:15
Label Studio 交通监控目标检测模板:Bounding Box 标注配置与实践
2026/9/13 21:18:25
昇思MindSpore关系抽取实战:从大模型到工业级逻辑理解
2026/9/13 21:18:25
Apache POI Fesod引擎族:替代EasyExcel的可控Excel处理方案
2026/9/13 21:18:25
深入解析 go-openapi/swag jsonutils:基于可插拔 Adapter 的 Go JSON 序列化工具包
2026/9/13 21:18:25
NocoBase 生产部署如何用 Nginx 反向代理与静态资源代理
2026/9/13 21:18:25
VictoriaMetrics 组件对接云对象存储实战指南:S3 / GCS / Azure Blob 备份与规则读取
2026/9/13 21:13:24
Ignite 新建项目时 CNG 与 manual 工作流怎么选?
2026/9/13 0:01:25
拯救者Y7000黑屏故障排查与维修实战指南
2026/9/13 0:01:25
AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
2026/9/13 0:01:25
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化
2026/9/13 0:01:25
拯救者Y7000黑屏故障排查与维修实战指南
2026/9/13 0:01:25
AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
2026/9/13 0:01:25
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化