移动应用架构设计:MVVM实战(零基础也能懂)
作者:一个从中文系转行做移动开发的“老”程序员。当初学 MVVM 的时候,被一堆英文缩写绕得头晕眼花。今天,我就用最接地气的方式,带你从零开始搞懂它。
为什么我要写这篇教程?
我当初自学 Android 开发时,一上来就写 Activity 里塞满逻辑、网络请求、UI 更新……结果代码像一锅乱炖,改一处崩三处。后来接触到 MVVM 架构,才真正理解什么叫“代码有组织、开发不抓狂”。
更巧的是,现在很多公司(尤其是涉及运营后台 + App 联动的场景)都采用 SpringBoot 做后端服务,前端用 MVVM 写 App。所以,这篇教程不仅教你 MVVM,还会让你看到它和 SpringBoot 如何配合——哪怕你是文科生,也能看懂!
一、MVVM 是什么?能干啥?
MVVM = Model + View + ViewModel
你可以把它想象成一家奶茶店:
- Model:原料库(数据来源,比如用户信息、商品列表)
- View:前台点单界面(你看到的 App 页面)
- ViewModel:店员(负责把原料变成奶茶,并告诉前台“做好了!”)
核心思想:让 UI(View)只管“显示”,不管“怎么拿数据”。数据变了,自动刷新界面——这就是 数据驱动 UI。
✅ 优点:代码清晰、易测试、不怕产品经理临时改需求!
二、环境准备(5 分钟搞定)
我们要做一个超简单的 App:从 SpringBoot 后台获取一条“今日运营公告”,显示在手机上。
所需工具
| 工具 | 版本建议 | 用途 |
|---|---|---|
| Android Studio | Giraffe 或以上 | 写 App |
| JDK | 17 | 运行 Java/Kotlin |
| IntelliJ IDEA (或复用 AS) | 最新版 | 写 SpringBoot 后端 |
| Postman(可选) | - | 测试接口 |
步骤
- 安装 Android Studio:官网下载安装即可。
- 创建 Android 项目:
- 语言选 Kotlin
- Minimum SDK 选 API 21 (Android 5.0)(兼容性好)
- 添加必要依赖(在
app/build.gradle中):
dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
}
💡 我当初漏加
livedata依赖,ViewModel 数据变了 UI 却不更新,折腾半天才发现!
三、核心概念通俗讲
1. ViewModel:UI 的“私人助理”
- 它不会随屏幕旋转销毁(Activity 会!)
- 它持有 LiveData(一种“可观察的数据”)
2. LiveData:会“喊话”的数据
- 当数据更新,所有“监听者”(比如 TextView)自动收到通知
- 自动处理生命周期(App 退到后台就不会回调,防内存泄漏)
3. Model:数据从哪来?
- 可以是本地数据库、网络接口(我们这里用 SpringBoot 提供的 API)
四、实战:做一个“运营公告”App
第一步:写 SpringBoot 后端(3 行代码!)
创建一个 SpringBoot 项目(用 start.spring.io 快速生成),添加一个 Controller:
// AnnouncementController.java
@RestController
public class AnnouncementController {
@GetMapping("/api/announcement")
public Map<String, String> getAnnouncement() {
Map<String, String> resp = new HashMap<>();
resp.put("content", "【运营通知】今日全场奶茶第二杯半价!");
return resp;
}
}
启动后,访问 http://localhost:8080/api/announcement,你会看到:
{"content":"【运营通知】今日全场奶茶第二杯半价!"}
🎯 这就是我们 App 要显示的内容!
第二步:定义数据模型(Model)
在 Android 项目中创建 Announcement.kt:
data class Announcement(
val content: String
)
第三步:写网络请求(Retrofit)
创建 ApiService.kt:
interface ApiService {
@GET("api/announcement")
suspend fun getAnnouncement(): Announcement
}
// 工具类
object ApiClient {
private const val BASE_URL = "http://10.0.2.2:8080/" // Android 模拟器访问本机
val instance: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
⚠️ 注意:
10.0.2.2是 Android 模拟器访问电脑 localhost 的特殊 IP!真机调试要用电脑 IP。
第四步:写 ViewModel
class AnnouncementViewModel : ViewModel() {
private val _announcement = MutableLiveData<Announcement>()
val announcement: LiveData<Announcement> = _announcement
fun fetchAnnouncement() {
viewModelScope.launch {
try {
val data = ApiClient.instance.getAnnouncement()
_announcement.value = data
} catch (e: Exception) {
// 实际项目要处理错误,这里简化
}
}
}
}
🔍
viewModelScope.launch:在 ViewModel 中安全地启动协程(Kotlin 异步神器)
第五步:写 UI(View)
修改 MainActivity.kt:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: AnnouncementViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this)[AnnouncementViewModel::class.java]
// 观察数据变化
viewModel.announcement.observe(this) { announcement ->
binding.textView.text = announcement.content
}
// 点击按钮刷新
binding.button.setOnClickListener {
viewModel.fetchAnnouncement()
}
// 首次加载
viewModel.fetchAnnouncement()
}
}
对应的 activity_main.xml:
<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">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载中..." />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="刷新公告" />
</LinearLayout>
第六步:别忘了网络权限!
在 AndroidManifest.xml 中添加:
<uses-permission android:name="android.permission.INTERNET" />
五、常见问题 & 避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 点击按钮没反应 | 忘记调用 fetchAnnouncement() |
检查按钮点击监听是否绑定 |
| 显示“加载中...”不动 | 网络请求失败 | 1. 检查 SpringBoot 是否运行 2. 模拟器用 10.0.2.2,真机用电脑 IP3. 关闭电脑防火墙 |
| 屏幕旋转后数据消失 | 把数据存在 Activity 里了 | 一定要用 ViewModel + LiveData |
编译报错 Unresolved reference: binding |
忘记启用 ViewBinding | 在 build.gradle 中添加:buildFeatures { viewBinding true } |
💡 我当初在咖啡馆用公共 Wi-Fi,死活连不上本地 SpringBoot,差点放弃——原来是防火墙!后来插网线解决。
六、下一步学习建议
你已经迈出了 MVVM 的第一步!接下来可以:
- 加入 Repository 层:让 ViewModel 不直接调 API,而是通过 Repository 获取数据(支持本地缓存 + 网络)
- 用 Room 数据库:把公告缓存到本地,离线也能看
- 学习 Jetpack Compose:Google 新一代 UI 框架,和 MVVM 天然契合
- 深入 SpringBoot:给公告加上管理后台,让运营同学自己编辑内容!
结语
MVVM 不是魔法,而是一种让代码活得更久的设计方式。作为曾经连“架构”两个字都怕的文科生,我想告诉你:只要动手写,就没有学不会的技术。
现在,去跑通你的第一个 MVVM + SpringBoot 项目吧!遇到问题,欢迎回来再读一遍——我当年就是这么过来的 😄
本文约 2480 字,纯手打无图,但希望能照亮你的第一个 App 架构之路。

评论 0