一、 MVVM 架构的核心概念
MVVM 是 Model-View-ViewModel 的缩写,是一种基于数据驱动的架构模式,核心是分离视图(UI)和业务逻辑,通过数据绑定实现视图与数据的自动同步。
它包含四个核心组件(部分资料将其归纳为三个):
-
Model(模型)
- 负责数据的存储和处理,包括业务逻辑、网络请求、数据库操作等。
- 不依赖任何其他组件,只关注数据本身(如实体类、数据源、仓库层等)。
示例:用户信息实体类、网络接口调用工具、数据库操作类。
-
View(视图)
- 负责展示 UI 和接收用户交互(如按钮点击、输入框输入等)。
- 不包含业务逻辑,仅通过数据绑定关联 ViewModel,当数据变化时自动更新。
示例:Android 中的 Activity/Fragment、iOS 中的 ViewController、前端的 HTML 页面。
-
ViewModel(视图模型)
- 作为 View 和 Model 的中间层,负责处理 View 的业务逻辑,并暴露可观察的数据给 View。
- 持有 Model 的引用,通过 Model 获取数据后转换为 View 可直接使用的格式。
- 不依赖 View,生命周期独立于 View(如 Android 中 ViewModel 在屏幕旋转时不会重建)。
-
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了,所以,放心敞开用。
调用后,如何收到接口的结果呢? 这样,我们在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文件外围,包裹一层
2、你需要在buidl.gradle中如下图,把这个属性设置为true就好,多组件情况下,在app.build.gradle下设置一次就ok
手把手教学,舒服。 稍后再讲下,我封装的LiveDataBus,基于lifeCycle的 消息通信机制,完美契合MvvM架构。让观察者模式继续大行其道。