零基础也能懂:用 MVVM 写出第一个像样的移动 App
大家好!我是你们的老朋友,一个在大厂写了三年代码、业余时间在 B站做技术分享的开发者。今天我想带大家走进移动应用架构的世界——特别是 MVVM(Model-View-ViewModel) 这个听起来高大上、但其实特别实用的设计模式。
你可能会问:“我又不是架构师,为什么要学这个?”
我当初学的时候也这么想。结果呢?写了个小项目,界面一改就崩,数据一多就乱,调 bug 调到凌晨三点。后来才明白:不是你会写代码就能做出好 App,而是你得知道怎么“组织”代码。
所以今天这篇教程,不讲抽象理论,只讲 问题 → 思路 → 代码 的实战路径。哪怕你是第一次听说“后端”和“前端”的区别,也能跟着一步步做出一个结构清晰、易于维护的小应用。
为什么我们需要 MVVM?
想象你在搭积木:
- 如果所有积木都堆在一起,想换一块红色的?得把整座塔拆了。
- 但如果红色积木单独放在一个盒子里,蓝色在另一个盒子,换起来就轻松多了。
MVVM 就是帮你把“界面”、“数据”、“逻辑”分开放进不同盒子的设计方法。
在移动开发中,我们常遇到这些问题:
- 点击按钮没反应,不知道是 UI 没绑对,还是逻辑写错了
- 后端接口一改,整个 App 崩了
- 想加个新功能,结果要改 10 个文件
MVVM 能帮我们避免这些坑。它把代码分成三部分:
| 角色 | 职责 | 举个栗子 |
|---|---|---|
| Model(模型) | 负责数据,比如从后端获取用户信息 | 调 API、读数据库 |
| View(视图) | 用户看到的界面 | 按钮、文字、图片 |
| ViewModel(视图模型) | 连接 View 和 Model 的“翻译官” | 把后端返回的 JSON 转成界面上能显示的文字 |
💡 关键思想:View 不直接碰 Model,一切通过 ViewModel 中转。
环境准备:5 分钟搭好开发环境
我们用 Android + Kotlin + Jetpack Compose 来演示(这是目前 Google 官方推荐的现代 Android 开发方式)。别担心,Kotlin 语法比 Java 简洁得多,新手友好!
步骤 1:安装 Android Studio
- 访问 developer.android.com/studio
- 下载最新版 Android Studio(选“Chipmunk”或更新版本)
- 安装时勾选 Android SDK 和 Android Virtual Device
步骤 2:创建新项目
- 打开 Android Studio → “New Project”
- 选择 Empty Compose Activity
- 语言选 Kotlin
- Minimum SDK 设为 API 21 (Android 5.0)(覆盖 95% 以上设备)
步骤 3:添加必要依赖
打开 app/build.gradle 文件,在 dependencies 里加上:
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.1"
这两行是 MVVM 的核心支持库,让 ViewModel 能和 Compose 无缝协作。
✅ 提示:如果 Gradle sync 失败,点右上角“Sync Now”就行。网络慢的话可以配阿里云镜像。
核心概念:用生活例子理解 MVVM
1. Model:你的“数据管家”
假设我们要做一个“用户资料页”,需要显示用户名和头像。
Model 就是那个负责去“后端”拿数据的人。
// User.kt
data class User(
val id: Int,
val name: String,
val avatarUrl: String
)
// UserRepository.kt
class UserRepository {
// 模拟从后端获取数据(实际项目这里会调 API)
suspend fun fetchUser(userId: Int): User {
// 模拟网络延迟
delay(1000)
return User(1, "小明", "https://example.com/avatar.jpg")
}
}
🌟 注意:
suspend表示这是一个“挂起函数”,适合处理网络、数据库等耗时操作,不会卡住主线程。
2. ViewModel:界面和数据的“中间人”
View(界面)不能直接调 UserRepository,而是通过 ViewModel:
// UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(
private val repository: UserRepository
) : ViewModel() {
private val _user = mutableStateOf<User?>(null)
val user: State<User?> = _user
init {
loadUser()
}
private fun loadUser() {
viewModelScope.launch {
_user.value = repository.fetchUser(1)
}
}
}
关键点解释:
_user是可变状态(MutableState),用来存储数据user是对外暴露的只读状态(State),View 只能读,不能改viewModelScope.launch在后台线程执行网络请求,安全又高效
💡 我当初学的时候总搞混
_user和user,记住:下划线的是“内部写”,没下划线的是“外部读”。
3. View:用户看到的一切
现在用 Compose 写界面,自动响应数据变化:
// MainActivity.kt
@Composable
fun UserProfileScreen(viewModel: UserViewModel) {
val user by viewModel.user.collectAsState()
if (user == null) {
CircularProgressIndicator() // 加载中
} else {
Column {
Text(text = "你好,${user!!.name}!")
// 这里可以加 Image 显示头像
}
}
}
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
val viewModel: UserViewModel = hiltViewModel()
UserProfileScreen(viewModel)
}
}
}
}
神奇之处在于:只要 user 的值变了,界面自动刷新! 不用手动调 setText()。
实战:做一个“天气查询”小应用
光看理论不过瘾,咱们动手做一个真实项目!
需求
- 输入城市名
- 点击“查询”按钮
- 显示该城市的当前温度(模拟数据)
第一步:定义 Model
// Weather.kt
data class Weather(
val city: String,
val temperature: Int // 摄氏度
)
// WeatherRepository.kt
class WeatherRepository {
suspend fun getWeather(city: String): Weather {
delay(800) // 模拟网络请求
// 实际项目这里会调后端 API,比如 https://api.weather.com/v1/...
return Weather(city, (20..35).random())
}
}
第二步:写 ViewModel
// WeatherViewModel.kt
@HiltViewModel
class WeatherViewModel @Inject constructor(
private val repo: WeatherRepository
) : ViewModel() {
private val _weather = mutableStateOf<Weather?>(null)
val weather: State<Weather?> = _weather
private val _isLoading = mutableStateOf(false)
val isLoading: State<Boolean> = _isLoading
fun searchWeather(city: String) {
if (city.isBlank()) return
_isLoading.value = true
viewModelScope.launch {
try {
_weather.value = repo.getWeather(city)
} catch (e: Exception) {
// 实际项目这里要处理错误
} finally {
_isLoading.value = false
}
}
}
}
第三步:搭建 View(Compose 界面)
@Composable
fun WeatherApp(viewModel: WeatherViewModel) {
var cityInput by remember { mutableStateOf("") }
val weather by viewModel.weather.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center
) {
OutlinedTextField(
value = cityInput,
onValueChange = { cityInput = it },
label = { Text("请输入城市") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = { viewModel.searchWeather(cityInput) },
enabled = !isLoading,
modifier = Modifier.fillMaxWidth()
) {
if (isLoading) {
CircularProgressIndicator(
color = Color.White,
modifier = Modifier.size(16.dp)
)
} else {
Text("查询天气")
}
}
Spacer(modifier = Modifier.height(24.dp))
weather?.let { w ->
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(w.city, style = MaterialTheme.typography.headlineMedium)
Text("${w.temperature}°C", style = MaterialTheme.typography.displaySmall)
}
}
}
}
}
最后:连接一切
在 MainActivity 中注入并使用:
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
val viewModel: WeatherViewModel = hiltViewModel()
WeatherApp(viewModel)
}
}
}
}
✅ 运行效果:输入“北京”,点查询,1 秒后显示“北京 28°C”。
新手常见问题 & 解决方案
Q1:为什么我的界面不刷新?
- 原因:可能没用
State或collectAsState() - 解决:确保 ViewModel 中的状态是
mutableStateOf,View 中用by collectAsState()读取
Q2:ViewModel 里怎么调后端 API?
- 实际项目中,
UserRepository或WeatherRepository会使用 Retrofit 或 Ktor 发起 HTTP 请求:
这属于“后端交互”范畴,MVVM 的好处是:无论你用什么网络库,ViewModel 的接口不变!interface ApiService { @GET("weather") suspend fun getWeather(@Query("city") city: String): WeatherResponse }
Q3:Hilt 是什么?必须用吗?
- Hilt 是依赖注入框架,帮你自动创建
UserRepository并传给 ViewModel。 - 初学可以手动 new 对象(不推荐长期使用):
class UserViewModel : ViewModel() { private val repository = UserRepository() // ... }
Q4:MVVM 和 MVC、MVP 有什么区别?
简单对比:
| 架构 | 数据流向 | 适合场景 |
|---|---|---|
| MVC | View → Controller → Model → View | Web 开发(如 Spring MVC) |
| MVP | View ↔ Presenter ↔ Model | 老 Android 项目(findViewById 时代) |
| MVVM | View ↔ ViewModel ↔ Model | 现代 Android / iOS(数据驱动 UI) |
🎯 MVVM 的最大优势:UI 自动响应数据变化,无需手动同步。
学习建议:从“代码人生”走向专业开发
我当初也是从“Hello World”开始的。如果你刚入门,记住这三条:
先跑通,再优化
别一上来就想设计完美架构。先把功能做出来,再用 MVVM 重构。后端不是你的敌人
很多新手怕“后端”,其实你只需要知道:后端给你 JSON,你解析成 Kotlin 对象就行。用工具如 JSON to Kotlin Class 自动生成代码。每天写 20 行代码
我坚持了三年,从实习生到大厂工程师。代码人生,贵在持续。
下一步学什么?
- ✅ 掌握 Retrofit:连接真实后端 API
- ✅ 学习 Room Database:本地数据持久化
- ✅ 了解 Coroutines Flow:处理复杂数据流
- ✅ 尝试 Navigation Compose:多页面跳转
结语
MVVM 不是魔法,而是一种 让代码更清晰、更易维护的思维方式。当你能把界面、逻辑、数据分离开,你就已经超越了 80% 的初学者。
希望这篇教程能帮你迈出架构设计的第一步。如果你觉得有用,欢迎去 B站 搜我的频道(ID:CodeLifeGuide),我会持续更新 “零基础到大厂”系列视频。
记住:每一个大神,都曾是连 ViewModel 都拼不对的新手。 你的代码人生,现在开始书写!
本文完整代码已上传 GitHub:github.com/yourname/mvvm-starter
(替换 yourname 为你的用户名即可)
加油,未来的架构师!

评论 0