我所使用的MVVM框架

304 阅读4分钟

一、 MVVM 架构的核心概念

MVVM 是 Model-View-ViewModel 的缩写,是一种基于数据驱动的架构模式,核心是分离视图(UI)和业务逻辑,通过数据绑定实现视图与数据的自动同步。

它包含四个核心组件(部分资料将其归纳为三个):

  1. Model(模型)

    • 负责数据的存储和处理,包括业务逻辑、网络请求、数据库操作等。
    • 不依赖任何其他组件,只关注数据本身(如实体类、数据源、仓库层等)。
      示例:用户信息实体类、网络接口调用工具、数据库操作类。
  2. View(视图)

    • 负责展示 UI 和接收用户交互(如按钮点击、输入框输入等)。
    • 不包含业务逻辑,仅通过数据绑定关联 ViewModel,当数据变化时自动更新。
      示例:Android 中的 Activity/Fragment、iOS 中的 ViewController、前端的 HTML 页面。
  3. ViewModel(视图模型)

    • 作为 View 和 Model 的中间层,负责处理 View 的业务逻辑,并暴露可观察的数据给 View。
    • 持有 Model 的引用,通过 Model 获取数据后转换为 View 可直接使用的格式。
    • 不依赖 View,生命周期独立于 View(如 Android 中 ViewModel 在屏幕旋转时不会重建)。
  4. Data Binding(数据绑定,可选但核心)

    • 建立 View 和 ViewModel 之间的自动关联,当 ViewModel 中的数据变化时,View 会自动更新,反之亦然(双向绑定)。
    • 减少手动更新 UI 的代码(如setText()setVisibility()),让 View 更 “被动”。

这里我不用dataBinding,具体原因如下: 1、使用了dataBinding,模块编译时间会加长 2、如果在xml中做一下setValue操作其实代码维护起来不方便,换人接受维护可能增加难度,数据源更新不统一,导致定位问题,或修改无谓耗时。得不偿失

二、我使用的MVVM框架 我这个BaseViewModel基类

public class BaseViewModel extends AndroidViewModel
        implements IBaseLifeCycleObserver {
    private final HashMap<String, Observer<?>> mObserverForeverMap = new HashMap<>();

    public BaseViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        //ViewModel销毁时会执行,同时取消所有异步任务
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner, @NonNull Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            onCleared();
        }
    }

}

解析下代码,你只需要关注 onStateChanged, 这个是实现IBaseLifeCycleObserver,相当于此viewModel监听了其绑定界面的声明周期,您可以在这个回调里做你想要的操作

下面是我封装的Activity的基类

abstract class AbsBaseActivity<VM : BaseViewModel, SV : ViewDataBinding?>
    : AbsCommonViewActivity() {
    // ViewModel
    @JvmField
    protected var viewModel: VM? = null

    // 布局view
    @JvmField
    protected var bindingView: SV? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //这个初始化layout,因人而异,可不关注
        if (initLayout() != -1) {
            val params = RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            layoutInflater.inflate(initLayout(), null, false).also {
                it.layoutParams = params
                mContentView = it
            }

            if (enableRefreshLayout()) {
                handleRefreshLayout()
            } else {
                mContentRootView?.addView(mContentView)
            }
            bindingView = DataBindingUtil.bind<SV>(mContentView!!)
           
        }

        initViewModel()

        //初始化控件
        initView()
        bindData()
       
    }


    protected abstract fun initLayout(): Int


    /**
     * 初始化ViewModel
     */
    open fun initViewModel() {
        val viewModelClass = ClassUtil.getViewModel<VM>(this)
        if (viewModelClass != null) {
            val factory = getViewModelFactory() // 获取自定义的Factory
            viewModel = if (factory != null) {
                ViewModelProvider(this, factory)[viewModelClass]
            } else {
                ViewModelProvider(this)[viewModelClass]
            }
            lifecycle.addObserver(viewModel!!)
        }
    }

    /**
     * 子类可以重写此方法,提供自定义的ViewModelProvider.Factory
     * 默认返回null,使用默认的ViewModelProvider.Factory
     */
    open fun getViewModelFactory(): ViewModelProvider.Factory? {
        return null
    }

    

    protected open fun initView() {}

    protected open fun bindData() {}


}

同理,附上我封装的Fragment基类

abstract class AbsBaseFragment<VM : BaseViewModel, SV : ViewDataBinding?> :
    AbsCommonViewFragment() {
    //VM model
    @JvmField
    protected var viewModel: VM? = null

    private var parentBindingView: SV? = null

    var bindingView: SV? = null

    override fun initData(p0: BundleGet?) {
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        parentBindingView =
            DataBindingUtil.inflate(inflater, R.layout.fragment_base, container, false)
        return parentBindingView?.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val params =
            RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
        val also = DataBindingUtil.inflate<SV>(layoutInflater, initLayout(), null, false).also {
            bindingView = it
            bindingView?.root?.layoutParams = params
            mContentView = bindingView?.root
        }
        if (enableRefreshLayout()) {
            handleRefreshLayout()
        } else {
            mFragmentContentView?.addView(mContentView)
        }

        Log.d("bindingView", "also = $also, bindView = $bindingView, mContentView = $mContentView")
        initViewModel()
        initView()
        bindData()
        registeredUIChangeLiveDataCallBack()
    }

    private fun handleRefreshLayout() {
        mSmartRefreshLayout = SmartRefreshLayout(requireContext()).apply {
            val params = RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            layoutParams = params
            addView(mContentView)
        }
        configRefreshLayout()
        mFragmentContentView?.addView(mSmartRefreshLayout)
    }

    open fun configRefreshLayout() {
        mSmartRefreshLayout?.run {
            setEnableRefresh(true)
            setEnableLoadMore(true)
            setEnablePureScrollMode(false)
            setEnableNestedScroll(true)
            setEnableOverScrollBounce(true)
            setEnableOverScrollDrag(true)
            setHeaderMaxDragRate(3f)
            setOnRefreshListener(this@AbsBaseFragment)
            setOnLoadMoreListener(this@AbsBaseFragment)
        }
    }

    protected abstract fun initLayout(): Int

    open fun initView() {}

    //用于网络请求调用
    open fun bindData() {}

    /**
     * 初始化ViewModel
     */
    /**
     * 初始化ViewModel
     */
    open fun initViewModel() {
        val viewModelClass = ClassUtil.getViewModel<VM>(this)
        if (viewModelClass != null) {
            val factory = getViewModelFactory() // 获取自定义的Factory
            viewModel = if (factory != null) {
                ViewModelProvider(this, factory)[viewModelClass]
            } else {
                ViewModelProvider(this)[viewModelClass]
            }
            lifecycle.addObserver(viewModel!!)
        }
    }

    /**
     * 子类可以重写此方法,提供自定义的ViewModelProvider.Factory
     * 默认返回null,使用默认的ViewModelProvider.Factory
     */
    open fun getViewModelFactory(): ViewModelProvider.Factory? {
        return null
    }


}

您只需要关注initViewModel中对于activity和viewModel的绑定。这很类似于MVP架构中,activity和present的绑定。当然,viewModel在感知生命周期上更胜一筹。 下面附上ClassUtil代码

object ClassUtil {
    /**
     * 获取泛型ViewModel的class对象
     */
    @JvmStatic
    fun <T> getViewModel(obj: Any): Class<T>? {
        val currentClass: Class<*> = obj.javaClass
        val tClass = getGenericClass<T>(currentClass, AndroidViewModel::class.java)
        Log.d("ClassUtil", "test tClass = $tClass")
        return if (tClass == null || tClass == AndroidViewModel::class.java || tClass == NoViewModel::class.java) {
            null
        } else tClass
    }

    private fun <T> getGenericClass(klass: Class<*>, filterClass: Class<*>): Class<T>? {
        val type = klass.genericSuperclass
        if (type == null || type !is ParameterizedType) return null
        val types = type.actualTypeArguments
        for (t in types) {
            val tClass = t as Class<T>
            if (filterClass.isAssignableFrom(tClass)) {
                return tClass
            }
        }
        return null
    }
}

这样您的代码架构就初具规模了

你问我外面怎么实现? 外部PhotoCropActivity

class PhotoCropActivity :
    CommonBusinessActivity<CommonBusinessViewModel, ActivityPhotoCropBinding>() {
    override fun initLayout(): Int {
        return R.layout.activity_photo_crop
    }

通用viewModel ->CommonBusinessViewModel, 当然你也可以每个acitivty 对应一个xxxViewMolde,只要是BaseViewModel的子类就好

open class CommonBusinessViewModel(application: Application) : BaseViewModel(application) { 

var mCheckOriginalTestingLimitedData = MutableLiveData<OriginalTestingLimitedBean>()

fun checkOriginalLimitedStatus() {
    LogUtils.d("checkOriginalLimitedStatus into ..")
    Http.getApis().requestOriginalityTestingLimitedStatus(
        getRequestData(
            BusinessRequest(
                Url.checkOriginalityTestingLimiteStatus,
                "get",
                BusinessRequest.Params(),
                76
            )
        )
    )
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : HttpObserver<OriginalTestingLimitedBean>() {
            override fun success(bean: OriginalTestingLimitedBean) {
                hideLoadingView()

                mCheckOriginalTestingLimitedData.postValue(bean)

            }

            override fun err(code: Int, s: String) {
                hideLoadingView()
                if (code != Http.SUCCESS_STATUS) {
                    MainHandlerUtils.post { showShortToast(s) }
                }
            }

            override fun end() {
            }
        })
}

}

在Activity 中怎么调用viewModel, just like

viewModel?.checkOriginalLimitedStatus()

这个viewModel是怎么来的,因为,我们在基类中初始化的时候,已经赋值,并声明是protected了,所以,放心敞开用。 1755521011606.jpg

调用后,如何收到接口的结果呢? 这样,我们在activity 中 bindData()中,你也可以在initView()中,如下写,随你

override fun bindData() {
    super.bindData()
    viewModel?.run {
        mCheckOriginalTestingLimitedData.observe(this@CommonBusinessActivity) { event ->
            LogUtils.d("mCheckOriginalTestingLimitedData receiver bean = " + event?.speed_limit +", isResumed" + "${event.module_id}")
            if (event.speed_limit == 1 ) {
                showSecondPayDialog()
            }
        }
    }
}

这样就形成了闭环,其实它使用的就是观察者模式。避免了耦合,解决了内存泄漏,非常舒服。

至于你说,这个实现的子类,这个viewBinding 怎么传入,这个很简单, 1、你只需要在你的xml layout文件外围,包裹一层

444.jpg

2、你需要在buidl.gradle中如下图,把这个属性设置为true就好,多组件情况下,在app.build.gradle下设置一次就ok

555.jpg

手把手教学,舒服。 稍后再讲下,我封装的LiveDataBus,基于lifeCycle的 消息通信机制,完美契合MvvM架构。让观察者模式继续大行其道。

嗯.png