《Kotlin系列》之MVVM架构封装(kotlin+mvvm)

前言

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

mvvm 图解

《Kotlin系列》之MVVM架构封装(kotlin+mvvm)_第1张图片
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
}

//开启databinding
android{
  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=true
kotlin.code.style=official
android.enableJetifier=true//表示Android插件会通过重写其二进制文件来自动迁移现有的第三方库,以使用AndroidX依赖项;未设置时默认为false;
//必要配置项enableJetifier

base 封装

  • 1、先看看大致也结构:
    《Kotlin系列》之MVVM架构封装(kotlin+mvvm)_第2张图片

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

BaseViewModel Model层和ViewModel 的组成

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

  • 2、封装
  • BaseActivity

package com.kt.ktmvvm.basic

import android.content.Intent
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.BUNDLE
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.CLASS
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.REQUEST
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity
import java.lang.reflect.ParameterizedType

abstract 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.basic

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity
import com.trello.rxlifecycle2.components.support.RxFragment

import java.lang.reflect.ParameterizedType

abstract 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.basic

import android.app.Activity
import android.app.Application
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.*
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.BUNDLE
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.CLASS
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.REQEUST_DEFAULT
import com.kt.ktmvvm.basic.BaseViewModel.Companion.ParameterField.REQUEST
import com.kt.ktmvvm.net.ExceptionUtil
import com.trello.rxlifecycle2.LifecycleProvider
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference

open 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.ktmvvm

import android.app.Application
import android.util.Log
import android.widget.Toast
import androidx.databinding.ObservableField
import androidx.lifecycle.viewModelScope
import com.kt.ktmvvm.basic.BaseViewModel
import com.kt.ktmvvm.download.DownloadActivity
import com.kt.ktmvvm.jetpack.coordinatorlayout.CoordinatorActivity
import com.kt.ktmvvm.jetpack.room.RoomActivity
import com.kt.ktmvvm.jetpack.viewpager.ViewPager2Activity
import com.kt.ktmvvm.net.ApiException
import com.kt.ktmvvm.net.DataService
import kotlinx.coroutines.launch
import kotlin.math.log

open 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.ktmvvm

import android.os.Bundle
import android.view.LayoutInflater
import androidx.databinding.DataBindingUtil
import com.kt.ktmvvm.basic.BaseActivity
import com.kt.ktmvvm.databinding.ActivityMainNewBinding

class 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网络请求封装

你可能感兴趣的:(kotlin,架构,android,mvvm,jetpack)