Appearance
工程目录结构
项目初创
一个 Android 项目创建后,会生成一套默认的目录结构,它也是 Android 工程体系与 Gradle 构建系统的基础。
典型结构如下:
my-android-project/
├── app/
│ ├── build/
│ ├── libs/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ ├── kotlin/
│ │ │ ├── res/
│ │ │ └── AndroidManifest.xml
│ │ ├── test/
│ │ └── androidTest/
│ ├── build.gradle
│ └── proguard-rules.pro
├── build.gradle
├── settings.gradle
└── gradle/
└── wrapper/在实际开发中,大多数目录结构都是围绕 main 与 res 展开的,其中最核心的部分通常是:
- 包名目录
- 资源目录
其中 res 目录本身具备固定规则,而项目中最容易随着业务增长不断演化的,往往是包名目录结构。
res 资源目录
基本轮廓如下,文件和文件夹新增都是有固定规则的。“一些目录下的资源文件需要有命名规范后续会提到”
res/
├── drawable/
├── drawable-xxhdpi/
├── layout/
├── values/
│ ├── strings.xml
│ ├── colors.xml
│ └── styles.xml
├── mipmap/
└── raw/谷歌官方指南:《资源目录存放指引》
因此,本文重点讨论的是:如何合理组织包名目录结构。
包名目录
在 main 目录下,如果项目是 Java 和 kotlin 混开,那么可以:
- 分别存放在
java和kotlin目录中 - 统一
java目录。
当项目规模较大、两种语言文件较多时,更推荐按语言拆分:
main/java/
└──com/example/myapp
main/kotlin
└──com/example/myapp两个目录下的包结构应保持一致。
🚫 重要注意事项
不建议将 Java 文件放在 kotlin 目录下。
虽然部分 Gradle 或 Kotlin 插件版本能够正常编译,但在不同 AGP、IDE 或构建配置下,可能出现索引异常、类缺失或构建行为不一致的问题。
推荐保持:
- Java 文件放在 java/
- Kotlin 文件放在 kotlin/
统一目录职责,避免潜在兼容性问题。
基础目录结构
包名目录的层级设计,会直接影响项目的:
- 可维护性
- 可扩展性
- 阅读效率
- 团队协作成本
一个常见的基础结构如下:
com/example/myapp/
│
├── app/ # Application、全局初始化、全局变量
├── base/ # 存放各个组件的通用基类(取名 base/basic 均可)
│ ├── activity/
│ ├── fragment/
│ ├── dialog/
│ ├── holder/
│ └── ...
├── consts/ # 全局常量
├── model/ # 数据模型
├── ui/
│ ├── activity/
│ ├── fragment/
│ ├── dialog/
│ ├── adapter/
│ │ └── holder/
│ ├── widget/
│ └── ...
├── utils/ # 工具类
└── ...即使是中小型项目,也通常建议具备基础目录分层。
随着业务扩展,再逐步细化。
注意
避免在项目初期过度拆分目录。
目录结构的目标是降低理解成本,而不是追求层级数量。
如果一个目录长期只包含少量文件,或拆分后无法带来明显收益,应优先保持简单结构。
避免将 utils 逐渐演变成“杂项目录”。
工具类应具备明确职责与边界,不相关逻辑不要持续堆积在同一个目录中。
当工具类开始明显增长时,应进一步拆分:
常见目录扩展
除了基础结构外,实际项目中还会根据:
- Android 组件
- 第三方框架
- 架构设计
- 业务规模
继续扩展目录层级。
组件目录
Android 四大组件 除 Activity 通常放在 ui 下外,其余组件一般会独立管理。
- Service
com/example/myapp/
├──app/
├──.../
└──services/ # 管理各个服务组件- BroadcastReceiver
com/example/myapp/
├──app/
├──.../
└──receivers/ # 管理各个广播组件(全局和本地广播都在此存放)- ContentProvider
com/example/myapp/
├──app/
├──.../
└──providers/ # 管理各个内容提供器组件第三方框架
当第三方框架开始产生:
- 配置类
- 扩展类
- 适配层
- 自定义实现
通常会单独建立一级目录。
Glide 图片加载框架
当 Glide 仅作为简单图片加载库使用时,不一定需要单独目录。
但如果已经开始:
- 自定义 Module
- 自定义 Loader
- 编译期配置
- 网络适配
则建议:
com/example/myapp/
└──glide/ # 存放glide框架相关的配置类用于统一管理 Glide 相关配置。
网络请求库
当网络层逐渐复杂后,通常会统一抽象:
com/example/myapp/
└──http/
├── api/
├── interceptor/
├── server/
├── response/
└── ...当网络相关能力开始集中后,像 Glide 网络配置这类内容,也可以归入网络层统一管理。
数据库
例如 Room:
com/example/myapp/
└──db/
├──dao/ # dao 层分类
├──entity/ # 相关实体类
└──.../依赖注入
例如:
- Hilt
- Dagger
- Koin
通常会建立:
com/example/myapp/
└──di/ # 依赖注入其他目录
串口通信
com/example/myapp/
└──serial/
├── config/ # 串口配置
├── manager/ # 串口管理
├── listener/ # 数据监听
├── protocol/ # 通信协议
└── parser/ # 数据解析自定义注解
com/example/myapp/
└──annotation/ # 存放自定义的注解类Kotlin 扩展函数
com/example/myapp/
└──extensions/ # 存放扩展函数文件任务类
com/example/myapp/
└──tasks/ # 存放各个任务类目录扩展原则
通过以上目录扩展可以发现,新增一级目录通常遵循以下原则。
1. 单一职责原则
每一级目录应聚焦单一职责。
避免将不相关逻辑混合存放。
2. 功能聚合原则
目录应围绕:
- 功能模块
- 技术能力
- 业务领域
进行聚合。
3. 可维护性和可扩展性
目录结构应具备扩展空间。
新增功能时,能够自然融入,而不是不断推翻原有结构。
例如:
service/
model/
http/都具备天然扩展能力。
4. 避免横向膨胀
当一级目录数量开始明显增多时,应考虑:
- 模块聚合
- 业务分层
- feature 化
而不是无限新增一级目录。
避免最终出现:
/user
/order
/pay
/chat
/video
/home
/profile导致根目录持续膨胀。
架构目录扩展
目录结构最终应服务于架构设计。
不同架构下,目录划分也会有所区别。
MVVM 分层结构
这个架构的详细内容可具体参见官方文档:https://developer.android.com/topic/architecture?hl=zh-cn
基于 Repository 的 MVVM 分层是目前较常见的一种组织方式,它会将:
- UI
- 数据源
- 数据仓库
- ViewModel
进行职责拆分。
典型结构如下:
com/example/myapp/
├── model/
├── sources/
│ ├── local/ # 本地 api 接口和实现类
│ ├── remote/ # 远端 api 接口和实现类
│ └── repository/ # 存放数据仓库接口和实现类
├── viewmodel/
└── ui/这种结构的核心目标是:
- ViewModel 更轻量
- 数据来源更清晰
- 业务职责更明确
注意
不要让 ViewModel 同时承担:
- UI 控制
- 业务逻辑
- 数据处理
- 网络请求
否则本质上只是换了一种形式继续耦合。
模块化目录结构
当项目规模继续扩大后,可以进一步演化为模块化结构。
模块化之后,目录划分会更加偏向:
- 业务域
- 能力域
- 基础设施层
而不仅仅是简单的功能分类。
总结
工程目录结构没有绝对标准。
真正合理的目录结构,应当:
- 清晰
- 稳定
- 易维护
- 易扩展
目录的意义,不是追求 “分类越多越专业”,而是让项目能够随着业务增长依然保持可读性与可控性。
演进
在实际开发中,应根据:
- 项目规模
- 团队人数
- 架构复杂度
- 业务特点
不断调整与演进目录结构,不要一味的机械套用固定模板。