随着 Android 应用复杂性的增加,采用良好的架构模式变得越来越重要。MVVM(Model-View-ViewModel) 是一种流行的架构模式,旨在将应用的 UI 逻辑、数据和业务逻辑分离,使代码更易于维护、测试和扩展。本章节将深入讲解 MVVM 架构模式的原理、组件、Jetpack 组件(如 ViewModel 和 LiveData)的使用,以及如何在 Android 项目中应用 MVVM 架构。
MVVM 的起源:
MVVM 的核心思想:
MVVM 的优点:
MVVM 架构模式主要由以下三个部分组成:
Model(模型):
View(视图):
ViewModel(视图模型):
Jetpack 提供了多个组件,可以帮助我们更好地实现 MVVM 架构:
ViewModel:
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> get() = _data
fun loadData() {
// 模拟网络请求
_data.value = "Hello, MVVM!"
}
}
LiveData:
class MyActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
viewModel.data.observe(this) { data ->
// 更新 UI
findViewById<TextView>(R.id.textView).text = data
}
viewModel.loadData()
}
}
Data Binding:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.MyViewModel" />
data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.data}" />
layout>
Room:
@Entity
data class User(
@PrimaryKey val id: Int,
val name: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAllUsers(): LiveData<List<User>>
}
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val db = Room.databaseBuilder(
application,
AppDatabase::class.java, "database-name"
).build()
val users: LiveData<List<User>> = db.userDao().getAllUsers()
}
在前面的章节中,我们介绍了 MVVM 架构模式的基本概念和组成部分。接下来,我们将详细介绍如何在 Android 项目中实现 MVVM 架构,包括 ViewModel 的创建与使用、LiveData 的观察、数据绑定以及与 Repository 模式的结合。
ViewModel 是 MVVM 架构的核心组件,负责存储和管理 UI 相关的数据和业务逻辑。ViewModel 不直接引用 View,因此可以独立于 View 进行单元测试。
步骤:
ViewModel
的类。LiveData
或 StateFlow
暴露数据给 View。示例:
// UserRepository.kt
class UserRepository {
fun getUsers(): List<User> {
// 模拟网络请求或数据库查询
return listOf(User(1, "Alice"), User(2, "Bob"))
}
}
// UserViewModel.kt
class UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> get() = _users
fun loadUsers() {
// 模拟数据加载
val userList = repository.getUsers()
_users.value = userList
}
}
// User.kt
data class User(val id: Int, val name: String)
View 负责 UI 的展示和用户交互。在 Android 中,View 通常是 Activity 或 Fragment。在 Jetpack Compose 中,View 可以是 Composable 函数。
步骤:
ViewModelProvider
或 ViewModel
的构造函数注入 ViewModel。示例:
// UserActivity.kt
class UserActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
// 创建 ViewModel 实例
viewModel = ViewModelProvider(this, ViewModelFactory(UserRepository()))
.get(UserViewModel::class.java)
// 观察 LiveData 数据
viewModel.users.observe(this) { users ->
// 更新 UI,例如使用 RecyclerView 显示用户列表
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.adapter = UserAdapter(users)
}
// 加载数据
viewModel.loadUsers()
}
}
// UserAdapter.kt
class UserAdapter(private val users: List<User>) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false)
return UserViewHolder(view)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = users[position]
holder.bind(user)
}
override fun getItemCount(): Int = users.size
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
itemView.findViewById<TextView>(R.id.textViewName).text = user.name
}
}
}
Data Binding 允许将 UI 组件直接绑定到 ViewModel 的数据,减少样板代码,提高代码可读性。
步骤:
build.gradle
文件中启用 Data Binding。android {
...
buildFeatures {
dataBinding true
}
}
标签包裹 UI 组件,并定义 ViewModel 变量。<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.UserViewModel" />
data>
<RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="@{viewModel.users}" />
layout>
class UserActivity : AppCompatActivity() {
private lateinit var viewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityUserBinding = DataBindingUtil.setContentView(this, R.layout.activity_user)
viewModel = ViewModelProvider(this, ViewModelFactory(UserRepository()))
.get(UserViewModel::class.java)
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel.loadUsers()
}
}
示例:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.UserViewModel" />
data>
<RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="@{viewModel.users}" />
layout>
// UserViewModel.kt
class UserViewModel(private val repository: UserRepository) : ViewModel() {
val users: LiveData<List<User>> = MutableLiveData()
fun loadUsers() {
val userList = repository.getUsers()
(users as MutableLiveData).value = userList
}
}
Repository 模式 是 MVVM 架构的重要组成部分,用于抽象数据源,提供统一的接口给 ViewModel。Repository 模式可以有效地管理数据来源,包括网络请求、数据库、文件存储等,使 ViewModel 更加专注于 UI 逻辑和数据处理,而无需关心数据的具体来源。
以下是一个典型的 Repository 模式实现步骤:
定义数据模型:
User
。data class User(val id: Int, val name: String)
定义数据源接口:
UserDataSource
,用于获取用户数据。interface UserDataSource {
suspend fun getUsers(): List<User>
}
实现具体的数据源:
RemoteUserDataSource
,通过 API 获取用户数据。class RemoteUserDataSource(private val apiService: ApiService) : UserDataSource {
override suspend fun getUsers(): List<User> {
return apiService.fetchUsers()
}
}
LocalUserDataSource
,从数据库获取用户数据。class LocalUserDataSource(private val userDao: UserDao) : UserDataSource {
override suspend fun getUsers(): List<User> {
return userDao.getAllUsers()
}
}
实现 Repository 类:
UserRepository
类,负责管理数据源。class UserRepository(private val remoteDataSource: RemoteUserDataSource, private val localDataSource: LocalUserDataSource) {
suspend fun getUsers(forceRefresh: Boolean = false): List<User> {
if (forceRefresh) {
// 从网络获取数据
val users = remoteDataSource.getUsers()
// 保存到数据库
localDataSource.saveUsers(users)
return users
} else {
// 先从数据库获取数据
val users = localDataSource.getUsers()
if (users.isNotEmpty()) {
return users
} else {
// 如果数据库为空,则从网络获取数据
val usersFromNetwork = remoteDataSource.getUsers()
// 保存到数据库
localDataSource.saveUsers(usersFromNetwork)
return usersFromNetwork
}
}
}
}
在 ViewModel 中使用 Repository:
UserRepository
实例,并调用其方法获取数据。class UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> get() = _users
fun loadUsers(forceRefresh: Boolean = false) {
viewModelScope.launch {
try {
val users = repository.getUsers(forceRefresh)
_users.postValue(users)
} catch (e: Exception) {
// 处理异常,例如显示错误信息
}
}
}
}
依赖注入:
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
@Provides
@Singleton
fun provideUserRepository(
remoteDataSource: RemoteUserDataSource,
localDataSource: LocalUserDataSource
): UserRepository {
return UserRepository(remoteDataSource, localDataSource)
}
@Provides
@Singleton
fun provideRemoteUserDataSource(apiService: ApiService): RemoteUserDataSource {
return RemoteUserDataSource(apiService)
}
@Provides
@Singleton
fun provideLocalUserDataSource(userDao: UserDao): LocalUserDataSource {
return LocalUserDataSource(userDao)
}
}
通过使用 Repository 模式,ViewModel 可以专注于 UI 逻辑和数据处理,而不需要关心数据的具体来源和实现细节。这种分离使得代码更加清晰、可维护,并且易于测试。
前腾讯电子签的前端负责人,现 whentimes tech CTO,专注于前端技术的大咖一枚!一路走来,从小屏到大屏,从 Web 到移动,什么前端难题都见过。热衷于用技术打磨产品,带领团队把复杂的事情做到极简,体验做到极致。喜欢探索新技术,也爱分享一些实战经验,帮助大家少走弯路!
温馨提示:可搜老码小张公号联系导师