MVP模式

MVP_Project

一个基于 Android + Kotlin + Jetpack Compose 的示例工程,本文档用于说明如何在当前项目中采用 MVP 架构进行开发,并约束后续代码组织方式。

项目说明

当前工程已经完成基础 Android 应用初始化,主要技术栈如下:

  • Kotlin
  • Android SDK 34
  • Jetpack Compose
  • Material 3
  • Gradle Kotlin DSL

当前入口页面位于 app/src/main/java/com/hk/word/mvp/MainActivity.kt,后续业务页面建议在此基础上逐步迁移为标准的 MVP 分层结构。

为什么采用 MVP

MVP 是一种经典的表现层架构模式,适合在页面逻辑逐渐变复杂时保持代码清晰。

采用 MVP 后可以得到这些收益:

  • View 负责界面展示,减少页面类中混杂的业务逻辑
  • Presenter 负责流程编排与状态控制,便于测试
  • Model 负责数据获取与业务处理,便于复用
  • 页面结构清晰,后续多人协作时更容易维护

MVP 架构说明

在本项目中,推荐将 MVP 理解为以下三层:

1. View

View 负责:

  • 展示 UI
  • 接收用户操作
  • 将用户行为转交给 Presenter
  • 接收 Presenter 回传的数据并刷新界面

在当前项目里,ActivityFragment,以及承载界面展示的 Compose 页面都可以视为 View 层的一部分。

注意:

  • View 不直接处理复杂业务逻辑
  • View 不直接操作网络、数据库等数据源
  • View 只关心"显示什么"和"把事件交给谁"

2. Presenter

Presenter 负责:

  • 接收 View 的事件
  • 调用 Model 获取或处理数据
  • 对结果进行业务编排
  • 将最终结果回传给 View

Presenter 是 MVP 的核心,建议做到:

  • 不依赖 Android 具体控件实现
  • 不编写 UI 细节代码
  • 尽量保持单一职责,一个页面对应一个 Presenter

3. Model

Model 负责:

  • 网络请求
  • 本地数据库访问
  • Repository 封装
  • 数据转换与业务数据处理

Model 不关心界面如何展示,只提供 Presenter 所需的数据能力。

推荐目录结构

建议后续按"功能模块 + MVP 分层"组织代码,例如:

text 复制代码
app/src/main/java/com/hk/word/mvp
├── common/                     # 通用工具、基类、扩展函数
├── data/                       # 数据层
│   ├── local/                  # 本地数据源
│   ├── remote/                 # 远程数据源
│   ├── repository/             # Repository 实现
│   └── model/                  # 数据实体
├── ui/
│   ├── home/                   # 首页模块
│   │   ├── contract/           # HomeContract
│   │   ├── presenter/          # HomePresenter
│   │   ├── view/               # Activity/Fragment/Compose Screen
│   │   └── model/              # Home 模块相关业务模型
│   └── theme/                  # Compose 主题
└── MainActivity.kt

如果后续模块变多,也可以进一步改为:

text 复制代码
ui/
├── login/
├── home/
├── profile/
└── ...

每个模块内部都保持自己的 ContractPresenterView 和必要的数据封装,避免所有 Presenter 混在同一个目录。

推荐开发规范

Contract 先行

建议每个页面先定义一个 Contract,明确 View 与 Presenter 的职责边界。

示例:

kotlin 复制代码
interface HomeContract {
    interface View {
        fun showLoading()
        fun showContent(text: String)
        fun showError(message: String)
    }

    interface Presenter {
        fun loadData()
        fun onDestroy()
    }
}

这样做的好处是:

  • 页面职责明确
  • Presenter 与 View 的交互清晰
  • 更容易进行单元测试和后续重构

Presenter 持有 View 接口

Presenter 不应该依赖具体 ActivityComposable 实现,而应依赖 View 接口。

示例:

kotlin 复制代码
class HomePresenter(
    private var view: HomeContract.View?,
    private val repository: HomeRepository
) : HomeContract.Presenter {

    override fun loadData() {
        view?.showLoading()
        val result = repository.getWelcomeText()
        view?.showContent(result)
    }

    override fun onDestroy() {
        view = null
    }
}

Model 负责数据来源统一

不论数据来自本地还是网络,建议统一通过 Repository 暴露给 Presenter。

示例职责:

  • RemoteDataSource 负责接口调用
  • LocalDataSource 负责本地缓存
  • Repository 负责决定取缓存还是远端

在 Compose 中如何落地 MVP

当前项目使用 Jetpack Compose,因此 View 层可以这样理解:

  • ActivityFragment 负责生命周期管理
  • Composable 负责 UI 渲染
  • Presenter 继续负责业务流程和页面逻辑

一个推荐的实现方式是:

  1. Activity 创建 Presenter
  2. Activity 实现 Contract.View
  3. Activity 将 UI State 传给 Compose 页面
  4. Compose 页面只负责展示和事件回调
  5. 用户点击事件再由 View 转交给 Presenter

例如:

kotlin 复制代码
data class HomeUiState(
    val loading: Boolean = false,
    val content: String = "",
    val error: String? = null
)
kotlin 复制代码
@Composable
fun HomeScreen(
    state: HomeUiState,
    onReload: () -> Unit
) {
    // 这里只负责渲染,不直接处理业务
}

这种方式的核心思想是:

  • Compose 只关心状态驱动的界面渲染
  • Presenter 只关心业务逻辑和状态更新
  • 两者通过 View 层进行衔接

推荐页面交互流程

一个标准的页面调用链建议如下:

text 复制代码
用户操作
  -> View 接收事件
  -> Presenter 处理逻辑
  -> Model / Repository 获取数据
  -> Presenter 整理结果
  -> View 刷新 UI

例如用户点击"加载数据":

  1. Compose 按钮点击后通知 View
  2. View 调用 presenter.loadData()
  3. Presenter 调用 Repository 获取数据
  4. Presenter 根据结果调用 showContent()showError()
  5. View 更新 UiState
  6. Compose 根据最新状态重组界面

当前项目的落地建议

结合当前工程现状,建议按以下顺序演进:

  1. 保留 MainActivity 作为应用入口
  2. 将示例 Greeting() 页面替换为业务页面 HomeScreen()
  3. 新增 home/contract/HomeContract.kt
  4. 新增 home/presenter/HomePresenter.kt
  5. 新增 data/repository/HomeRepository.kt
  6. MainActivity 中实现 HomeContract.View
  7. 使用 UiState 驱动 Compose 页面更新

编码约束建议

为保证 MVP 架构长期稳定,建议遵循以下规范:

  • 不在 View 中直接写网络请求
  • 不在 Presenter 中直接依赖 Android UI 控件
  • 不在 Model 中写界面逻辑
  • 一个页面对应一个 Contract 和一个 Presenter
  • 复杂页面统一使用 UiState 管理显示状态
  • Presenter 在页面销毁时及时释放 View 引用,避免内存泄漏

后续可扩展方向

当项目继续扩展时,可以在 MVP 基础上补充以下能力:

  • Repository + DataSource 的完整数据层抽象
  • 协程与异步任务封装
  • 单元测试覆盖 Presenter
  • 错误码与统一异常处理
  • 页面级状态管理规范

总结

本项目建议采用 MVP + Compose 的方式组织表现层:

  • View 负责展示和事件转发
  • Presenter 负责业务逻辑与流程编排
  • Model 负责数据处理与获取

这样既保留了 MVP 易维护、职责清晰的优点,也能兼容当前基于 Compose 的 Android 项目结构,适合作为后续业务开发的基础架构。

完整代码github仓库 https://github.com/songlige/MVP_Project

相关推荐
xiangxiongfly9152 小时前
Android MMKV
android·mmkv
王莎莎-MinerU3 小时前
MinerU 生态全景:Skills、RAG、MCP、Cursor Rules
人工智能·深度学习·计算机视觉·chatgpt·pdf·软件工程
北漂Zachary3 小时前
PHP3.0:改变Web开发的里程碑
android·php·laravel
fundroid4 小时前
Google 发布 Android Skill & Android CLI:大幅提升 Android Agent 能力
android·agent·cli·skill
seabirdssss4 小时前
Flutter 开发环境配置
android·windows·flutter·adb
CoderJia程序员甲4 小时前
GitHub 热榜项目 - 日榜(2026-04-17)
ai·大模型·llm·github·ai教程
TheRouter4 小时前
AI 不会消灭软件工程,它只会消灭低维的软件工程
人工智能·软件工程
阿里嘎多学长5 小时前
2026-04-17 GitHub 热点项目精选
开发语言·程序员·github·代码托管
Evavava啊5 小时前
Android WebView 中 React useState 更新失效问题
android·前端·react.js·渲染