目录
[1.1 MVC](#1.1 MVC)
[1.2 MVP](#1.2 MVP)
[1.3 MVVM](#1.3 MVVM)
[2.1 Model层](#2.1 Model层)
[2.2 View层](#2.2 View层)
[2.2.1 ViewBinding](#2.2.1 ViewBinding)
[2.2.2 ViewModel+LiveData](#2.2.2 ViewModel+LiveData)
[2.2.3 封装BaceActivity](#2.2.3 封装BaceActivity)
[2.3 ViewModel层](#2.3 ViewModel层)
一,定义
安卓的框架由最初的mvc,到后来的mvp,又到现在的mvvm。
1.1 MVC
Android采用XML文件实现页面布局,通过Java在Activity中开发业务逻辑,这种开发模式实际上已经采用了MVC的思想,分离视图和控制器。MVC模式(Model--view--controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
- 控制器(Controller)- 负责转发请求,对请求进行处理。
- 视图(View) -- 界面设计人员进行图形界面设计。
- 模型(Model) -- 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
在Android编程中,View对应xml布局文件,Model对应实体模型(网络、数据库、I/O),Controller对应Activity业务逻辑,数据处理和UI处理。如下图所示:

在实际开发过程中,纯粹作为View的各个XML文件功能较弱,Activity基本上都是View和Controller的合体,既要负责视图的显示又要加入控制逻辑,承担的功能很多,导致代码量很大。所有更贴切的目前常规的开发说应该是View-Model模式,大部分都是通过Activity的协调。
1.2 MVP
关于MVP模式,之前的文章已经讲过了,并进行了框架的搭建,https://yuanzhen.blog.csdn.net/article/details/133266373
1.3 MVVM
MVVM是Model-View-ViewModel的简称,它由三个部分组成,也就是 Model、View 和 ViewModel,其中视图模型(ViewModel)其实就是 PM 模式中的展示模型,在 MVVM 中叫做视图模型。从实际效果来看,ViewModel是View的数据模型和Presenter的结合,具体结构如下图所示:

- Model(模型层)通过网络和本地数据库获取视图层所需数据;
- View(视图层)采用XML文件进行界面的描述;
- ViewModel(视图-模型层)负责View和Model之间的通信,以此分离视图和数据。
View和Model之间通过Android Data Binding技术,实现视图和数据的双向绑定;ViewModel持有Model的引用,通过Model的方法请求数据;获取数据后,通过Callback(回调)的方式回到ViewModel中,由于ViewModel与View的双向绑定,使得界面得以实时更新。同时,界面输入的数据变化时,由于双向绑定技术,ViewModel中的数据得以实时更新,提高了数据采集的效率。
MVVM架构将Presenter改名为ViewModel,基本上与MVP模式完全一致,唯一的区别是,它采用双向绑定(data-binding)View的变动,自动反映在 ViewModel,反之亦然,这就导致了我们如果要完整的采用 MVVM 必须熟练的掌握 DataBinding 等基础组建,这就给我们MVVM引入项目带了困难。
二,MVVM框架搭建
2.1 Model层
Model层的主要作用就是通过网络和本地数据库获取视图层所需数据,由于网络框架还没搭建,所以先用数据模拟一下:
测试数据:
bash
{"status":200,"desc":"操作成功","data":"2025-09-26 13:15:03"}
首先创建一个数据类:
Kotlin
data class DateTimeBean(val status :Int ,val desc:String ,val data:String)
然后创建一个接口,用于数据成功和失败的回调:
Kotlin
interface ICallBack<T> {
fun onSuccess(result: T)
fun onError(e:Throwable)
}
最后创建MainActivity的Model,因为Model层获取数据需要不同的传参,所以Model层就没有必要创建基类或者接口,这里我们直接创建一个MainModel,并模拟数据的获取:
Kotlin
open class MainModel {
fun getTimeData(callBack: ICallBack<DateTimeBean>){
val data = DateTimeBean(200, "获取成功", "结果获取成功")
callBack.onSuccess(data)
}
}
Model层的创建就完成了。非常简单,如果需要增加一些通用的功能,比如loading等,可以封装基类去实现。
2.2 View层
View层其实就是Activity及其xml,因为要实现mvvm,所以这里我们采用viewbinding+viewmodel+livedata 来实现
2.2.1 ViewBinding
关于viewbinding下面我们来讲解下它的使用
首先在app的build.gradle中添加viewbinding的使用:
bash
buildFeatures {
dataBinding = true
viewBinding = true
}
我们创建一个名为 activity_test 的xml文件:
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
此时会通过apt生成ActivityTestBinding,然后在TestActivity中使用:
Kotlin
class TestActivity :AppCompatActivity() {
var binding :ActivityTestBinding? =null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestBinding.inflate(layoutInflater)
val view = binding?.root
setContentView(view)
binding?.txtTest?.setText("viewbinding")
}
}
这里所有在xml中定义的view都可以直接通过binding.id获取到。比如id为txt_test可以通过binding.txtTest获取到。
这样就不用通过findViewById去获取id了。
2.2.2 ViewModel+LiveData
ViewModel+LiveData前面的文章讲过:https://yuanzhen.blog.csdn.net/article/details/134795587
下面来讲一下kotlin中的使用:
首先在app的build.gradle中添加依赖:
bash
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
创建TestViewModel:
Kotlin
class TestViewModel: ViewModel() {
val count:MutableLiveData<String> by lazy { getTest() }
fun getTest():MutableLiveData<String> {
return MutableLiveData<String>()
}
}
在TestActivity中,使用viewModel:
Kotlin
class TestActivity :AppCompatActivity() {
var binding :ActivityTestBinding? =null
protected lateinit var viewModel:TestViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestBinding.inflate(layoutInflater)
val view = binding?.root
setContentView(view)
var observer:Observer<String> = Observer {
binding?.txtTest?.setText(it)
}
viewModel =ViewModelProvider(this).get(TestViewModel::class.java)
viewModel.count.observe(this,observer)
binding?.txtTest?.setOnClickListener{
viewModel.count.postValue("测试测试")
}
}
}
运行效果:点击后显示

2.2.3 封装BaceActivity
上面的使用中,每次创建一个activity都要写一堆的代码去获取viewbinding和viewModel,所以我们可以封装一个baseactivity 来去获取这些事情。
首先创建一个ViewModelFactory用来创建ViewModel。
Kotlin
class ViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return try {
modelClass.newInstance()
} catch (e: IllegalAccessException) {
Log.e(ViewModelFactory::class.java.simpleName, e.toString())
throw IllegalArgumentException("unexpected model class $modelClass", e)
} catch (e: InstantiationException) {
Log.e(ViewModelFactory::class.java.simpleName, e.toString())
throw IllegalArgumentException("unexpected model class $modelClass", e)
}
}
}
然后创建一个BaseActivity 来创建ViewModel和ViewBinding:
Kotlin
abstract class BaseActivity<K :ViewModel,W :ViewBinding> :AppCompatActivity() {
private var factory: ViewModelProvider? = null
protected lateinit var viewModel: K
protected lateinit var binding: W
protected val vMFactory: ViewModelProvider.Factory =ViewModelFactory()
override fun onCreate(savedInstanceState: Bundle?) {
initWindow()
super.onCreate(savedInstanceState)
bindView()
createViewModel()
initView()
initClick()
}
private fun createViewModel() {
val superclass = javaClass.genericSuperclass!!
if (superclass is ParameterizedType) {
val arguments = superclass.actualTypeArguments
if (arguments.isNotEmpty()) {
try {
val aClass = arguments[0] as Class<K>
if (factory == null) {
factory = ViewModelProvider(this, vMFactory!!)
}
viewModel = factory!![aClass]
} catch (e: Exception) {
Log.e(BaseActivity::class.java.simpleName, e.toString())
e.printStackTrace()
}
}
}
}
private fun bindView() {
//获得带有泛型的父类
val superclass = javaClass.genericSuperclass!!
// ParameterizedType参数化类型,即泛型
if (superclass is ParameterizedType) {
//泛型数组
val arguments = superclass.actualTypeArguments
try {
//取第二个,也就是我们的viewbinding
val aClass: Class<*> = arguments[1] as Class<*>
val method = aClass.getDeclaredMethod("inflate", LayoutInflater::class.java)
binding = method.invoke(aClass, layoutInflater) as W
setContentView(binding.root)
} catch (e: NoSuchMethodException) {
Log.e(BaseActivity::class.java.simpleName, e.toString())
e.printStackTrace()
} catch (e: IllegalAccessException) {
Log.e(BaseActivity::class.java.simpleName, e.toString())
e.printStackTrace()
} catch (e: InvocationTargetException) {
Log.e(BaseActivity::class.java.simpleName, e.toString())
e.printStackTrace()
}
}
}
//初始化窗口可重写此方法
open fun initWindow() {}
//点击事件设置,可重写此方法
open fun initClick() {}
//初始化view
abstract fun initView()
}
这样MainActivity就可以简化为:
Kotlin
class MainActivity : BaseActivity<MyViewModel, ActivityMainBinding>() {
override fun initView() {
viewModel.myViewModel.observe(this) {
binding.txtContent.setText(it.data)
}
binding.txtContent.setOnClickListener {
}
}
}
2.3 ViewModel层
下面就来创建ViewModel层,有特殊需求的也可以封装一个基类,这里没有涉及到具体项目,所以就不创建基类了,直接创建一个MainModel类:
Kotlin
open class MyViewModel : ViewModel() {
val myViewModel : MutableLiveData<DateTimeBean> by lazy { getTimeLiveData() }
val model: MainModel by lazy { initModel() }
fun initModel(): MainModel {
return MainModel()
}
fun getTimeLiveData():MutableLiveData<DateTimeBean>{
return MutableLiveData<DateTimeBean>()
}
fun getTime(){
model.getTimeData(object : ICallBack<DateTimeBean> {
override fun onSuccess(result: DateTimeBean) {
myViewModel.postValue(result)
}
override fun onError(e: Throwable) {
}
})
}
}
总结
三层都封装完之后,在MainActivity中使用:
Kotlin
class MainActivity : BaseActivity<MyViewModel, ActivityMainBinding>() {
override fun initView() {
viewModel.myViewModel.observe(this) {
binding.txtContent.setText(it.data)
}
binding.txtContent.setOnClickListener {
viewModel.getTime()
}
}
}
运行效果如下:

三,Retrofit封装
关于retrofit的使用及其源码,之前的文章已经讲过了https://yuanzhen.blog.csdn.net/article/details/145372493
首先我们创建一个基本的数据类:
Kotlin
@Keep
data class BaseResponseString(val status :Int ,val desc:String ,val data: String)
然后创建一个Request接口:
Kotlin
interface Request {
@GET
fun get(@Url url:String): Call<BaseResponseBody>
@GET
fun getString(@Url url:String): Call<BaseResponseString>
@GET
fun get(@Url url:String, @QueryMap map:Map<String, Object> ):Call<BaseResponseBody>
@POST
fun post(@Url url:String):Call<BaseResponseBody>
@POST
fun post(@Url url:String, @Body map: Map<String, Object>):Call<BaseResponseBody>
@POST
fun post(@Url url:String, @Body body:List<Object>):Call<BaseResponseBody>
}
还有之前创建的ICallBack:
Kotlin
interface ICallBack<T> {
fun onSuccess(result: T)
fun onError(e:Throwable)
}
最后去创建一个单例的RetrofitManager,用来创建Retrofit ,请求get,post等。 这里只封装下get:
Kotlin
class RetrofitManager private constructor() {
companion object {
private const val BASE_URL = "http://192.168.31.87/"
@Volatile private var instance: RetrofitManager? = null
fun getInstance(): RetrofitManager {
return instance ?: synchronized(this) {
instance ?: RetrofitManager().also { instance = it }
}
}
}
val retrofit:Retrofit by lazy { initRetrofit() }
fun initRetrofit():Retrofit{
val loggingInterceptor = HttpLoggingInterceptor { message -> //打印retrofit日志
Log.i("RetrofitLog", "retrofitBack = $message")
}
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)//设置打印等级
val client = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(20, TimeUnit.SECONDS)//连接超时事件
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build()
val retrofit =Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()//配置属性
return retrofit
}
fun <T> get(url:String,callBack:ICallBack<T>){
val request =retrofit.create(Request::class.java)
request.get(BASE_URL+url).enqueue(object :Callback<BaseResponseBody>{
override fun onResponse(
call: Call<BaseResponseBody>,
response: Response<BaseResponseBody>
) {
callBack.onSuccess(response.body()!!.data as T)
}
override fun onFailure(call: Call<BaseResponseBody>, t: Throwable) {
callBack.onError(t)
}
})
}
fun <T> getString(url:String,callBack:ICallBack<T>){
val request =retrofit.create(Request::class.java)
request.getString(BASE_URL+url).enqueue(object :Callback<BaseResponseString>{
override fun onResponse(
call: Call<BaseResponseString>,
response: Response<BaseResponseString>
) {
callBack.onSuccess(response.body()!!.data as T)
}
override fun onFailure(call: Call<BaseResponseString>, t: Throwable) {
callBack.onError(t)
}
})
}
}
在model中使用:
Kotlin
class SecondModel {
fun getSecondData(callBack: ICallBack<String>){
RetrofitManager.getInstance().getString("passport/web-rbac/logins/currentTime",object:ICallBack<String>{
override fun onSuccess(result: String) {
callBack.onSuccess(result)
}
override fun onError(e: Throwable) {
callBack.onError(e)
}
})
}
}
创建ViewModel:
Kotlin
class SecondViewModel : ViewModel() {
val secondViewModel : MutableLiveData<String> by lazy { getSecondLiveData() }
val secondModel: SecondModel by lazy { initModel() }
fun initModel(): SecondModel {
return SecondModel()
}
fun getSecondLiveData():MutableLiveData<String>{
return MutableLiveData<String>()
}
fun getSecondData(){
viewModelScope.launch (Dispatchers.IO){
secondModel.getSecondData(object : ICallBack<String> {
override fun onSuccess(result: String) {
secondViewModel.postValue(result)
}
override fun onError(e: Throwable) {
secondViewModel.postValue(e.message)
}
})
}
}
}
创建Activity:
Kotlin
class SecondActivity: BaseActivity<SecondViewModel, ActivitySecondBinding>() {
override fun initView() {
viewModel.secondViewModel.observe(this) {
binding.txtSecond.setText(it)
}
binding.txtSecond.setOnClickListener {
viewModel.getSecondData()
}
}
}
创建activity_second.xml:
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="第二页"
android:textColor="#00ff22"/>
</RelativeLayout>
运行效果如下:
