1. 标准Gradle项目结构(基础)

当你创建一个新的Android项目时,Android Studio会默认生成一个基于Gradle的结构。这是所有结构的基础。

MyApp/
├── app/                    # 主模块(Module)
│   ├── build.gradle       # 模块级别的Gradle构建脚本(最重要)
│   ├── src/
│   │   ├── main/          # 主源代码目录
│   │   │   ├── java/      # Java/Kotlin 源代码
│   │   │   │   └── com/example/myapp/  # 你的包名
│   │   │   ├── res/       # 资源文件(layout, drawable, values等)
│   │   │   └── AndroidManifest.xml
│   │   └── androidTest/   # Android仪器化测试
│   │   └── test/          # 本地单元测试(运行在JVM上)
│   └── proguard-rules.pro
├── gradle/
├── build.gradle           # 项目级别的Gradle构建脚本
└── settings.gradle        # 定义项目包含哪些模块

关键点:

  • src/main/java 是核心代码所在地。问题就在于如何组织这个包下的代码。
  • src/testsrc/androidTest 的目录结构应该与 main 下的结构一一镜像,这样便于找到对应类的测试。

2. “按层分包” (By Layer) - 传统但不推荐

早期Android项目常用这种方式,按代码的“类型”或“层级”划分包。

com.example.myapp/
├── activity/
│   ├── MainActivity.java
│   └── DetailActivity.java
├── adapter/
│   └── MyListAdapter.java
├── fragment/
│   └── HomeFragment.java
├── model/
│   └── User.java
├── service/
│   └── ApiService.java
└── utils/
    └── DateUtils.java

缺点:

  • 难以扩展和维护:当项目变大时,每个包下的文件数量会爆炸式增长,找一个文件非常困难。
  • 功能内聚性差:一个完整的“用户详情”功能可能分散在 activity, fragment, adapter, model 等多个包中,无法一眼看出哪些代码属于同一个功能。
  • 不利于模块化:这种结构很难抽离出一个独立的功能模块。

3. “按功能/模块分包” (By Feature) - 当前最佳实践

这是目前最受推崇的结构。它将所有属于同一功能的代码
这种结构的核心思想是:将属于同一业务功能的所有代码聚集在一起,而不是将同一类型的代码(Activity, Fragment, ViewModel, Repository等)放在同一个包下。顶层辅以一些共享的、与功能无关的通用包。

以下是一个典型且高度推荐的结构:

<你的包名,如 com.company.app>/
│
├── di/                           # 【共享】依赖注入模块 (Dagger Hilt/Koin)
│   ├── AppComponent.java
│   ├── AppModule.java
│   └── ViewModelModule.java
│
├── data/                         # 【共享】数据层
│   ├── local/                    # 本地数据源 (数据库、文件、Preferences)
│   │   ├── dao/                  # Room Dao 接口
│   │   └── entity/               # Room 实体类
│   ├── remote/                   # 远程数据源 (网络)
│   │   ├── api/                  # Retrofit 接口
│   │   ├── dto/                  # 网络传输数据对象 (Data Transfer Object)
│   │   └── interceptor/          # OkHttp 拦截器
│   └── repository/               # 仓库实现类
│       ├── UserRepositoryImpl.java
│       └── SettingsRepositoryImpl.java
│
├── domain/                       # 【共享】领域层 (可选但强烈推荐)
│   ├── model/                    # 业务核心模型/实体 (Entity)
│   ├── repository/               # 仓库接口定义
│   └── usecase/                  # 用例/交互器 (Use Cases)
│       ├── GetUserUseCase.java
│       └── LoginUserUseCase.java
│
├── core/                         # 【共享】核心工具和基础组件
│   ├── util/                     # 通用工具类 (避免上帝类,按功能细分)
│   ├── extensions/               # Kotlin 扩展函数
│   ├── base/                     # 基类 (BaseActivity, BaseFragment, BaseViewModel)
│   └── common/                   # 常量、配置等
│
├── feature_a/                    # 【功能模块A】例如: auth, home, profile, settings
│   ├── ui/                       # UI 相关组件
│   │   ├── FeatureAActivity.java
│   │   ├── FeatureAFragment.java
│   │   ├── FeatureAViewModel.java # ViewModel
│   │   └── adapter/              # 功能专用的适配器
│   │   └── view/                 # 自定义View
│   └── model/                    # 功能专用的呈现模型 (Presentation Model)
│       └── FeatureAUiState.java  # 或 Vo (Value Object)
│
├── feature_b/                    # 【功能模块B】
│   └── ui/
│       └── ...
│
└── app/                          # 应用的入口 (可选的包)
    └── MainActivity.java         # 通常是最初的启动Activity

优点:

    1. 高内聚,低耦合 (High Cohesion, Low Coupling)
    • 内聚性:所有与“用户资料”相关的代码(Activity, ViewModel, 状态类)都在 feature_profile 包里。修改一个功能时,你只需要关注一个目录。
    • 耦合性:功能模块之间不直接依赖,而是通过顶层共享的 domain(接口)和 data 层进行通信,使得模块独立性更强。
  1. 极大的可维护性和可读性

    • 新成员加入项目,可以很快地根据功能名称找到相关代码。
    • 导航代码库变得非常直观,不再需要在天花乱坠的 adapter, fragment 包中大海捞针。
  2. 为模块化铺平道路

    • 这是该结构最大的优势之一。当项目变得庞大时,你可以轻松地将每个 feature_* 包提取到一个独立的 Android Library Module 中。
    • 模块化可以显著改善构建时间,实现团队并行开发,并严格强制模块间的边界。从这种结构开始,迁移到完全模块化的成本最低。
  3. 与现代架构完美契合

    • 这种结构天然地适配 MVVMMVI 等模式。每个功能包内的 ui 子包就对应了 MVVM 中的 View 和 ViewModel。

4. 结合架构组件(MVVM)的清晰结构

上面的“按功能分包”完美契合了流行的 MVVM(Model-View-ViewModel) 架构。

在一个功能包内(例如 feature_user),结构可以是这样:

feature_user/
├── UserActivity.java          # View层 (Activity/Fragment)
├── UserViewModel.java         # ViewModel层 (继承自ViewModel)
├── UserRepository.java        # 仓库接口(在domain层或本地定义)
├── ui/                        # 可选的更细化的UI包
│   ├── UserAdapter.java
│   └── UserItemDecoration.java
└── model/                     # 该功能专用的模型(Vo)
    └── UserVo.java

各层职责:

  • View (Activity/Fragment): 处理UI显示和用户输入,向ViewModel请求数据。
  • ViewModel: 为View准备数据,处理业务逻辑,调用UseCase或Repository。
  • Repository: 数据仓库,协调多个数据源(本地、远程),是唯一的数据入口。
  • Model: 实体类,代表核心数据。

5. "按功能"与传统“按层分包”的对比

特性 按层分包 (By Layer) 按功能分包 (By Feature)
结构 ui/, model/, adapter/ feature_a/, feature_b/, data/
可查找性 差。需记得类类型才能查找。 优秀。根据功能名直接定位。
内聚性 低。相关代码分散各处。 。功能代码集中管理。
可维护性 项目变大后极难维护。 易于维护和扩展
模块化准备 很难重构为模块。 极易迁移到模块化。
团队协作 容易在通用包上产生冲突。 冲突少。每人负责不同功能模块。

6. 总结与最终建议

最合理、最现代的项目结构是:

以“按功能/模块分包”为主,顶层辅以data, domain, di等共享模块,并结合MVVM等清晰架构进行组织。

给你的建议:

  1. 从按功能分包开始:即使是新项目,也立即采用这种结构。不要等到项目复杂了再重构。
  2. 功能划分粒度:功能的划分要合理。例如 login(登录)、home(首页)、profile(个人资料)、settings(设置)都是典型的功能模块。
  3. 提取共享层:将 data, domain (包含Repository接口和UseCase), di 放在顶层,供所有功能模块使用。
  4. 保持测试结构镜像:为每个功能类编写测试,并确保测试文件位于 testandroidTest 中对应的包路径下。
  5. 命名清晰:功能包的名字要能清晰表达其业务含义。
  6. 为模块化做准备:在代码组织上就假设每个功能未来都是一个独立模块,避免产生跨功能的直接依赖。使用 Dagger HiltKoin 等依赖注入框架来管理依赖关系,这对模块化至关重要。

总而言之,放弃传统的按层分包,拥抱按功能分包 + 分层架构,是现代 Android 开发中最重要的项目结构最佳实践。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐