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实战指南
    • 前言
    • ViewModel:生命周期感知的数据容器
      • 什么是ViewModel?
      • 基本使用
      • ViewModel的高级特性
      • 1. ViewModelProvider与工厂
      • 2. viewModelScope
    • LiveData:生命周期感知的数据持有者
      • 什么是LiveData?
      • 基本使用
      • LiveData的高级特性
      • 1. Transformations
    • ViewModel与LiveData的协同工作
      • 实战案例:用户列表展示
      • 1. 创建Repository层
      • 2. 创建ViewModel
      • 3. 在Activity中使用
    • 最佳实践与注意事项
      • 1. 不要在ViewModel中持有View或Activity的引用
      • 2. 使用协程简化异步操作
      • 3. 考虑使用StateFlow替代LiveData
    • 结语
  • Android Jetpack组件:构建现代Android应用的核心利器
  • Android Jetpack组件详解:构建现代化应用的核心
  • Android数据绑定与MVVM架构-构建现代化应用
  • Android架构组件:构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基础
  • Android架构设计之MVVM模式实战指南
  • 拥抱未来: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 Jetpack架构组件:ViewModel与LiveData实战指南

# 前言

在Android开发的世界里,我们常常面临各种挑战:配置变更导致的Activity重建、数据管理与UI更新的一致性、后台任务与UI线程的协调等等。这些问题不仅增加了开发的复杂性,还可能导致难以追踪的bug。

幸运的是,Google推出的Jetpack架构组件为我们提供了一套强大的工具,帮助我们构建健壮、可维护且响应式的Android应用。其中,ViewModel和LiveData是两个最核心、最常用的组件,它们共同解决了上述挑战。

今天,我们就来深入探讨这两个组件,并通过实际案例展示它们如何协同工作,提升我们的开发体验和代码质量。

# ViewModel:生命周期感知的数据容器

# 什么是ViewModel?

ViewModel是Jetpack架构组件的一部分,它被设计用来存储和管理与UI相关的数据。它的最大特点是能够在配置变更(如屏幕旋转)时存活,从而避免数据丢失。

传统的做法中,当屏幕旋转时,Activity会被销毁并重建,我们通常需要通过onSaveInstanceState()来保存和恢复数据。这种方式不仅繁琐,而且对于复杂数据结构来说实现起来相当困难。

而ViewModel的生命周期与Activity或Fragment不同,它只会在Activity或Fragment真正被销毁时(如用户导航离开)才会被销毁,而不是在配置变更时。

# 基本使用

首先,我们需要添加依赖:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
1

然后,创建一个简单的ViewModel:

class UserProfileViewModel : ViewModel() {
    private val _userName = MutableLiveData<String>()
    val userName: LiveData<String> = _userName
    
    private val _userAge = MutableLiveData<Int>()
    val userAge: LiveData<Int> = _userAge
    
    fun loadUser(userId: String) {
        // 模拟从网络或数据库加载数据
        viewModelScope.launch {
            delay(1000) // 模拟网络延迟
            _userName.value = "John Doe"
            _userAge.value = 30
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在Activity或Fragment中使用:

class UserProfileActivity : AppCompatActivity() {
    private lateinit var viewModel: UserProfileViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user_profile)
        
        viewModel = ViewModelProvider(this).get(UserProfileViewModel::class.java)
        
        // 观察LiveData的变化
        viewModel.userName.observe(this) { name ->
            textViewName.text = name
        }
        
        viewModel.userAge.observe(this) { age ->
            textViewAge.text = "$age years old"
        }
        
        // 触发数据加载
        buttonLoad.setOnClickListener {
            viewModel.loadUser("user123")
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# ViewModel的高级特性

# 1. ViewModelProvider与工厂

当我们需要向ViewModel传递参数时,可以使用ViewModelProvider.Factory:

class UserProfileViewModel(private val userId: String) : ViewModel() {
    // ...
}

class UserProfileViewModelFactory(private val userId: String) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserProfileViewModel::class.java)) {
            return UserProfileViewModel(userId) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

// 在Activity中使用
val factory = UserProfileViewModelFactory("user123")
viewModel = ViewModelProvider(this, factory).get(UserProfileViewModel::class.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2. viewModelScope

每个ViewModel实例都有一个viewModelScope,它是CoroutineScope的一个实例,与ViewModel的生命周期绑定。当ViewModel被销毁时,这个作用域中的所有协程都会被自动取消:

class UserProfileViewModel : ViewModel() {
    fun loadData() {
        viewModelScope.launch {
            // 这个协程会在ViewModel被销毁时自动取消
            val data = withContext(Dispatchers.IO) {
                // 模拟网络请求
                delay(1000)
                "User Data"
            }
            // 更新UI
            _data.value = data
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# LiveData:生命周期感知的数据持有者

# 什么是LiveData?

LiveData是一个可观察的数据持有者类,与普通的可观察对象不同,LiveData是生命周期感知的。这意味着它会根据观察者的生命周期状态(如Activity的状态)来决定是否通知数据变化。

这种特性确保了UI只在处于活跃状态时才会更新,从而避免了常见的内存泄漏和崩溃问题。

# 基本使用

首先,添加依赖:

implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
1

创建LiveData:

class UserViewModel : ViewModel() {
    // 使用MutableLiveData作为私有属性
    private val _user = MutableLiveData<User>()
    // 提供只读的LiveData给外部
    val user: LiveData<User> = _user
    
    fun loadUser(userId: String) {
        viewModelScope.launch {
            val user = withContext(Dispatchers.IO) {
                // 模拟网络请求
                delay(1000)
                User(userId, "John Doe", 30)
            }
            _user.value = user
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在Activity或Fragment中观察:

class UserProfileActivity : AppCompatActivity() {
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user_profile)
        
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        
        // 观察LiveData
        viewModel.user.observe(this) { user ->
            textViewName.text = user.name
            textViewAge.text = "${user.age} years old"
        }
        
        buttonLoad.setOnClickListener {
            viewModel.loadUser("user123")
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# LiveData的高级特性

# 1. Transformations

LiveData提供了map和switchMap转换方法,让我们能够基于一个LiveData创建另一个LiveData:

class UserViewModel : ViewModel() {
    private val _userId = MutableLiveData<String>()
    val user: LiveData<User> = _userId.switchMap { userId ->
        // 当userId变化时,触发新的数据加载
        UserRepository.getUser(userId)
    }
    
    fun setUserId(userId: String) {
        _userId.value = userId
    }
}

// 使用map转换
class UserProfileViewModel : ViewModel() {
    private val _user = MutableLiveData<User>()
    val userName: LiveData<String> = _user.map { user ->
        "${user.firstName} ${user.lastName}"
    }
    
    fun loadUser(userId: String) {
        // ...
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# ViewModel与LiveData的协同工作

单独使用ViewModel或LiveData已经能解决很多问题,但它们真正强大的地方在于协同工作。下面我们来看一个完整的例子:

# 实战案例:用户列表展示

# 1. 创建Repository层

class UserRepository {
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> = _users
    
    suspend fun refreshUsers() {
        // 模拟网络请求
        val users = withContext(Dispatchers.IO) {
            delay(1000)
            listOf(
                User("1", "Alice", 25),
                User("2", "Bob", 30),
                User("3", "Charlie", 35)
            )
        }
        _users.postValue(users)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2. 创建ViewModel

class UserListViewModel : ViewModel() {
    private val repository = UserRepository()
    
    // 使用Transformations.map转换数据
    val users: LiveData<List<User>> = Transformations.map(repository.users) { users ->
        // 可以在这里对数据进行处理
        users.sortedBy { it.name }
    }
    
    val isLoading = MutableLiveData<Boolean>()
    val errorMessage = MutableLiveData<String>()
    
    fun loadUsers() {
        isLoading.value = true
        errorMessage.value = null
        
        viewModelScope.launch {
            try {
                repository.refreshUsers()
            } catch (e: Exception) {
                errorMessage.value = "Failed to load users: ${e.message}"
            } finally {
                isLoading.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

# 3. 在Activity中使用

class UserListActivity : AppCompatActivity() {
    private lateinit var viewModel: UserListViewModel
    private lateinit var adapter: UserAdapter
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user_list)
        
        setupRecyclerView()
        
        viewModel = ViewModelProvider(this).get(UserListViewModel::class.java)
        
        // 观察用户列表
        viewModel.users.observe(this) { users ->
            adapter.submitList(users)
        }
        
        // 观察加载状态
        viewModel.isLoading.observe(this) { isLoading ->
            progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
        }
        
        // 初始加载数据
        viewModel.loadUsers()
        
        // 下拉刷新
        swipeRefreshLayout.setOnRefreshListener {
            viewModel.loadUsers()
        }
    }
}
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

# 最佳实践与注意事项

# 1. 不要在ViewModel中持有View或Activity的引用

ViewModel应该与UI层解耦,不应该持有Activity、Fragment或View的引用。这可以通过使用LiveData或其他可观察对象来实现。

# 2. 使用协程简化异步操作

结合viewModelScope和协程,可以大大简化异步代码:

class UserViewModel : ViewModel() {
    private val repository = UserRepository()
    
    val users: LiveData<List<User>> = MutableLiveData()
    val isLoading = MutableLiveData<Boolean>()
    
    fun loadUsers() {
        viewModelScope.launch {
            isLoading.value = true
            try {
                val userList = repository.getUsers()
                (users as MutableLiveData).value = userList
            } catch (e: Exception) {
                // 处理错误
            } finally {
                isLoading.value = false
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 3. 考虑使用StateFlow替代LiveData

在Kotlin协程的世界里,StateFlow是LiveData的现代替代品。它提供了更好的协程支持和更灵活的功能:

class UserViewModel : ViewModel() {
    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()
    
    fun loadUsers() {
        viewModelScope.launch {
            val users = withContext(Dispatchers.IO) {
                // 从网络或数据库获取用户
                repository.getUsers()
            }
            _users.value = users
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 结语

ViewModel和LiveData是Android Jetpack架构组件中的基石,它们共同为我们提供了一种构建健壮、可维护且响应式Android应用的方式。通过生命周期感知的特性,它们帮助我们解决了许多常见的开发难题,如配置变更导致的数据丢失、UI与数据的一致性等。

在现代Android开发中,掌握这些组件的使用已经成为了必备技能。随着Android开发的不断演进,我们也应该关注StateFlow等现代替代方案,但ViewModel和LiveData的核心思想和设计模式仍然值得我们深入理解和应用。

希望这篇文章能帮助你更好地理解和使用这些强大的工具。如果你有任何问题或建议,欢迎在评论区留言交流!

"优秀的架构不是关于如何组织代码,而是关于如何组织思想。" — Robert C. Martin

#Android#Jetpack#ViewModel#LiveData#架构组件
上次更新: 2026/01/28, 14:21:05
Android Jetpack与架构组件-构建现代化应用
Android Jetpack组件:构建现代Android应用的核心利器

← Android Jetpack与架构组件-构建现代化应用 Android Jetpack组件:构建现代Android应用的核心利器→

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