Jorgen's blog Jorgen's blog
首页
  • 平台架构
  • 混合式开发记录
  • 推送服务
  • 数据分析
  • 实时调度
  • 架构思想

    • 分布式
  • 编程框架工具

    • 编程语言
    • 框架
    • 开发工具
  • 数据存储与处理

    • 数据库
    • 大数据
  • 消息、缓存与搜索

    • 消息队列
    • 搜索与日志分析
  • 前端与跨端开发

    • 前端技术
    • Android
  • 系统与运维

    • 操作系统
    • 容器化与 DevOps
  • 物联网与安全

    • 通信协议
    • 安全
    • 云平台
newland
  • 关于我
  • 终身学习
  • 关于时间的感悟
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

jorgen

Love it, make mistakes, learn, keep grinding.
首页
  • 平台架构
  • 混合式开发记录
  • 推送服务
  • 数据分析
  • 实时调度
  • 架构思想

    • 分布式
  • 编程框架工具

    • 编程语言
    • 框架
    • 开发工具
  • 数据存储与处理

    • 数据库
    • 大数据
  • 消息、缓存与搜索

    • 消息队列
    • 搜索与日志分析
  • 前端与跨端开发

    • 前端技术
    • Android
  • 系统与运维

    • 操作系统
    • 容器化与 DevOps
  • 物联网与安全

    • 通信协议
    • 安全
    • 云平台
newland
  • 关于我
  • 终身学习
  • 关于时间的感悟
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Capacitor浅析
  • ADB调试
  • mlkit
  • face detection
  • Android中的Kotlin协程:告别回调地狱
  • Android性能优化实践与技巧
  • Android Jetpack Compose入门:构建现代UI的全新方式
  • Android Jetpack Compose入门与实践 - 构建现代Android UI
  • Android Jetpack Compose入门指南 - 构建现代UI的全新方式
  • Android Jetpack与架构组件-构建现代化应用
  • Android Jetpack架构组件:ViewModel与LiveData实战指南
  • Android Jetpack组件:构建现代Android应用的核心利器
  • Android Jetpack组件详解:构建现代化应用的核心
  • Android数据绑定与MVVM架构-构建现代化应用
  • Android架构组件:构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基础
  • Android架构设计之MVVM模式实战指南
    • 前言
    • MVVM架构概述
      • 什么是MVVM?
      • MVVM的优势
    • MVVM在Android中的实践
      • 基本结构
      • 实战案例:用户信息展示
      • 1. 定义Model
      • 2. 创建Repository
      • 3. 创建UseCase
      • 4. 创建ViewModel
      • 5. 创建UI
    • Jetpack组件与MVVM的结合
      • 1. ViewModel
      • 2. LiveData
      • 3. Data Binding
      • 4. Room数据库
    • MVVM最佳实践
      • 1. 单一职责原则
      • 2. 依赖注入
      • 3. 错误处理
      • 4. 测试策略
    • 常见问题与解决方案
      • 1. ViewModel持有Context导致内存泄漏
      • 2. 过度使用LiveData
      • 3. ViewModel与View的强耦合
    • 结语
  • 拥抱未来:Android Jetpack Compose入门指南
  • Android权限管理完全指南-从基础到实践
  • Android测试指南-从单元测试到UI测试的实践之路
  • Android依赖注入实战:从Dagger到Hilt的进化之路
  • Android网络编程完全指南-从HTTP到Retrofit的实践之路
  • Android数据持久化完全指南-从SharedPreferences到Room数据库
  • Android多线程与并发处理完全指南-从Thread到协程的进阶之路
  • Android应用打包与发布全流程指南-从签名到上架的实战之路
  • Android安全完全指南-从基础防护到高级加密的实战之路
  • android
Jorgen
2023-11-15
目录

Android架构设计之MVVM模式实战指南

# 前言

作为一名Android开发者,你是否曾经面对过这样的困境:随着项目规模不断扩大,代码变得越来越难以维护,业务逻辑和UI逻辑混杂在一起,修改一个功能需要牵一发而动全身?🤔

我曾在多个项目中经历过这样的"代码噩梦",直到接触到了MVVM架构模式,才真正体会到了代码结构化带来的便利。今天,我想和大家分享如何在Android开发中实践MVVM架构模式,以及它如何帮助我们构建更清晰、更易维护的应用程序。

提示

MVVM(Model-View-ViewModel)是一种架构设计模式,它将应用程序分为三个主要组件:模型(Model)、视图(View)和视图模型(ViewModel)。这种模式有助于分离关注点,提高代码的可测试性和可维护性。

# MVVM架构概述

# 什么是MVVM?

MVVM架构模式最初由微软工程师John Gossman于2005年提出,后来在Android开发中得到了广泛应用。它通过引入ViewModel层,实现了视图(View)和业务逻辑(Model)之间的完全分离。

  • Model:负责处理数据和业务逻辑,通常包括数据模型、数据源、API调用等。
  • View:负责UI展示和用户交互,在Android中通常指Activity、Fragment、Layout等。
  • ViewModel:作为View和Model之间的桥梁,处理View的业务逻辑,但不直接引用View。

# MVVM的优势

使用MVVM架构模式,我们可以获得以下好处:

  1. 关注点分离:UI逻辑和业务逻辑被清晰地分离,使代码更易于理解和维护。
  2. 可测试性:ViewModel不依赖View,可以轻松进行单元测试。
  3. 生命周期感知:ViewModel能够感知组件的生命周期,避免内存泄漏。
  4. 团队协作:不同的开发者可以同时工作在View和ViewModel上,减少冲突。

# MVVM在Android中的实践

# 基本结构

一个典型的Android MVVM应用结构如下:

app/
├── data/
│   ├── local/
│   │   └── dao/          // 数据访问对象
│   ├── remote/
│   │   └── api/          // API接口
│   └── repository/       // 仓库模式
├── di/                    // 依赖注入模块
├── domain/
│   ├── model/            // 领域模型
│   ├── repository/       // 仓库接口
│   └── usecase/          // 用例
├── presentation/
│   ├── ui/               // UI组件
│   │   ├── activity/     // Activity
│   │   ├── fragment/     // Fragment
│   │   └── adapter/      // 适配器
│   └── viewmodel/        // ViewModel
└── util/                  // 工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 实战案例:用户信息展示

让我们通过一个简单的用户信息展示应用来实践MVVM架构。

# 1. 定义Model

首先,我们定义用户数据模型:

// domain/model/User.kt
data class User(
    val id: String,
    val name: String,
    val email: String,
    val avatarUrl: String
)
1
2
3
4
5
6
7

# 2. 创建Repository

Repository作为数据层,负责从不同数据源获取数据:

// data/repository/UserRepository.kt
class UserRepository @Inject constructor(
    private val apiService: ApiService,
    private val userDao: UserDao
) {
    
    suspend fun getUser(userId: String): User {
        // 1. 先从数据库检查缓存
        var user = userDao.getUserById(userId)
        
        // 2. 如果没有缓存或缓存过期,从网络获取
        if (user == null) {
            user = apiService.getUser(userId)
            userDao.insertUser(user)
        }
        
        return user
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3. 创建UseCase

UseCase定义应用程序的业务规则:

// domain/usecase/GetUserUseCase.kt
class GetUserUseCase @Inject constructor(
    private val userRepository: UserRepository
) {
    suspend operator fun invoke(userId: String): Result<User> {
        return try {
            Result.success(userRepository.getUser(userId))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 4. 创建ViewModel

ViewModel处理UI相关的业务逻辑:

// presentation/viewmodel/UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(
    private val getUserUseCase: GetUserUseCase
) : ViewModel() {
    
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user
    
    private val _loading = MutableLiveData<Boolean>()
    val loading: LiveData<Boolean> = _loading
    
    private val _error = MutableLiveData<String>()
    val error: LiveData<String> = _error
    
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _loading.value = true
            when (val result = getUserUseCase(userId)) {
                is Result.Success -> {
                    _user.value = result.data
                }
                is Result.Failure -> {
                    _error.value = result.exception.message ?: "Unknown error"
                }
            }
            _loading.value = false
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 5. 创建UI

最后,我们在Activity或Fragment中展示数据:

// presentation/ui/UserActivity.kt
class UserActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityUserBinding
    private val viewModel: UserViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityUserBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        val userId = intent.getStringExtra("USER_ID") ?: ""
        
        // 观察ViewModel中的数据变化
        viewModel.user.observe(this) { user ->
            binding.nameTextView.text = user.name
            binding.emailTextView.text = user.email
            Glide.with(this)
                .load(user.avatarUrl)
                .into(binding.avatarImageView)
        }
        
        viewModel.loading.observe(this) { isLoading ->
            binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
        }
        
        viewModel.error.observe(this) { errorMessage ->
            Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show()
        }
        
        // 加载用户数据
        viewModel.loadUser(userId)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# Jetpack组件与MVVM的结合

Android Jetpack组件与MVVM架构模式是天作之合,以下是几个关键组件:

# 1. ViewModel

ViewModel类专门用于存储和管理与UI相关的数据,在配置更改(如屏幕旋转)时不会销毁。

class MyViewModel : ViewModel() {
    private val _items = MutableLiveData<List<Item>>()
    val items: LiveData<List<Item>> = _items
    
    fun loadItems() {
        // 从数据源加载数据
    }
}
1
2
3
4
5
6
7
8

# 2. LiveData

LiveData是一种可观察的数据持有者类,遵循观察者模式。当数据变化时,它会通知观察者(通常是UI组件)。

// 在ViewModel中
private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user

// 在Activity/Fragment中
viewModel.user.observe(this) { user ->
    // 更新UI
}
1
2
3
4
5
6
7
8

# 3. Data Binding

Data Binding库允许我们直接在XML布局文件中绑定UI组件到数据源:

<!-- activity_user.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.UserViewModel" />
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        
        <TextView
            android:id="@+id/nameTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.user.name}" />
            
        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}" />
            
    </LinearLayout>
</layout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 4. Room数据库

Room持久化库提供了本地数据库功能,可以轻松地将数据存储在设备上:

// 数据实体
@Entity
data class User(
    @PrimaryKey val id: String,
    val name: String,
    val email: String
)

// DAO接口
@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: String): User?
    
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(user: User)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# MVVM最佳实践

# 1. 单一职责原则

每个类都应该只有一个责任。例如:

  • ViewModel只负责处理UI相关的业务逻辑
  • Repository只负责数据获取
  • UseCase只包含一个业务用例

# 2. 依赖注入

使用Hilt或Dagger进行依赖注入,减少类之间的耦合:

@HiltViewModel
class UserViewModel @Inject constructor(
    private val getUserUseCase: GetUserUseCase
) : ViewModel()
1
2
3
4

# 3. 错误处理

实现统一的错误处理机制:

sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Failure(val exception: Exception) : Result<Nothing>()
}

// 在ViewModel中使用
private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error

// 在UI中观察
viewModel.error.observe(this) { errorMessage ->
    showErrorSnackbar(errorMessage)
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4. 测试策略

MVVM架构使测试变得更加容易:

// ViewModel测试
class UserViewModelTest {
    private lateinit var viewModel: UserViewModel
    private val getUserUseCase: MockKMockScope<GetUserUseCase> = mockk()
    
    @Before
    fun setup() {
        viewModel = UserViewModel(getUserUseCase)
    }
    
    @Test
    fun `loadUser should update user LiveData when successful`() = runTest {
        // Given
        val user = User("1", "John Doe", "john@example.com")
        coEvery { getUserUseCase(any()) } returns Result.success(user)
        
        // When
        viewModel.loadUser("1")
        
        // Then
        assertEquals(user, viewModel.user.value)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 常见问题与解决方案

# 1. ViewModel持有Context导致内存泄漏

问题:在ViewModel中直接使用Activity或Fragment的Context会导致内存泄漏。

解决方案:使用Application Context:

class MyViewModel(application: Application) : AndroidViewModel(application) {
    private val context = application.applicationContext
    
    // 使用context而不是activity/fragment的context
}
1
2
3
4
5

# 2. 过度使用LiveData

问题:将所有数据都包装在LiveData中,导致不必要的观察者注册。

解决方案:根据需求选择合适的数据类型:

// 对于简单状态,可以使用StateFlow
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()

// 对于一次性事件,可以使用SingleLiveEvent
private val _event = SingleLiveEvent<String>()
val event: LiveData<String> = _event
1
2
3
4
5
6
7

# 3. ViewModel与View的强耦合

问题:ViewModel直接引用View,违反了MVVM的原则。

解决方案:通过LiveData或Flow进行通信:

// ViewModel中
private val _showSnackbar = MutableLiveData<String>()
val showSnackbar: LiveData<String> = _showSnackbar

// View中
viewModel.showSnackbar.observe(this) { message ->
    Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show()
}
1
2
3
4
5
6
7
8

# 结语

MVVM架构模式为Android开发提供了一种清晰、可维护的代码组织方式。通过将UI逻辑与业务逻辑分离,我们能够构建更加健壮、可测试的应用程序。

当然,架构选择应该根据项目需求和团队特点来决定,没有放之四海而皆准的"最佳架构"。但MVVM结合Jetpack组件确实为大多数Android项目提供了一个很好的起点。

记住,好的架构不仅仅是关于代码组织,更是关于团队协作和长期维护。选择适合你团队的架构模式,并坚持下去,你会发现随着项目的发展,代码质量会越来越高。

希望这篇指南能帮助你在Android开发中更好地实践MVVM架构模式!如果你有任何问题或建议,欢迎在评论区留言交流。👋

"架构不是关于工具,而是关于思维模式。"

#架构设计#MVVM#Jetpack
上次更新: 2026/01/28, 10:42:53
Android架构组件与Jetpack-构建现代化应用的基础
拥抱未来:Android Jetpack Compose入门指南

← Android架构组件与Jetpack-构建现代化应用的基础 拥抱未来:Android Jetpack Compose入门指南→

最近更新
01
LLM
01-30
02
intro
01-30
03
intro
01-30
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Jorgen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式