前言

目前市面上流行的客户端app的架构基本都是基于MVVM 设计的,其实就是为了去更好的使用jetpack 组件,mvvm配合jetpack 去搭建的话,不仅仅在业务上达到了解耦、方便维护和review的效果,如果配合lifeCycle 的使用,更能有效的减少对象生命周期的控制问题导致的内存泄漏。而使用kotlin的初衷,如flow, 能很好的替代RxJava 和jetpack 中的LiveData,如协程能更轻量合理减少的对于线程的创建开销、线程切换负责、线程关闭等一系列繁琐的操作。所以,使用kotlin 和 mvvm +jetpack 组件去搭建的的框架思想源于此。

mvvm 图解


mvvm 几乎就是mvc 的优化版,将mvc中的c 替换成了model,减少了c层逻辑耦合性和代码雍总问题。
从图中可以看到,mvvm 有三个模块,分别是ViewModel、Model、View

  • ViewModel: 负责管理数据的抽象类,通过ViewBinding 中的setVariable 方法绑定绑定到View层 进行页面更新。
    ViewModel 的生命周期会从创建绑定到Activity/Fragment 中开始 一直到Activity/Fragment 的onDestroy 结束,所以,不需要担心数据在
    使用过程中丢失。

  • Model: 负责业务逻辑处理,如网络请求、数据库、耗时等操作,通常model 层会和ViewModel 使用同一个类进行封装处理,这样的好处在与,当model中的数据处理完毕拿到结果时,可以通知ViewModel 中需要更新的变量进行更新,再者ViewModel 会通知View 刷新更改页面的值。

  • View: 负责页面展示,在Activity\Fragment 创建时,通过binding.setVariable()的方式 将ViewModel 绑定到页面布局中。从而打到双向绑定的效果。

导入相关包以及gradle 配置

  • 1、kotlin 和viewmodel 相关
    api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0' //viewmodel    api 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'    api 'androidx.appcompat:appcompat:1.6.0-alpha05'     api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' //协程    api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'    api 'androidx.core:core-ktx:1.8.0' //kt
    api 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'//rxlifecycler
  • 2、子目录下的build.gradle中配置,以下不配置的话,kotlin 会报错。
plugins {    id 'org.jetbrains.kotlin.android'}apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'apply plugin: 'kotlin-kapt'kapt {    generateStubs = true}//开启databindingandroid{  buildFeatures{        dataBinding = true    }      }
  • 3、根目录build.gradle 配置
   ext.kotlin_version ='1.7.0'    dependencies {        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"    }    plugins {    id 'com.android.application' version '7.1.3' apply false    id 'com.android.library' version '7.1.3' apply false    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false//    id 'org.jetbrains.kotlin:kotlin-gradle-plugin' version '1.7.0' apply false}
  • 4、gradle.properties 中配置
android.useAndroidX=truekotlin.code.style=officialandroid.enableJetifier=true//表示Android插件会通过重写其二进制文件来自动迁移现有的第三方库,以使用AndroidX依赖项;未设置时默认为false;//必要配置项enableJetifier

base 封装

  • 1、先看看大致也结构:

BaseActivity\BaseFragment代表View层,也就是直观看到界面组件

BaseViewModel Model层和ViewModel 的组成

IBaseViewModel 实现LifecycleObserver的一个自定义接口,让BaseViewModel 同样能感知组件的生命周期,方便我们在生命周期中处理一下逻辑。

  • 2、封装
  • BaseActivity
package com.kt.ktmvvm.basicimport android.content.Intentimport android.os.Bundleimport androidx.databinding.DataBindingUtilimport androidx.databinding.ViewDataBindingimport androidx.fragment.app.FragmentActivityimport androidx.lifecycle.ViewModelimport androidx.lifecycle.ViewModelProviderimport com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.BUNDLEimport com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.CLASSimport com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.REQUESTimport com.trello.rxlifecycle2.components.support.RxAppCompatActivityimport java.lang.reflect.ParameterizedTypeabstract class BaseActivity<V : ViewDataBinding, VM : BaseViewModel> : RxAppCompatActivity(),    IBaseView {    open var binding: V? = null    open var viewModel: VM? = null    open var viewModelId = 0    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        initViewDataBinding(savedInstanceState)        //页面接受的参数方法        initParam()        //私有的ViewModel与View的契约事件回调逻辑        registerUIChangeLiveDataCallBack()        //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册        initViewObservable()    }    private fun registerUIChangeLiveDataCallBack() {        //跳入新页面        viewModel?.getUC()?.getStartActivityEvent()?.observe(this) { params ->            params?.let {                val clz = params[CLASS] as Class<*>?                val intent = Intent(this@BaseActivity, clz)//            intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);                val bundle = params[BUNDLE]                if (bundle is Bundle) {                    intent.putExtras((bundle as Bundle?)!!)                }                startActivityForResult(intent, params[REQUEST] as Int)            }        }        viewModel?.getUC()?.getFinishResult()?.observe(this) { integer ->            integer?.let {                setResult(integer)                finish()            }        }             //关闭界面        viewModel?.getUC()?.getFinishEvent()?.observe(this) { finish() }        //关闭上一层        viewModel?.getUC()?.getOnBackPressedEvent()?.observe(this) { onBackPressed() }        viewModel?.getUC()?.getSetResultEvent()?.observe(this) { params ->            params?.let {                val intent = Intent()                if (params.isNotEmpty()) {                    val strings: Set<String> = params.keys                    for (string in strings) {                        intent.putExtra(string, params[string])                    }                }                setResult(RESULT_OK, intent)            }        }    }    private fun initViewDataBinding(savedInstanceState: Bundle?) {        //DataBindingUtil类需要在project的build中配置 dataBinding {enabled true }, 同步后会自动关联android.databinding包        binding =            DataBindingUtil.setContentView(this@BaseActivity, initContentView(savedInstanceState))        viewModelId = initVariableId()        val modelClass: Class<BaseViewModel>        val type = javaClass.genericSuperclass        modelClass = if (type is ParameterizedType) {            type.actualTypeArguments[1] as Class<BaseViewModel>        } else {            //如果没有指定泛型参数,则默认使用BaseViewModel            BaseViewModel::class.java        }        viewModel = createViewModel(this, modelClass as Class<VM>)        //关联ViewModel        binding?.setVariable(viewModelId, viewModel)        //支持LiveData绑定xml,数据改变,UI自动会更新        binding?.lifecycleOwner = this        //让ViewModel拥有View的生命周期感应        lifecycle.addObserver(viewModel!!)        //注入RxLifecycle生命周期        viewModel?.injectLifecycleProvider(this)    }    override fun onDestroy() {        super.onDestroy()        binding?.unbind()    }    /**     * 创建ViewModel 如果 需要自己定义ViewModel 直接复写此方法     *     * @param cls     * @param      * @return     */    open fun <T : ViewModel> createViewModel(activity: FragmentActivity?, cls: Class<T>?): T {        return ViewModelProvider(activity!!)[cls!!]    }    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {        super.onActivityResult(requestCode, resultCode, data)        viewModel?.onActivityResult(requestCode, resultCode, data)    }    /**     * 提供livedata 或者flow 数据流观察回调     */    override fun initViewObservable() {    }    /**     * 返回vaeriableId     */    abstract fun initVariableId(): Int    /**     * 返回布局id     */    abstract fun initContentView(savedInstanceState: Bundle?): Int}
  • BaseFragment 和BaseActivity 差不多
package com.kt.ktmvvm.basicimport android.content.Intentimport android.os.Bundleimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport androidx.databinding.DataBindingUtilimport androidx.databinding.ViewDataBindingimport androidx.fragment.app.Fragmentimport androidx.fragment.app.FragmentActivityimport androidx.lifecycle.ViewModelimport androidx.lifecycle.ViewModelProviderimport com.trello.rxlifecycle2.components.support.RxAppCompatActivityimport com.trello.rxlifecycle2.components.support.RxFragmentimport java.lang.reflect.ParameterizedTypeabstract class BaseFragment<V : ViewDataBinding, VM : BaseViewModel> : RxFragment(), IBaseView {    open var binding: V? = null    open var viewModel: VM? = null    open var viewModelId = 0    @Deprecated("Deprecated in Java")    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        initParam()    }    override fun onCreateView(        inflater: LayoutInflater,        container: ViewGroup?,        savedInstanceState: Bundle?    ): View? {        binding = DataBindingUtil.inflate<ViewDataBinding>(            inflater,            initContentView(inflater, container, savedInstanceState),            container,            false        ) as V?        return binding?.root    }    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {        super.onViewCreated(view, savedInstanceState)        //私有的初始化Databinding和ViewModel方法        initViewDataBinding()        //私有的ViewModel与View的契约事件回调逻辑        registerUIChangeLiveDataCallBack()        //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册        initViewObservable()    }    private fun registerUIChangeLiveDataCallBack() {        //跳入新页面        viewModel?.getUC()?.getStartActivityEvent()?.observe(this) { params ->            params?.let {                val clz = params[BaseViewModel.Companion.ParameterField.CLASS] as Class<*>?                val intent = Intent(activity, clz)//            intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);                val bundle = params[BaseViewModel.Companion.ParameterField.BUNDLE]                if (bundle is Bundle) {                    intent.putExtras((bundle as Bundle?)!!)                }                this@BaseFragment.startActivityForResult(                    intent,                    params[BaseViewModel.Companion.ParameterField.REQUEST] as Int                )            }        }        viewModel?.getUC()?.getFinishResult()?.observe(this) { integer ->            integer?.let {                activity?.setResult(integer)                activity?.finish()            }        }        //关闭界面        viewModel?.getUC()?.getFinishEvent()?.observe(this) { activity?.finish() }        //关闭上一层        viewModel?.getUC()?.getOnBackPressedEvent()?.observe(this) { activity?.onBackPressed() }        viewModel?.getUC()?.getSetResultEvent()?.observe(this) { params ->            params?.let {                val intent = Intent()                if (params.isNotEmpty()) {                    val strings: Set<String> = params.keys                    for (string in strings) {                        intent.putExtra(string, params[string])                    }                }                activity?.setResult(RxAppCompatActivity.RESULT_OK, intent)            }        }    }    private fun initViewDataBinding() {        viewModelId = initVariableId()        viewModelId = initVariableId()        val modelClass: Class<BaseViewModel>        val type = javaClass.genericSuperclass        modelClass = if (type is ParameterizedType) {            type.actualTypeArguments[1] as Class<BaseViewModel>        } else {            //如果没有指定泛型参数,则默认使用BaseViewModel            BaseViewModel::class.java        }        viewModel = createViewModel(this, modelClass as Class<VM>)        //关联ViewModel        binding?.setVariable(viewModelId, viewModel)        //支持LiveData绑定xml,数据改变,UI自动会更新        binding?.lifecycleOwner = this        //让ViewModel拥有View的生命周期感应        lifecycle.addObserver(viewModel!!)        //注入RxLifecycle生命周期        viewModel?.injectLifecycleProvider(this)    }    open fun <T : ViewModel> createViewModel(fragment: Fragment?, cls: Class<T>?): T {        return ViewModelProvider(fragment!!)[cls!!]    }    /**     * 返回variableid     */    abstract fun initVariableId(): Int    /**     * 返回布局id     */    abstract fun initContentView(        inflater: LayoutInflater?,        container: ViewGroup?,        savedInstanceState: Bundle?    ): Int}
  • BaseViewModel 封装
package com.kt.ktmvvm.basicimport android.app.Activityimport android.app.Applicationimport android.content.Intentimport android.os.Bundleimport androidx.lifecycle.*import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.BUNDLEimport com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.CLASSimport com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.REQEUST_DEFAULTimport com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.REQUESTimport com.kt.ktmvvm.net.ExceptionUtilimport com.trello.rxlifecycle2.LifecycleProviderimport kotlinx.coroutines.CoroutineExceptionHandlerimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.launchimport java.lang.ref.WeakReferenceopen class BaseViewModel(application: Application) : AndroidViewModel(application), IBaseViewModel {    private var mLifecycle: WeakReference<LifecycleProvider<*>>? = null    private var uc: UIChangeLiveData? = null    //生命周期管理         override fun onAny(owner: LifecycleOwner?, event: Lifecycle.Event?) {    }    override fun onCreate() {    }    override fun onDestroy() {    }    override fun onStart() {    }    override fun onStop() {    }    override fun onResume() {    }    override fun onPause() {    }    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {    }    /**     * 注入RxLifecycle生命周期     *     * @param lifecycle     */    fun injectLifecycleProvider(lifecycle: LifecycleProvider<*>?) {        mLifecycle = WeakReference(lifecycle)    }    /**     * 跳转页面     *     * @param clz 所跳转的目的Activity类     */    fun startActivity(clz: Class<out Activity?>?) {        startActivity(clz, null)    }    /**     * @param clz  clz 所跳转的目的Activity类     * @param code 启动requestCode     */    fun startActivity(clz: Class<out Activity?>?, code: Int) {        startActivity(clz, null, code)    }    /**     * 跳转页面     *     * @param clz    所跳转的目的Activity类     * @param bundle 跳转所携带的信息     */    private fun startActivity(clz: Class<out Activity?>?, bundle: Bundle?) {        startActivity(clz, bundle, REQEUST_DEFAULT)    }    /**     * 跳转页面     *     * @param clz    所跳转的目的Activity类     * @param bundle 跳转所携带的信息     */    fun startActivity(clz: Class<out Activity?>?, bundle: Bundle?, requestCode: Int) {        val params: MutableMap<String, Any?> = HashMap()        params[CLASS] = clz        params[REQUEST] = requestCode        params[BUNDLE] = bundle        uc?.getStartActivityEvent()?.postValue(params as Map<String, Any>)    }    fun startActivityForFragment(clz: Class<out Activity?>, bundle: Bundle, requestCode: Int) {        val params: MutableMap<String, Any> = HashMap()        params[CLASS] = clz        params[REQUEST] = requestCode        params[BUNDLE] = bundle        uc?.getStartActivityForFragment()?.postValue(params)    }    /**     * 关闭界面     */    fun finish() {        uc?.getFinishEvent()?.postValue(null)    }    /**     * 携带code的 finish     */    fun finishFragmentResult() {        uc?.getResultFragment()?.postValue(null)    }    /**     * 返回上一层     */    fun onBackPressed() {        uc?.getOnBackPressedEvent()!!.postValue(null)    }    fun getUC(): UIChangeLiveData? {        if (uc == null) {            uc = UIChangeLiveData()        }        return uc    }    companion object {        class UIChangeLiveData : SingleLiveEvent<Any?>() {            private var startActivityEvent: SingleLiveEvent<Map<String, Any>>? = null            private var finishEvent: SingleLiveEvent<Void>? = null            private var onBackPressedEvent: SingleLiveEvent<Void>? = null            private var setResultEvent: SingleLiveEvent<Map<String, String>>? = null            private var finishResult: SingleLiveEvent<Int>? = null            private var startActivityForFragment: SingleLiveEvent<Map<String, Any>>? = null            private var setResultFragment: SingleLiveEvent<Map<String, Any>>? = null            fun getResultFragment(): SingleLiveEvent<Map<String, Any>> {                return createLiveData(setResultFragment).also {                    setResultFragment = it                }            }            fun getStartActivityForFragment(): SingleLiveEvent<Map<String, Any>> {                return createLiveData(startActivityForFragment).also {                    startActivityForFragment = it                }            }            fun getFinishResult(): SingleLiveEvent<Int> {                return createLiveData(finishResult ).also {                    finishResult = it                }            }            fun getStartActivityEvent(): SingleLiveEvent<Map<String, Any>> {                return createLiveData(startActivityEvent).also {                    startActivityEvent = it                }            }            fun getSetResultEvent(): SingleLiveEvent<Map<String, String>> {                return createLiveData(setResultEvent).also {                    setResultEvent = it                }            }            fun getFinishEvent(): SingleLiveEvent<Void> {                return createLiveData(finishEvent).also {                    finishEvent = it                }            }            fun getOnBackPressedEvent(): SingleLiveEvent<Void> {                return createLiveData(onBackPressedEvent).also {                    onBackPressedEvent = it                }            }            private fun <T> createLiveData(liveData: SingleLiveEvent<T>?): SingleLiveEvent<T> {                var mLive: SingleLiveEvent<T>?=liveData                liveData?.let {                    return mLive!!                }?:let {                    mLive= SingleLiveEvent()                }                return mLive!!            }            override fun observe(owner: LifecycleOwner, observer: Observer<in Any?>) {                super.observe(owner, observer)            }        }        object ParameterField {            const val CLASS = "CLASS"            const val CANONICAL_NAME = "CANONICAL_NAME"            const val BUNDLE = "BUNDLE"            const val REQUEST = "REQUEST"            const val REQEUST_DEFAULT = 1        }    }}

使用方式

  • 1、创建model
package com.kt.ktmvvmimport android.app.Applicationimport android.util.Logimport android.widget.Toastimport androidx.databinding.ObservableFieldimport androidx.lifecycle.viewModelScopeimport com.kt.ktmvvm.basic.BaseViewModelimport com.kt.ktmvvm.download.DownloadActivityimport com.kt.ktmvvm.jetpack.coordinatorlayout.CoordinatorActivityimport com.kt.ktmvvm.jetpack.room.RoomActivityimport com.kt.ktmvvm.jetpack.viewpager.ViewPager2Activityimport com.kt.ktmvvm.net.ApiExceptionimport com.kt.ktmvvm.net.DataServiceimport kotlinx.coroutines.launchimport kotlin.math.logopen class MainViewModel(application: Application) : BaseViewModel(application) {    companion object {        val TAG: String? = MainViewModel::class.simpleName    }      /**     * 跳转viewpager2     */    open fun goViewPager2() {        startActivity(ViewPager2Activity::class.java)    }    /**     * 进入room数据库     */    open fun goRoom() {        startActivity(RoomActivity::class.java)    }    /**     * 进入coordinator     */    open fun goCoordinator() {        startActivity(CoordinatorActivity::class.java)    }    /**     * 进入下载器     */    open fun goDownloadManager() {        startActivity(DownloadActivity::class.java)    }}
  • 2.创建布局
<?xml version="1.0" encoding="utf-8"?><layout xmlns:tools="http://schemas.android.com/tools">    <data>        <variable            name="model"            type="com.kt.ktmvvm.MainViewModel" />    </data>    <androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"        xmlns:app="http://schemas.android.com/apk/res-auto"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center_horizontal"        android:orientation="vertical"        tools:context=".MainActivity">    </androidx.appcompat.widget.LinearLayoutCompat></layout>

布局中需要用 layout 和data 包裹并导入 model路径,如上.

  • 3.创建activity
package com.kt.ktmvvmimport android.os.Bundleimport android.view.LayoutInflaterimport androidx.databinding.DataBindingUtilimport com.kt.ktmvvm.basic.BaseActivityimport com.kt.ktmvvm.databinding.ActivityMainNewBindingclass MainActivity : BaseActivity<ActivityMainNewBinding, MainViewModel>() {    override fun initParam() {    }    override fun initVariableId(): Int {        return BR.model    }    override fun initContentView(savedInstanceState: Bundle?): Int {        return R.layout.activity_main_new    }}

代码已上传github:https://github.com/ljlstudio/KtMvvm
以上就是使用kotlin 语言编写的MVVM 设计模式架构,使用起来非常方便,也很好维护,只需维护model中的业务逻辑即可。

下一篇:《Kotlin系列》之协程搭配Retrofit+OkHttp3网络请求封装