目录

一,定义

1.1 MVC

1.2 MVP

1.3 MVVM

二,MVVM框架搭建

2.1 Model层

2.2 View层

2.2.1 ViewBinding

2.2.2 ViewModel+LiveData

2.2.3 封装BaceActivity

2.3 ViewModel层

总结

三,Retrofit封装


一,定义

安卓的框架由最初的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层的主要作用就是通过网络和本地数据库获取视图层所需数据,由于网络框架还没搭建,所以先用数据模拟一下:

测试数据:

{"status":200,"desc":"操作成功","data":"2025-09-26 13:15:03"}

首先创建一个数据类:

data class DateTimeBean(val status :Int ,val desc:String ,val data:String)

然后创建一个接口,用于数据成功和失败的回调:

interface ICallBack {
    fun onSuccess(result: T)
    fun onError(e:Throwable)
}

最后创建MainActivity的Model,因为Model层获取数据需要不同的传参,所以Model层就没有必要创建基类或者接口,这里我们直接创建一个MainModel,并模拟数据的获取:

open class MainModel {
    fun getTimeData(callBack: ICallBack){
        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的使用:

buildFeatures {
    dataBinding = true
    viewBinding = true
}

我们创建一个名为 activity_test 的xml文件:



    

此时会通过apt生成ActivityTestBinding,然后在TestActivity中使用:

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中添加依赖:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"

创建TestViewModel:

class TestViewModel: ViewModel() {
    val count:MutableLiveData by lazy { getTest() }
    fun getTest():MutableLiveData {
      return MutableLiveData()
    }
}

在TestActivity中,使用viewModel:

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 = 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。

class ViewModelFactory : ViewModelProvider.Factory {
    override fun  create(modelClass: Class): 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:

abstract class BaseActivity :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
                    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就可以简化为:

class MainActivity : BaseActivity() {
    override fun initView() {
        viewModel.myViewModel.observe(this) {
            binding.txtContent.setText(it.data)
        }
        binding.txtContent.setOnClickListener {
        }
    }
}

2.3 ViewModel层

下面就来创建ViewModel层,有特殊需求的也可以封装一个基类,这里没有涉及到具体项目,所以就不创建基类了,直接创建一个MainModel类:

open class MyViewModel : ViewModel() {
    val myViewModel : MutableLiveData by lazy { getTimeLiveData() }
    val model: MainModel by lazy { initModel() }
    fun initModel(): MainModel {
        return MainModel()
    }
    fun getTimeLiveData():MutableLiveData{
        return MutableLiveData()
    }
    fun getTime(){
        model.getTimeData(object : ICallBack {
            override fun onSuccess(result: DateTimeBean) {
                myViewModel.postValue(result)
            }
            override fun onError(e: Throwable) {
            }
        })
    }
}

总结

三层都封装完之后,在MainActivity中使用:

class MainActivity : BaseActivity() {
    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

首先我们创建一个基本的数据类:

@Keep
data class BaseResponseString(val status :Int ,val desc:String ,val data: String)

然后创建一个Request接口:

interface Request {
    @GET
    fun get(@Url url:String): Call
    @GET
    fun getString(@Url url:String): Call
    @GET
    fun get(@Url url:String, @QueryMap map:Map ):Call
    @POST
    fun post(@Url url:String):Call
    @POST
    fun post(@Url url:String, @Body map: Map):Call
    @POST
    fun post(@Url url:String, @Body body:List):Call
}

还有之前创建的ICallBack:

interface ICallBack {
    fun onSuccess(result: T)
    fun onError(e:Throwable)
}

最后去创建一个单例的RetrofitManager,用来创建Retrofit ,请求get,post等。 这里只封装下get:

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  get(url:String,callBack:ICallBack){
        val request =retrofit.create(Request::class.java)
        request.get(BASE_URL+url).enqueue(object :Callback{
            override fun onResponse(
                call: Call,
                response: Response
            ) {
               callBack.onSuccess(response.body()!!.data as T)
            }
            override fun onFailure(call: Call, t: Throwable) {
               callBack.onError(t)
            }
        })
    }
    fun  getString(url:String,callBack:ICallBack){
        val request =retrofit.create(Request::class.java)
        request.getString(BASE_URL+url).enqueue(object :Callback{
            override fun onResponse(
                call: Call,
                response: Response
            ) {
                callBack.onSuccess(response.body()!!.data as T)
            }
            override fun onFailure(call: Call, t: Throwable) {
                callBack.onError(t)
            }
        })
    }
}

在model中使用:

class SecondModel {
     fun getSecondData(callBack: ICallBack){
        RetrofitManager.getInstance().getString("passport/web-rbac/logins/currentTime",object:ICallBack{
            override fun onSuccess(result: String) {
                callBack.onSuccess(result)
            }
            override fun onError(e: Throwable) {
                callBack.onError(e)
            }
        })
    }
}

创建ViewModel:

class SecondViewModel : ViewModel()  {
    val secondViewModel : MutableLiveData by lazy { getSecondLiveData() }
    val secondModel: SecondModel by lazy { initModel() }
    fun initModel(): SecondModel {
        return SecondModel()
    }
    fun getSecondLiveData():MutableLiveData{
        return MutableLiveData()
    }
    fun getSecondData(){
        viewModelScope.launch (Dispatchers.IO){
            secondModel.getSecondData(object : ICallBack {
                override fun onSuccess(result: String) {
                    secondViewModel.postValue(result)
                }
                override fun onError(e: Throwable) {
                    secondViewModel.postValue(e.message)
                }
            })
        }
    }
}

创建Activity:

class SecondActivity: BaseActivity() {
    override fun initView() {
        viewModel.secondViewModel.observe(this) {
            binding.txtSecond.setText(it)
        }
        binding.txtSecond.setOnClickListener {
            viewModel.getSecondData()
        }
    }
}

创建activity_second.xml:



    

运行效果如下:

posted on 2025-10-13 13:18  ycfenxi  阅读(13)  评论(0)    收藏  举报