学习Android(三)

简介

在上一章节,我们对Android中使用的基础UI有了大致的了解和使用,这使得我们开发复杂界面有了基础,但是一个好的项目不能只有好界面,还需要有好的项目架构管理,尤其是在团队开发中,一个有条理的项目架构,可以避免很多麻烦,那么本章节我们将讲讲常用的几种软件架构模式,以及为什么要使用这些架构模式

1. 为什么要使用 MVC、MVP、MVVM等架构模式

  • 代码解耦(Separation of Concerns)

    好处

    • 职责清晰 :每个模块(ModelViewPresenter/ViewModel)只做自己的事情,避免代码混乱。
    • 减少"上帝类" :防止Activity/Fragment 承担过多逻辑,变得臃肿难维护。
    • 降低修改风险:修改一个模块时,不会轻易影响其他模块。
  • 提高可测试性(Testability)

    好处

    • 单元测试更容易 :业务逻辑(ModelViewPresenter/ViewModel)不依赖Android框架,可以直接用JUnit测试。
    • UI测试更稳定:View只负责渲染,测试时可以用Mock数据。
  • 提升可维护性(Maintainability)

    好处

    • 代码可读性高:不同功能模块分开,新人更容易理解。
    • 便于团队协作:前端(UI)和后端(逻辑)开发可以并行工作。
    • 减少Bug传播:一个模块的Bug不会轻易扩散到整个应用。
  • 更好的扩展性(Scalability)

好处

  • 轻松替换组件:比如更换网络库(Retrofit → Ktor),只需修改Model层。

  • 适应需求变化:新增功能时,可以在现有架构上扩展,而不是重写代码。

  • 支持响应式编程(Reactive Programming)

    (MVVM特有优势)

    • 数据驱动UI:LiveData/Flow 自动通知View更新,避免手动刷新。
    • 减少回调地狱:用观察者模式替代Callback嵌套。
  • 适应不同平台(跨平台兼容)

    好处

    • 业务逻辑可复用:Model和Presenter/ViewModel可以移植到其他平台(如KMM、Desktop)。
    • 前端灵活切换:同一套逻辑支持不同UI(Android、iOS、Web)。

    适用场景建议

    1. 小型项目 → MVC(快速开发)

    2. 需要测试/中型项目 → MVP

    3. 大型复杂应用 → MVVM(推荐Jetpack组合)

    4. 跨平台需求 → MVVM + KMM

    优势 MVC MVP MVVM
    代码解耦
    可测试性
    可维护性
    扩展性
    响应式编程支持
    跨平台潜力

    架构的核心目标是 让代码更健壮、更易维护,选择时需权衡项目规模和团队熟悉度。

2. MVC

  • Model

    Model 是对应用数据及业务逻辑的封装,负责获取、存储和处理数据。通常以纯 Java/Kotlin 类或与持久化框架(如 Room)和网络库(如 Retrofit)结合的实体类形式存在

  • View

    View 负责呈现 Model 提供的数据和接受用户交互,通常由布局 XML 文件和各类 Android 控件(TextView、Button、RecyclerView 等)组成。View 不直接包含业务逻辑,只负责渲染和事件转发,真正的逻辑处理由 Controller 完成

  • Controller

    Controller 充当 Model 与 View 之间的协调者,接收来自 View 的用户输入,调用 Model 更新数据后,再通知 View 刷新显示。在 Android 中,Controller 通常由 Activity 或 Fragment 实现,直接操作 View 并调用 Model 接口。

  • 在Androd中如何实现MVC

    • Activity 作为 Controller 与 View

      在典型的 Android MVC 实现中,Activity 同时承担 Controller 和部分 View 的职责:它监听界面事件(如按钮点击),调用 Model 方法,并在数据变更后更新界面组件

    • Model 类

      Model 类通常是一个简单的 POJO,包含属性和方法来获取或修改数据,不直接依赖 Android SDK。实际项目中,可结合 Android 架构指南中推荐的 Repository 模式,将不同数据源抽象为统一接口,交由 Model 负责管理

    示例代码以下示例展示了一个简单的登录流程如何应用 MVC 模式:

    XML 布局仅负责定义输入框和按钮,属于 View 层

    xml 复制代码
    <!-- res/layout/activity_login.xml -->
    <LinearLayout ...>
        <EditText
            android:id="@+id/etUsername"
            ... />
        <EditText
            android:id="@+id/etPassword"
            ... />
        <Button
            android:id="@+id/btnLogin"
            android:text="登录"
            ... />
    </LinearLayout>

    LoginActivity 既监听界面事件(View) ,又调用 UserModel(Model),充当 Controller

    kotlin 复制代码
    // Model:处理用户验证
    class UserModel {
        fun authenticate(user: String, pass: String): Boolean {
            // 模拟校验逻辑
            return (user == "admin" && pass == "1234")
        }
    }
    
    // Controller + View:Activity
    class LoginActivity : AppCompatActivity() {
        private val model = UserModel()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_login)
    
            btnLogin.setOnClickListener {
                val success = model.authenticate(etUsername.text.toString(), etPassword.text.toString())
                if (success) {
                    Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "用户名或密码错误", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
    • MVC项目包结构树:

      xml 复制代码
      com.example.app
       ├── model       // 存放所有数据实体、仓库、业务逻辑类
       ├── view        // 存放布局对应的 Activity/Fragment、Adapter
       └── controller  // 存放各模块的 Controller 类,协调 View 与 Model

      model 包:如 User.ktProductRepository.kt 等。

      view 包:如 LoginActivity.ktMainFragment.kt 等。

      controller 包:如 LoginController.ktMainController.kt 等。

    • 示例包结构树:

      xml 复制代码
      com.example.app
       ├── model
       │    ├── User.kt
       │    └── UserRepository.kt
       ├── view
       │    ├── LoginActivity.kt
       │    └── MainActivity.kt
       └── controller
            ├── LoginController.kt
            └── MainController.kt

      UserRepository 中封装网络/数据库请求。

      LoginController 中处理登录流程,调用 UserRepository 并将结果返回给 LoginActivity 更新 UI。

    通过上述示例,不难发现MVC的优缺点

    • 优点
      • 关注点分离,使界面、逻辑和数据解耦,便于多人协作和独立测试。
      • 实现简单,对小型或入门项目友好,无需引入额外框架
    • 缺点
      • Activity 承担过多职责,往往既是 View 又是 Controller,导致代码耦合度高。
      • 不利于单元测试,因为逻辑代码混杂在 Activity 中,难以脱离 Android 环境独立测试

    尽管 MVC 模式可作为学习架构模式的入门示例,但在实际中大型 Android 项目里,建议采用 MVP 或 MVVM 等更成熟的分层方案,以获得更好的可维护性和测试性。理解 MVC 的原理有助于更深入地掌握其他高级架构模式的设计思想和实现细节。

3. MVP

  • Model

    封装数据和业务规则,通常为纯 Java/Kotlin 类,负责网络请求、数据库操作或本地缓存,与 Android SDK 解耦,易于单元测试

  • View

    由布局 XML 与 Activity/Fragment 构成,仅负责渲染数据并将用户操作(点击、输入等)转发给 Presenter,不包含业务逻辑

  • Presenter

    持有 View 接口引用,接收用户操作回调,调用 Model 获取或更新数据,并在数据返回后调用 View 接口方法更新界面,是业务逻辑的真正聚合者

    kotlin 复制代码
    // LoginContract.kt
    interface LoginContract {
        interface View {
            fun showLoading()
            fun showError(msg: String)
            fun navigateToHome()
        }
        interface Presenter {
            fun login(username: String, password: String)
            fun detachView()
        }
    }
    
    // LoginPresenter.kt
    class LoginPresenter(
        private var view: LoginContract.View?,
        private val userRepository: UserRepository
    ) : LoginContract.Presenter {
        override fun login(username: String, password: String) {
            view?.showLoading()
            userRepository.login(username, password) { success ->
                if (success) view?.navigateToHome()
                else view?.showError("登录失败")
            }
        }
        override fun detachView() { view = null }
    }
    
    // LoginActivity.kt
    class LoginActivity : AppCompatActivity(), LoginContract.View {
        private lateinit var presenter: LoginContract.Presenter
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_login)
            presenter = LoginPresenter(this, UserRepositoryImpl())
            loginButton.setOnClickListener {
                presenter.login(usernameEdit.text.toString(), passwordEdit.text.toString())
            }
        }
        override fun showLoading() = progressBar.show()
        override fun showError(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
        override fun navigateToHome() { /* 跳转 */ }
        override fun onDestroy() {
            super.onDestroy()
            presenter.detachView()
        }
    }
    • MVP 项目包结构树:

      xml 复制代码
      com.example.app
       ├── data         // 网络请求、数据库、缓存等实现细节
       ├── domain       // 业务模型、用例(use case)接口
       ├── presentation // UI 层,包括所有 View/Presenter 实现
       ├── common       // 应用通用组件,如网络、DI、日志等
       └── util         // 工具类、扩展函数等
      • data:负责与外部数据源交互(Retrofit、Room 等),并向 domain 层提供数据。

      • domain:定义业务模型和用例接口,解耦 data 与 presentation;纯 Kotlin/Java,无任何 Android 依赖。

      • presentation:包含所有界面模块,每个功能点独立子包,内含 View、Presenter、Contract 接口等。

        • presentation 包下,针对每个"屏幕"或业务模块创建子包,例如:

          cpp 复制代码
          presentation
           ├── login
           │    ├── LoginActivity.kt    // View 实现
           │    ├── LoginContract.kt    // View/Presenter 接口
           │    └── LoginPresenter.kt   // Presenter 实现
           ├── settings
           │    ├── SettingsFragment.kt
           │    ├── SettingsContract.kt
           │    └── SettingsPresenter.kt
           └── profile
                ├── ProfileActivity.kt
                ├── ProfileContract.kt
                └── ProfilePresenter.kt

          这样,当你需要新建一个"订单"模块,只需在 presentation 下新增 order 包,并在其中添加对应文件;同一模块内的类也都集中,易于维护

4. MVVM

  • Model:封装应用数据和业务逻辑(网络请求、数据库操作等),与 Android 视图层解耦,只做数据处理与存储。

  • View:Activity/Fragment + XML 布局,只负责渲染数据和接受用户交互,不包含业务逻辑。

  • ViewModel:位于 View 与 Model 之间,持有界面状态(如表单输入、列表数据),并暴露可观察的数据源(LiveData、StateFlow 等),同时调用 Model 层获取或修改数据

关键组件与依赖

  1. build.gradle 中引入 Jetpack 组件:

    gradle 复制代码
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.x.x"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.x.x"
    implementation "androidx.databinding:databinding-runtime:7.x.x"

    这样即可启用 ViewModel、LiveData 与 DataBinding 功能。

  2. 在模块级 build.gradle 中开启 DataBinding:

    gradle 复制代码
    android {
      buildFeatures {
        dataBinding true
      }
    }

    允许在布局 XML 顶层使用 <layout> 标签和直接绑定数据

  3. 示例

变成下图即可

具体如何在这个架构模式下开发这里不进行细说,在日后的项目开发中会针对这个模式进行一个项目开发学习,到时候自然会懂的,当然我们也要对 Jetpack 的组件有对应的了解,在之后都会一一详细讲解的。

  • MVVM 项目结构树

    cpp 复制代码
    com.example.app
     ├── data         // Retrofit 接口、Room 实体、Repository 实现等(Model 层)&#8203;:contentReference[oaicite:4]{index=4}  
     ├── domain       // 可选:用例(UseCase)或业务接口,进一步解耦 data 与 presentation&#8203;:contentReference[oaicite:5]{index=5}  
     ├── ui           // Activity、Fragment、Adapter 等视图组件(View 层)&#8203;:contentReference[oaicite:6]{index=6}  
     ├── viewmodel    // 各界面的 ViewModel 实现,持有 LiveData 并调用 Repository&#8203;:contentReference[oaicite:7]{index=7}  
     ├── di           // Hilt/Dagger 的 Module、Component 定义,负责依赖注入配置&#8203;:contentReference[oaicite:8]{index=8}  
     └── common       // 网络配置、日志工具、扩展函数等通用代码&#8203;:contentReference[oaicite:9]{index=9}  

    在顶层 uiviewmodel 包下,按业务功能(如登录、个人中心、设置等)创建子包,将该功能的 View、ViewModel、Contract(若有)和相关资源聚合

    cpp 复制代码
    ui
     └── login
          ├── LoginActivity.kt      // View 实现  
          ├── LoginFragment.kt      // Fragment 实现(如采用多模块导航)  
          └── login_layout.xml      // 布局文件  
    viewmodel
     └── login
          └── LoginViewModel.kt     // 处理登录逻辑、暴露 LiveData  
    data
     └── login
          ├── LoginRepository.kt    // 与后端/数据库交互  
          └── LoginApiService.kt    // Retrofit 接口  

以上即为MVC、MVP、MVVM三种架构模式的介绍,具体项目结构树因人而异,没有说有统一的模版,只要在能保证各种架构核心思想不变并且项目阅读简单的都是好模版。

相关推荐
来来走走21 分钟前
Flutter SharedPreferences存储数据基本使用
android·flutter
安卓开发者1 小时前
Android模块化架构深度解析:从设计到实践
android·架构
雨白2 小时前
HTTP协议详解(二):深入理解Header与Body
android·http
阿豪元代码2 小时前
深入理解 SurfaceFlinger —— 如何调试 SurfaceFlinger
android
阿豪元代码2 小时前
深入理解 SurfaceFlinger —— 概述
android
CV资深专家4 小时前
Launcher3启动
android
stevenzqzq4 小时前
glide缓存策略和缓存命中
android·缓存·glide
雅雅姐5 小时前
Android 16 的用户和用户组定义
android
没有了遇见5 小时前
Android ConstraintLayout 之ConstraintSet
android
余辉zmh5 小时前
【MySQL基础篇】:MySQL索引——提升数据库查询性能的关键
android·数据库·mysql