目前市面上流行的客户端app的架构基本都是基于MVVM 设计的,其实就是为了去更好的使用jetpack 组件,mvvm配合jetpack 去搭建的话,不仅仅在业务上达到了解耦、方便维护和review的效果,如果配合lifeCycle 的使用,更能有效的减少对象生命周期的控制问题导致的内存泄漏。而使用kotlin的初衷,如flow, 能很好的替代RxJava 和jetpack 中的LiveData,如协程能更轻量合理减少的对于线程的创建开销、线程切换负责、线程关闭等一系列繁琐的操作。所以,使用kotlin 和 mvvm +jetpack 组件去搭建的的框架思想源于此。
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 绑定到页面布局中。从而打到双向绑定的效果。
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
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
}
}
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
}
android.useAndroidX=true
kotlin.code.style=official
android.enableJetifier=true//表示Android插件会通过重写其二进制文件来自动迁移现有的第三方库,以使用AndroidX依赖项;未设置时默认为false;
//必要配置项enableJetifier
BaseActivity\BaseFragment代表View层,也就是直观看到界面组件
BaseViewModel Model层和ViewModel 的组成
IBaseViewModel 实现LifecycleObserver的一个自定义接口,让BaseViewModel 同样能感知组件的生命周期,方便我们在生命周期中处理一下逻辑。
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
}
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
}
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
}
}
}
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)
}
}
<?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路径,如上.
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网络请求封装