Now in Android 项目结构分析:这个 App 是如何搭建起来的?
核心观点
Now in Android 的项目结构并不是为了让开发更简单,而是为了让大型团队能够长期维护同一个代码库。
理解 NIA 的关键,不是记住它有多少个 Module,而是理解这些 Module 分别承担什么职责,以及它们之间如何协作。
整体结构
NIA 大致可以分为五个部分:
app/
feature/
core/
sync/
benchmark/
build-logic/
它们共同构成了整个应用。
app:应用装配层
app 模块负责将所有能力组装成一个完整的应用。
主要职责包括:
- Application 初始化;
- MainActivity 入口;
- Navigation 配置;
- 依赖注入初始化;
- Feature 模块整合。
它本身不承载复杂业务逻辑,而是承担"组装者"的角色。
可以将其理解为:
App = Feature + Core 的装配工厂。
feature:业务功能层
Feature 模块按照业务进行拆分,例如:
feature:foryou
feature:search
feature:bookmarks
feature:topic
每个模块负责:
- Compose Screen;
- ViewModel;
- UI State;
- 与 Repository 交互。
其目标是:
让业务功能能够独立开发和演进。
这种拆分方式天然适合多人协作。
core:共享能力层
Core 模块提供整个应用共享的能力。
例如:
core:model
core:data
core:database
core:network
core:ui
core:designsystem
它们通常不直接面向用户,而是为 Feature 提供基础设施支持。
例如:
- model:领域模型;
- data:Repository 实现;
- database:Room 数据库;
- network:网络请求;
- ui:共享 UI 组件;
- designsystem:统一设计规范。
其目标是:
避免重复实现公共能力。
sync:同步层
NIA 引入了专门的数据同步模块。
其职责包括:
- 后台同步数据;
- 保证本地数据与远程数据一致;
- 管理同步策略。
这种设计体现了离线优先(Offline First)的思想。
在普通项目中,这一层往往被合并到 Repository 中。
benchmark:性能工程层
NIA 提供了完整的性能基础设施,包括:
- Macrobenchmark;
- Baseline Profile。
其目标是:
- 测量启动性能;
- 发现性能回退;
- 优化首次启动体验。
对于大型应用而言,性能属于工程能力的一部分。
build-logic:构建逻辑层
这是许多人第一次阅读 NIA 时最困惑的部分。
其作用是:
将重复的 Gradle 配置抽离出来。
例如:
- Android Library 配置;
- Compose 配置;
- Kotlin 配置;
- Hilt 配置。
通过 build-logic,整个项目的构建规则得到统一。
代价是:
- 学习成本增加;
- Gradle 理解门槛提高。
整个项目是如何协作的?
用户点击 App 图标后,大致流程如下:
#mermaid-svg-Yv4SqWV3hKKObhVH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Yv4SqWV3hKKObhVH .error-icon{fill:#552222;}#mermaid-svg-Yv4SqWV3hKKObhVH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Yv4SqWV3hKKObhVH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Yv4SqWV3hKKObhVH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Yv4SqWV3hKKObhVH .marker.cross{stroke:#333333;}#mermaid-svg-Yv4SqWV3hKKObhVH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Yv4SqWV3hKKObhVH p{margin:0;}#mermaid-svg-Yv4SqWV3hKKObhVH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH .cluster-label text{fill:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH .cluster-label span{color:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH .cluster-label span p{background-color:transparent;}#mermaid-svg-Yv4SqWV3hKKObhVH .label text,#mermaid-svg-Yv4SqWV3hKKObhVH span{fill:#333;color:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH .node rect,#mermaid-svg-Yv4SqWV3hKKObhVH .node circle,#mermaid-svg-Yv4SqWV3hKKObhVH .node ellipse,#mermaid-svg-Yv4SqWV3hKKObhVH .node polygon,#mermaid-svg-Yv4SqWV3hKKObhVH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Yv4SqWV3hKKObhVH .rough-node .label text,#mermaid-svg-Yv4SqWV3hKKObhVH .node .label text,#mermaid-svg-Yv4SqWV3hKKObhVH .image-shape .label,#mermaid-svg-Yv4SqWV3hKKObhVH .icon-shape .label{text-anchor:middle;}#mermaid-svg-Yv4SqWV3hKKObhVH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Yv4SqWV3hKKObhVH .rough-node .label,#mermaid-svg-Yv4SqWV3hKKObhVH .node .label,#mermaid-svg-Yv4SqWV3hKKObhVH .image-shape .label,#mermaid-svg-Yv4SqWV3hKKObhVH .icon-shape .label{text-align:center;}#mermaid-svg-Yv4SqWV3hKKObhVH .node.clickable{cursor:pointer;}#mermaid-svg-Yv4SqWV3hKKObhVH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Yv4SqWV3hKKObhVH .arrowheadPath{fill:#333333;}#mermaid-svg-Yv4SqWV3hKKObhVH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Yv4SqWV3hKKObhVH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Yv4SqWV3hKKObhVH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yv4SqWV3hKKObhVH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Yv4SqWV3hKKObhVH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yv4SqWV3hKKObhVH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Yv4SqWV3hKKObhVH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Yv4SqWV3hKKObhVH .cluster text{fill:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH .cluster span{color:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Yv4SqWV3hKKObhVH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Yv4SqWV3hKKObhVH rect.text{fill:none;stroke-width:0;}#mermaid-svg-Yv4SqWV3hKKObhVH .icon-shape,#mermaid-svg-Yv4SqWV3hKKObhVH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yv4SqWV3hKKObhVH .icon-shape p,#mermaid-svg-Yv4SqWV3hKKObhVH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Yv4SqWV3hKKObhVH .icon-shape .label rect,#mermaid-svg-Yv4SqWV3hKKObhVH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yv4SqWV3hKKObhVH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Yv4SqWV3hKKObhVH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Yv4SqWV3hKKObhVH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Yv4SqWV3hKKObhVH .ui>*{fill:#d6eaff!important;stroke:#4a90e2!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .ui span{fill:#d6eaff!important;stroke:#4a90e2!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .ui tspan{fill:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .feature>*{fill:#dff5df!important;stroke:#4caf50!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .feature span{fill:#dff5df!important;stroke:#4caf50!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .feature tspan{fill:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .core>*{fill:#fff4cc!important;stroke:#f4b400!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .core span{fill:#fff4cc!important;stroke:#f4b400!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .core tspan{fill:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .infra>*{fill:#ffd6d6!important;stroke:#db4437!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .infra span{fill:#ffd6d6!important;stroke:#db4437!important;color:#000!important;}#mermaid-svg-Yv4SqWV3hKKObhVH .infra tspan{fill:#000!important;} MainActivity
Compose Root
Navigation
Feature Screen
ViewModel
Repository
Database / Network
也可以抽象为:
#mermaid-svg-f2fSZc2Lzxoi3h7v{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f2fSZc2Lzxoi3h7v .error-icon{fill:#552222;}#mermaid-svg-f2fSZc2Lzxoi3h7v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f2fSZc2Lzxoi3h7v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .marker.cross{stroke:#333333;}#mermaid-svg-f2fSZc2Lzxoi3h7v svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f2fSZc2Lzxoi3h7v p{margin:0;}#mermaid-svg-f2fSZc2Lzxoi3h7v .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .cluster-label text{fill:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .cluster-label span{color:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .cluster-label span p{background-color:transparent;}#mermaid-svg-f2fSZc2Lzxoi3h7v .label text,#mermaid-svg-f2fSZc2Lzxoi3h7v span{fill:#333;color:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .node rect,#mermaid-svg-f2fSZc2Lzxoi3h7v .node circle,#mermaid-svg-f2fSZc2Lzxoi3h7v .node ellipse,#mermaid-svg-f2fSZc2Lzxoi3h7v .node polygon,#mermaid-svg-f2fSZc2Lzxoi3h7v .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .rough-node .label text,#mermaid-svg-f2fSZc2Lzxoi3h7v .node .label text,#mermaid-svg-f2fSZc2Lzxoi3h7v .image-shape .label,#mermaid-svg-f2fSZc2Lzxoi3h7v .icon-shape .label{text-anchor:middle;}#mermaid-svg-f2fSZc2Lzxoi3h7v .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .rough-node .label,#mermaid-svg-f2fSZc2Lzxoi3h7v .node .label,#mermaid-svg-f2fSZc2Lzxoi3h7v .image-shape .label,#mermaid-svg-f2fSZc2Lzxoi3h7v .icon-shape .label{text-align:center;}#mermaid-svg-f2fSZc2Lzxoi3h7v .node.clickable{cursor:pointer;}#mermaid-svg-f2fSZc2Lzxoi3h7v .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .arrowheadPath{fill:#333333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f2fSZc2Lzxoi3h7v .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f2fSZc2Lzxoi3h7v .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f2fSZc2Lzxoi3h7v .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f2fSZc2Lzxoi3h7v .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .cluster text{fill:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v .cluster span{color:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f2fSZc2Lzxoi3h7v .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f2fSZc2Lzxoi3h7v rect.text{fill:none;stroke-width:0;}#mermaid-svg-f2fSZc2Lzxoi3h7v .icon-shape,#mermaid-svg-f2fSZc2Lzxoi3h7v .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f2fSZc2Lzxoi3h7v .icon-shape p,#mermaid-svg-f2fSZc2Lzxoi3h7v .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f2fSZc2Lzxoi3h7v .icon-shape .label rect,#mermaid-svg-f2fSZc2Lzxoi3h7v .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f2fSZc2Lzxoi3h7v .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f2fSZc2Lzxoi3h7v .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f2fSZc2Lzxoi3h7v :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-f2fSZc2Lzxoi3h7v .ui>*{fill:#d6eaff!important;stroke:#4a90e2!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .ui span{fill:#d6eaff!important;stroke:#4a90e2!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .ui tspan{fill:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .feature>*{fill:#dff5df!important;stroke:#4caf50!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .feature span{fill:#dff5df!important;stroke:#4caf50!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .feature tspan{fill:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .core>*{fill:#fff4cc!important;stroke:#f4b400!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .core span{fill:#fff4cc!important;stroke:#f4b400!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .core tspan{fill:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .infra>*{fill:#ffd6d6!important;stroke:#db4437!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .infra span{fill:#ffd6d6!important;stroke:#db4437!important;color:#000!important;}#mermaid-svg-f2fSZc2Lzxoi3h7v .infra tspan{fill:#000!important;} UI Layer
Compose / Navigation
Feature Layer
Screen / ViewModel
Core Layer
Repository / Shared Components
Infrastructure Layer
Database / Network
其中:
- Feature 负责业务;
- Core 提供共享能力;
- App 完成组装。
依赖关系
NIA 的依赖关系是单向的:
#mermaid-svg-8xvqVgQQoPQanGpT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8xvqVgQQoPQanGpT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8xvqVgQQoPQanGpT .error-icon{fill:#552222;}#mermaid-svg-8xvqVgQQoPQanGpT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8xvqVgQQoPQanGpT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8xvqVgQQoPQanGpT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8xvqVgQQoPQanGpT .marker.cross{stroke:#333333;}#mermaid-svg-8xvqVgQQoPQanGpT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8xvqVgQQoPQanGpT p{margin:0;}#mermaid-svg-8xvqVgQQoPQanGpT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8xvqVgQQoPQanGpT .cluster-label text{fill:#333;}#mermaid-svg-8xvqVgQQoPQanGpT .cluster-label span{color:#333;}#mermaid-svg-8xvqVgQQoPQanGpT .cluster-label span p{background-color:transparent;}#mermaid-svg-8xvqVgQQoPQanGpT .label text,#mermaid-svg-8xvqVgQQoPQanGpT span{fill:#333;color:#333;}#mermaid-svg-8xvqVgQQoPQanGpT .node rect,#mermaid-svg-8xvqVgQQoPQanGpT .node circle,#mermaid-svg-8xvqVgQQoPQanGpT .node ellipse,#mermaid-svg-8xvqVgQQoPQanGpT .node polygon,#mermaid-svg-8xvqVgQQoPQanGpT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8xvqVgQQoPQanGpT .rough-node .label text,#mermaid-svg-8xvqVgQQoPQanGpT .node .label text,#mermaid-svg-8xvqVgQQoPQanGpT .image-shape .label,#mermaid-svg-8xvqVgQQoPQanGpT .icon-shape .label{text-anchor:middle;}#mermaid-svg-8xvqVgQQoPQanGpT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8xvqVgQQoPQanGpT .rough-node .label,#mermaid-svg-8xvqVgQQoPQanGpT .node .label,#mermaid-svg-8xvqVgQQoPQanGpT .image-shape .label,#mermaid-svg-8xvqVgQQoPQanGpT .icon-shape .label{text-align:center;}#mermaid-svg-8xvqVgQQoPQanGpT .node.clickable{cursor:pointer;}#mermaid-svg-8xvqVgQQoPQanGpT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8xvqVgQQoPQanGpT .arrowheadPath{fill:#333333;}#mermaid-svg-8xvqVgQQoPQanGpT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8xvqVgQQoPQanGpT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8xvqVgQQoPQanGpT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8xvqVgQQoPQanGpT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8xvqVgQQoPQanGpT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8xvqVgQQoPQanGpT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8xvqVgQQoPQanGpT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8xvqVgQQoPQanGpT .cluster text{fill:#333;}#mermaid-svg-8xvqVgQQoPQanGpT .cluster span{color:#333;}#mermaid-svg-8xvqVgQQoPQanGpT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8xvqVgQQoPQanGpT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8xvqVgQQoPQanGpT rect.text{fill:none;stroke-width:0;}#mermaid-svg-8xvqVgQQoPQanGpT .icon-shape,#mermaid-svg-8xvqVgQQoPQanGpT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8xvqVgQQoPQanGpT .icon-shape p,#mermaid-svg-8xvqVgQQoPQanGpT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8xvqVgQQoPQanGpT .icon-shape .label rect,#mermaid-svg-8xvqVgQQoPQanGpT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8xvqVgQQoPQanGpT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8xvqVgQQoPQanGpT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8xvqVgQQoPQanGpT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-8xvqVgQQoPQanGpT .app>*{fill:#d6eaff!important;stroke:#4a90e2!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .app span{fill:#d6eaff!important;stroke:#4a90e2!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .app tspan{fill:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .feature>*{fill:#dff5df!important;stroke:#4caf50!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .feature span{fill:#dff5df!important;stroke:#4caf50!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .feature tspan{fill:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .core>*{fill:#fff4cc!important;stroke:#f4b400!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .core span{fill:#fff4cc!important;stroke:#f4b400!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .core tspan{fill:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .engineering>*{fill:#eeeeee!important;stroke:#888888!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .engineering span{fill:#eeeeee!important;stroke:#888888!important;color:#000!important;}#mermaid-svg-8xvqVgQQoPQanGpT .engineering tspan{fill:#000!important;} app
feature:*
foryou
search
bookmarks
topic
core:*
data
model
database
network
ui
designsystem
sync
benchmark
build-logic
即:
- app 可以依赖 feature;
- feature 可以依赖 core;
- core 不依赖 feature;
- feature 之间尽量避免直接依赖。
这种设计的价值在于:
- 降低耦合;
- 提升可维护性;
- 支持独立演进。
为什么会显得复杂?
因为 NIA 优先考虑的是:
- 长期维护;
- 多人协作;
- 工程稳定性。
而不是:
- 快速开发;
- 最少代码量;
- 新人友好。
对于小型项目而言,这种复杂度可能难以带来足够收益。
但对于大型项目而言,这些边界和约束能够降低系统失控的风险。
我的结论
Now in Android 的项目结构,本质上是一种工程化分层。
它试图回答的问题不是:
"如何最快做出一个 App?"
而是:
"如果这个 App 需要被十几名开发者维护五年,它应该如何组织?"
因此,阅读 NIA 时不应关注:
"这个 Module 我需不需要复制?"
而应该关注:
"这个 Module 在解决什么问题?我的项目是否也存在这个问题?"
好的项目结构并不是最复杂的结构。
而是复杂度与问题规模相匹配的结构。