- Android 架构模式之 MVC
- Android 架构模式之 MVP
- Android 架构模式之 MVVM
各人好!
作为 Android 步伐猿,你认识 MVVM 架构吗。学过了 MVC 架构、MVP 架构,为什么还要继续 MVVM 架构?又是什么缘故原由导致它让人又爱又恨?
架构计划的目标
通过计划使步伐模块化,模块内 高内聚、模块间 低耦合,提高开辟效率,便于复用及后续维护。
对 MVVM 的理解
上图是 MVVM 的架构图,我们都知道,MVVM架构中 M 代表 Model(模型)、V 代表 View(视图)、VM 代表 ViewModel(视图模型)。它们的职责分别是:
- View 负责吸取用户的输入事件,然后将事件转达给 ViewModel;
- ViewModel 收到事件后,会进行业务分发,通知 Model 获取数据;
- Model 区分数据来源,进而通过不同渠道获取数据,拿到数据后返回给 ViewModel;
- ViewModel 进行后续处理,或者通知 View 更新 UI。
假如有看过 MVP 架构,会感觉这两个是一样的,不消怀疑,就是一样的,还有 MVC 也是一样的,因为这些都是从 MVC 演变过来的,只是每次演变都是为相识决特定的问题。
区别是实现方式不一样了,MVVM 架构是基于框架实现,变成了基于数据驱动。MVVM 是基于 DataBinding 框架进行数据 单向/双向 绑定,实现了 View 和 Model 的数据同步,这种方式增强了 xml 的能力,使得 Activity/Fragment 可以专职维护 View 的初始化,同时也减少了不少编码任务,这也体现了框架的强大之处。但是这里我们仅引入 DataBinding 库,以最少的引入,来相识 MVVM 架构的思绪,至于那些常用的开辟库,他们只是在 MVVM 架构的底子之上帮我们大大提高了开辟效率、规避可能存在的问题风险。
代码实现
Model
BaseModel.java
- public abstract class BaseModel {
- }
复制代码 View
BaseActivity.java
- public abstract class BaseActivity<B extends ViewDataBinding, VM extends BaseViewModel> extends AppCompatActivity {
- protected B mBinding;
- protected VM mViewModel;
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- this.mViewModel = createViewModel();
- setVariable();
- }
- /**
- * 初始化 ViewModel
- * @return
- */
- public abstract VM createViewModel();
- /**
- * 初始化 xml 中定义的变量
- */
- public abstract void setVariable();
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mViewModel != null) {
- mViewModel.onDestroy();
- mViewModel = null;
- }
- }
- }
复制代码 ViewModel
BaseViewModel.java
- public abstract class BaseViewModel<M extends BaseModel> {
- protected M mModel;
- public BaseViewModel() {
- this.mModel = createModel();
- }
- protected abstract M createModel();
- public void onDestroy() {
- if (mModel != null) {
- mModel = null;
- }
- }
- }
复制代码 上述代码中可以看到,View 中持有 ViewModel 引用,ViewModel 中持有 Model 引用,持有顺序为正向顺序,然后通过 setVariable 函数将 View 和 ViewModel 进行关联,关联后就会通过 DataBinding 在框架层进行数据绑定,代码很简洁,职责分配的也很清楚。
Android 中 MVVM 的问题
不幸的是,在 MVVM 架构中一旦出现了问题,会是噩梦般的存在,很难发现缘故原由,以致没有提示,以是我们在编写代码的时候务必勤于调试,完成一个小功能点就看一下效果,免得写了很多功能,最后一路报错,那样在排错的时候会无从动手、毫无思绪。
ViewModel 中会定义大量的数据绑定对象,以及 getter/setter 方法,会导致 ViewModel 越来越臃肿,可以思量进一步提取操作。
Google 建议
- 编写数据驱动型 UI
- xml 与 Activity/Fragment 通过 databinding 进行关联
- Activity/Fragment 尽可能保持精简
- ViewModel 充当毗连器
- 通过 协程 处理耗时操作
- 尽可能利用 依赖项注入 最佳实践,主要是构造函数注入。
注意事项
- 避免 ViewModel 持有 任何与生命周期相关的范例的引用。如,Activity, Fragment, Context 或 Resources。假如某元素需要在 ViewModel 中利用 Context,则应重新严格评估其是否位于正确的层级中。
- AndroidViewModel 中持有 applicationContext,但官方 建议 利用 ViewModel,而非 AndroidViewModel,不应在 ViewModel 中利用 Application。正确做法是将依赖项移至界面层或数据层。
try a demo
点击按钮,哀求 wanandroid 网站的 banner 接口数据,将最后一条数据展示到UI
将 表现控件/输入控件 绑定到同一个 Bean 上,检察数据绑定的效果
MVVM 架构的 Demo 是从 MVP 架构那套代码变更过来的,只涉及上述几个文件的变动,文件数/代码量都大大减少了,这里多了一个 IUpdateListener,主要用于定义数据更新的接口,Bean 中会实现更新接口,也可以不带它。
Bean
继承自 BaseObservable,是被观察者角色,View 充当观察者。
在需要关注的属性的 getter/setter 上通过 @Bindable 和 notifyPropertyChanged(BR.xx) 进行绑定
IUpdateListener.java
- public interface IUpdateListener<T> {
- /**
- * 获取到新数据后,用于更新与 xml 绑定的实体类的属性值
- * @param t
- */
- void update(T t);
- }
复制代码 Banner.java
Model
哀求接口,获取 banner 数据
MainModel.java
- public class MainModel extends BaseModel {
- public void getNetworkBanner(ResponseCallback<List<Banner>> callback) {
- // 收到需求,请求接口数据
- NetworkRepository.getInstance().requestBanners(callback);
- }
- }
复制代码 View
包含一个 Button,点击哀求接口数据
包含一个 TextView,用于回显数据
包含一个 EditText,用于检察数据绑定 UI 效果
注:xml 的变动是很重要的部分,它的功能增强了很多
activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools">
- <data>
- <import type="com.villen.mvvm.MainViewModel" />
- <import type="com.villen.mvvm.bean.Banner" />
- <variable
- name="vm"
- type="MainViewModel" />
- <variable
- name="banner"
- type="Banner" />
- </data>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="vertical"
- tools:context=".MainActivity">
- <Button
- android:id="@+id/btn_banner_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="@{(view) -> vm.getNetworkBanner()}"
- android:text="@string/get_network_info" />
- <EditText
- android:id="@+id/et_banner_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/hint_change_data"
- android:text="@={banner.title}" />
- <TextView
- android:id="@+id/tv_banner_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@{banner.title}" />
- </LinearLayout>
- </layout>
复制代码 MainActivity.java
- public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- mBinding = ActivityMainBinding.inflate(LayoutInflater.from(this));
- setContentView(mBinding.getRoot());
- super.onCreate(savedInstanceState);
- }
- @Override
- public MainViewModel createViewModel() {
- return new MainViewModel();
- }
- @Override
- public void setVariable() {
- mBinding.setVm(mViewModel);
- mBinding.setBanner(mViewModel.getBanner());
- }
- }
复制代码 ViewModel
通过 model 获取数据
通知 UI 更新
MainViewModel.java
- public class MainViewModel extends BaseViewModel<MainModel> {
- private Banner mBanner;
- /**
- * 获取实体类对象,用于 xml 中数据绑定
- */
- public Banner getBanner() {
- if (mBanner == null) {
- mBanner = new Banner();
- }
- return mBanner;
- }
- @Override
- protected MainModel createModel() {
- return new MainModel();
- }
- /**
- * 获取 banner 数据
- */
- public void getNetworkBanner() {
- if (mModel == null) {
- return;
- }
- // 收到新需求,分发给 model 处理
- mModel.getNetworkBanner(new ResponseCallback<List<Banner>>() {
- @Override
- public void onSuccess(List<Banner> banners) {
- if (banners != null && banners.size() > 0) {
- mBanner.update(banners.get(2));
- }
- }
- @Override
- public void onFail(String msg) {
- Log.e("network", msg);
- }
- });
- }
- }
复制代码 效果展示
附上源码链接
致谢:
感谢 wanandroid 提供的开放API
参考:
Android DataBinding 从入门到进阶,看这一篇就够
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |