Trae搭建Android 开发中 MVVM 架构,使用指南
我理解你需要一份关于如何搭建 Android MVVM 架构的详细指南。下面我将为你进行详细的解释。
一、MVVM 架构简介
MVVM(Model-View-ViewModel)是一种软件架构模式,将应用分为三个核心组件:
- Model:负责数据处理(网络请求、数据库操作等),包含数据模型和业务逻辑。
- View:负责 UI 展示与用户交互(Activity/Fragment、XML 布局),通过观察 ViewModel 获取数据。
- ViewModel:作为 View 与 Model 的桥梁,存储 UI 相关数据,处理 View 触发的业务逻辑,且生命周期独立于 View。
核心优势:
- 低耦合:View 与 Model 分离,互不依赖。
- 数据驱动:ViewModel 通知 View 数据变化,View 只需更新 UI。
- 生命周期安全:ViewModel 生命周期长于 View,可避免内存泄漏。
- 可测试性:各组件独立,便于单元测试。
二、核心依赖库
MVVM 架构依赖 Android 官方组件库(Jetpack)实现,需在
build.gradle 中添加以下依赖(以 AndroidX 为例):// 基础组件 implementation "androidx.core:core-ktx:1.12.0" implementation "androidx.appcompat:appcompat:1.6.1" implementation "com.google.android.material:material:1.11.0" // ViewModel + LiveData(数据观察) implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0" // Lifecycle(生命周期感知) implementation "androidx.lifecycle:lifecycle-common-java8:2.7.0" // Retrofit(网络请求,替代 Volley) implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0" // Gson 解析 implementation "com.squareup.okhttp3:okhttp:4.11.0" // OkHttp 支持 // Room(本地数据库,可选) implementation "androidx.room:room-runtime:2.6.1" kapt "androidx.room:room-compiler:2.6.1" implementation "androidx.room:room-ktx:2.6.1" // Kotlin 扩展 // Coroutines(异步处理,替代 RxJava) implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" // Glide(图片加载,可选) implementation "com.github.bumptech.glide:glide:4.16.0" kapt "com.github.bumptech.glide:compiler:4.16.0"
说明:
- 推荐使用 Kotlin 语言,配合 Coroutines 实现异步操作,代码更简洁。
- LiveData 用于 ViewModel 向 View 发送数据更新通知(生命周期感知)。
- Retrofit + OkHttp 负责网络请求,Room 负责本地数据持久化。
三、项目结构设计
按 MVVM 分层思想,推荐以下项目结构(模块化清晰):
com.example.mvvmapp ├── data // Model 层:数据处理相关 │ ├── api // 网络请求(Retrofit 接口、服务端模型) │ │ ├── ApiService.kt // Retrofit 接口定义 │ │ └── model // 服务端返回数据模型(如 UserResponse.kt) │ ├── db // 本地数据库(Room 实体、DAO) │ │ ├── AppDatabase.kt // Room 数据库实例 │ │ ├── dao // 数据访问对象(如 UserDao.kt) │ │ └── entity // 数据库实体(如 UserEntity.kt) │ └── repository // 仓库:统一数据来源(网络 + 本地) │ └── UserRepository.kt ├── ui // View 层:UI 相关 │ ├── activity // 活动(如 LoginActivity.kt) │ ├── fragment // 碎片(如 HomeFragment.kt) │ └── adapter // 列表适配器(如 UserAdapter.kt) ├── viewmodel // ViewModel 层:业务逻辑与数据存储 │ └── UserViewModel.kt ├── util // 工具类(网络判断、Toast 工具等) │ └── NetworkUtil.kt └── App.kt // 应用全局上下文
核心原则:
- 上层依赖下层,下层不依赖上层(如 View 依赖 ViewModel,ViewModel 依赖 Repository,Repository 依赖 Model)。
- 数据流动:View → ViewModel → Repository → Model(网络 / 数据库)→ Repository → ViewModel → View。
四、各层详细实现步骤
1. Model 层:数据处理
Model 层负责数据的获取与存储,核心是 Repository(仓库模式),统一管理网络和本地数据。
(1)网络请求(Retrofit)
- 步骤 1:定义 API 接口(
ApiService.kt)
import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Query interface ApiService { // 示例:获取用户列表 @GET("users") suspend fun getUsers(@Query("page") page: Int): Response<UserResponse> }
- 步骤 2:创建 Retrofit 实例(
RetrofitClient.kt)
import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory object RetrofitClient { private const val BASE_URL = "https://api.example.com/" val apiService: ApiService by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiService::class.java) } }
- 步骤 3:定义数据模型(
UserResponse.kt)
data class UserResponse( val data: List<User>, val page: Int, val total: Int ) data class User( val id: Int, val name: String, val email: String )
(2)本地数据库(Room)
- 步骤 1:定义实体类(
UserEntity.kt)
import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "users") data class UserEntity( @PrimaryKey val id: Int, val name: String, val email: String )
- 步骤 2:定义 DAO(
UserDao.kt)
import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import kotlinx.coroutines.flow.Flow @Dao interface UserDao { @Insert suspend fun insertAll(users: List<UserEntity>) @Query("SELECT * FROM users") fun getLocalUsers(): Flow<List<UserEntity>> // Flow 用于观察数据变化 }
骤 3:创建数据库实例(AppDatabase.kt)
import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import android.content.Context @Database(entities = [UserEntity::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao companion object { // 单例模式 @Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: Context): AppDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "app_database" ).build() INSTANCE = instance instance } } } }
(3)仓库(Repository)
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map class UserRepository( private val apiService: ApiService, private val userDao: UserDao ) { // 获取用户列表:优先网络,失败则读取本地 suspend fun getUsers(page: Int): Flow<Result<List<User>>> = flow { emit(Result.Loading) // 发送加载状态 try { val response = apiService.getUsers(page) if (response.isSuccessful) { val users = response.body()?.data ?: emptyList() // 网络数据存入本地 userDao.insertAll(users.map { it.toEntity() }) emit(Result.Success(users)) } else { emit(Result.Error("网络请求失败")) } } catch (e: Exception) { // 网络异常,读取本地数据 val localUsers = userDao.getLocalUsers().first() // 阻塞获取一次 if (localUsers.isNotEmpty()) { emit(Result.Success(localUsers.map { it.toModel() })) } else { emit(Result.Error(e.message ?: "未知错误")) } } } // User 转 UserEntity(扩展函数) private fun User.toEntity(): UserEntity { return UserEntity(id = id, name = name, email = email) } // UserEntity 转 User private fun UserEntity.toModel(): User { return User(id = id, name = name, email = email) } // 数据状态密封类 sealed class Result<out T> { object Loading : Result<Nothing>() data class Success<out T>(val data: T) : Result<T>() data class Error(val message: String) : Result<Nothing>() } }
2. ViewModel 层:业务逻辑
ViewModel 持有 Repository 实例,处理 View 触发的业务逻辑,通过 LiveData/StateFlow 向 View 发送数据。
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch class UserViewModel( private val userRepository: UserRepository ) : ViewModel() { // 私有 MutableStateFlow(可修改),对外暴露不可修改的 StateFlow private val _usersState = MutableStateFlow<UserRepository.Result<List<User>>>(UserRepository.Result.Loading) val usersState: StateFlow<UserRepository.Result<List<User>>> = _usersState // 加载用户列表 fun loadUsers(page: Int) { viewModelScope.launch { // 自动绑定 ViewModel 生命周期,避免内存泄漏 userRepository.getUsers(page).collect { result -> _usersState.value = result // 更新状态 } } } }
说明:
- 使用
StateFlow替代LiveData(Kotlin 推荐,支持协程,更灵活)。 viewModelScope是 ViewModel 专用协程作用域,当 ViewModel 销毁时自动取消协程。
3. View 层:UI 展示
View 层(Activity/Fragment)观察 ViewModel 的数据变化,更新 UI 并处理用户交互。
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.example.mvvmapp.databinding.ActivityUserBinding import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class UserActivity : AppCompatActivity() { private lateinit var binding: ActivityUserBinding private lateinit var userViewModel: UserViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityUserBinding.inflate(layoutInflater) // View Binding setContentView(binding.root) // 初始化 ViewModel(通过 ViewModelProvider 确保生命周期安全) val userRepository = UserRepository( RetrofitClient.apiService, AppDatabase.getDatabase(this).userDao() ) userViewModel = ViewModelProvider(this) { UserViewModel(userRepository) }[UserViewModel::class.java] // 观察 ViewModel 数据变化 observeUsersState() // 加载数据(用户交互触发,如按钮点击) binding.btnLoad.setOnClickListener { userViewModel.loadUsers(page = 1) } } private fun observeUsersState() { lifecycleScope.launch { // 绑定 Activity 生命周期 userViewModel.usersState.collect { result -> when (result) { is UserRepository.Result.Loading -> { binding.progressBar.visibility = View.VISIBLE binding.tvError.visibility = View.GONE } is UserRepository.Result.Success -> { binding.progressBar.visibility = View.GONE binding.rvUsers.adapter = UserAdapter(result.data) // 更新列表 } is UserRepository.Result.Error -> { binding.progressBar.visibility = View.GONE binding.tvError.text = result.message binding.tvError.visibility = View.VISIBLE } } } } } }
(2)布局文件(activity_user.xml)
使用 View Binding 简化视图操作:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <Button android:id="@+id/btnLoad" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加载用户列表" /> <ProgressBar android:id="@+id/progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" /> <TextView android:id="@+id/tvError" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/red" android:visibility="gone" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rvUsers" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
(3)列表适配器(UserAdapter.kt)
import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.example.mvvmapp.databinding.ItemUserBinding class UserAdapter : ListAdapter<User, UserAdapter.UserViewHolder>(DiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder { val binding = ItemUserBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return UserViewHolder(binding) } override fun onBindViewHolder(holder: UserViewHolder, position: Int) { val user = getItem(position) holder.binding.tvName.text = user.name holder.binding.tvEmail.text = user.email } class UserViewHolder(val binding: ItemUserBinding) : RecyclerView.ViewHolder(binding.root) // DiffUtil:优化列表更新(只刷新变化项) class DiffCallback : DiffUtil.ItemCallback<User>() { override fun areItemsTheSame(oldItem: User, newItem: User): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: User, newItem: User): Boolean { return oldItem == newItem } } }
五、关键注意事项
-
生命周期安全:
- 避免在 ViewModel 中持有 View 引用(如 Activity/Fragment),如需 Context,使用
AndroidViewModel并传入Application。 - 网络请求、数据库操作必须在协程中执行,避免阻塞主线程。
- 避免在 ViewModel 中持有 View 引用(如 Activity/Fragment),如需 Context,使用
-
数据状态管理:
- 使用密封类(如
Result)统一管理数据的加载、成功、失败状态,View 只需根据状态更新 UI。
- 使用密封类(如
-
依赖注入(可选):
- 复杂项目推荐使用 Hilt 或 Koin 实现依赖注入,替代手动创建 Repository 和 ViewModel,简化代码并提高可测试性。
-
错误处理:
- 网络异常、数据库操作失败需统一捕获,通过 ViewModel 通知 View 显示错误信息。
-
性能优化:
- 列表使用 DiffUtil 优化更新效率。
- 图片加载使用 Glide 并设置占位图、缓存策略。
六、总结
MVVM 架构通过分层设计实现了代码的低耦合、高可维护性,结合 Jetpack 组件(ViewModel、LiveData/StateFlow、Room)和 Kotlin 协程,可快速搭建健壮的 Android 应用。核心是数据驱动 UI和生命周期安全,遵循 “View 观察 ViewModel,ViewModel 调用 Repository,Repository 封装数据来源” 的数据流模式。

浙公网安备 33010602011771号