优雅地处理MVVM中各层次关系
Posted 周文凯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优雅地处理MVVM中各层次关系相关的知识,希望对你有一定的参考价值。
转载请标明出处:
https://blog.csdn.net/xuehuayous/article/details/84255731;
本文出自:【Kevin.zhou的博客】
前言:相信大家对MVVM架构都有过一定的了解,如果不太了解的朋友可以看下我之前写的《Android中MVVM是什么?》。整体分为View、ViewModel、Model三层,View层处理用户交互,ViewModel层进行业务处理;Model层进行数据处理。那么如何进行优雅地处理他们之间的关系呢?
分层关系
首先来看下它们之间的关系,如下图所示:
简图如下:
通过以上两图我们可以看出:
- View持有ViewModel引用;
- ViewModel持有Model引用;
- View与ViewModel存在一对多关系;
- ViewModel与Model存在一对多关系。
原生实现
布局binding
使用dataBinding技术,可以轻松实现数据的双向绑定,不太了解的同学可以先看下我之前写的《认识Android中的双向绑定》,使用双向绑定可以轻松实现很多原生比较麻烦实现的效果。
Activity
在Activity
中初始化binding
:
public class MainActivity extends AppCompatActivity
private MainActivityBinding mBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
mBinding = MainActivityBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
Fragment
在Fragment
中初始化binding
:
public class MainFragment extends Fragment
private MainFragmentBinding mBinding;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
mBinding = MainFragmentBinding.inflate(inflater, container, false);
return mBinding.getRoot();
ViewModel初始化
Activity
在Activity
中初始化ViewModel
:
public class MainActivity extends AppCompatActivity
private MainViewModel mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
Fragment
在Ftagment
中初始化ViewModel
:
public class MainFragment extends Fragment
private MainViewModel mViewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
mViewModel = ViewModelProviders.of(getActivity()).get(MainViewModel.class);
这里需要说明的是使用的ViewModelProviders.of(getActivity())
当然也可以使用ViewModelProviders.of(this)
如果使用getActivity()
则在Activity
中的Fragment
与Activity
使用相同的ViewModel
。
ViewModel生命周期
有朋友不仅要问,ViewModel
生命周期是什么鬼,由于我们定义View层
仅仅处理用户交互,向用户展示、响应用户的事件,ViewModel层
进行业务逻辑处理,需要和Activity
生命周期绑定的业务处理就必须移到ViewModel层
进行处理,所以ViewModel层
也需要和View层
相同的生命周期。
还好Google在android架构组件中已经想到了这个问题,可以通过添加LifecycleObserver
的方式方便实现。
由于使用Java
和Kotlin
都实现过,这里都贴出了,还没有入门Kotlin
的朋友以后也可以尝试使用下。
编写ViewModelObserver
Java实现
public class ViewModelObserver<T extends BaseViewModel> implements LifecycleObserver
private final T mViewModel;
public ViewModelObserver(@NonNull T viewModel)
this.mViewModel = viewModel;
@OnLifecycleEvent(Event.ON_CREATE)
public void onCreate()
this.mViewModel.onCreate();
@OnLifecycleEvent(Event.ON_START)
public void onStart()
this.mViewModel.onStart();
@OnLifecycleEvent(Event.ON_RESUME)
public void onResume()
this.mViewModel.onResume();
@OnLifecycleEvent(Event.ON_PAUSE)
public void onPause()
this.mViewModel.onPause();
@OnLifecycleEvent(Event.ON_STOP)
public void onStop()
this.mViewModel.onStop();
@OnLifecycleEvent(Event.ON_DESTROY)
public void onDestroy()
this.mViewModel.onDestroy();
Kotlin实现
class ViewModelObserver<T : BaseViewModel>(private val viewModel: T) : LifecycleObserver
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate()
viewModel.onCreate()
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart()
viewModel.onStart()
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume()
viewModel.onResume()
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause()
viewModel.onPause()
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop()
viewModel.onStop()
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy()
viewModel.onDestroy()
绑定周期
Java实现
public class MainActivity extends AppCompatActivity
private MainViewModel mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
getLifecycle().addObserver(new ViewModelObserver(mViewModel));
Kotlin实现
class MainActivity : AppCompatActivity()
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
lifecycle.addObserver(ViewModelObserver(viewModel))
Model初始化
Model层
主要做数据的存储与获取,有可能是本地的,也有可能是网络的,总之在ViewModel层
使用数据的时候是不需要知道数据到底是存储在什么位置的。这一部分可以通过我之前写的《一步步封装Retrofit + RxJava2》来理解下。
Repository编写
通过《一步步封装Retrofit + RxJava2》,我们知道DataRepository
(数据仓库)是Model层
和ViewModel层
交互的外交官。
public class MainDataRepository
private CompositeDisposable mSubscriptions;
public MainDataRepository(CompositeDisposable subscriptions)
this.mSubscriptions = subscriptions;
Repository初始化
public class MainViewModel extends BaseViewModel
MainDataRepository mDataRepository;
public MainViewModel()
mDataRepository = new MainDataRepository(getSubscriptions());
实现分析
通过以上原生实现可以发现,维护各层之间关系的基本都是模板代码,写起来繁琐,但是又没有技术含量,能不能让编辑器去自动生成呢?
我们想实现如下:
- 在
DataRepository
使用@Repository
标注这是一个数据仓库;@Repository public class MainDataRepository
- 在
ViewModel
上使用@ViewModel
标注这是一个ViewModel
,如果DataRepository
的字段上使用了@Autowired
标注,则自动实例化对象;@ViewModel public class MainViewModel extends BaseViewModel @Autowired MainDataRepository mDataRepository;
- 在
Activity
和Fragment
中对binding
及ViewModel
进行@Autowired
标注,也自动实例化。public class MainActivity extends BaseActivity @Autowired private MainActivityBinding mBinding; @Autowired private MainViewModel mViewModel;
这样是不是有对MVVM
没有那么抗拒了,是不是有朋友已经拍手叫好,抓紧去实现吧,别逼逼了。
那么怎么实现呢?首先想到的是apt,我们熟识的ButterKnife
、Dagger
、EventBus
等都是通过这种方式实现的,思考良久我们会发现,使用apt很难优雅地实现。那能不能动态修改字节码文件呢?这是一个很好的方式,只是实现成本比较大,那不要紧,实现成本大,实现之后就一劳永逸了。考察了一圈动态修改字节码的框架,最终选择了ASM
,为啥是ASM
以及怎么实现的,应该是一个特别长的篇幅。这里直接把最终代码奉上。
如何使用
引入依赖
-
项目
build.gradle
添加dependenciesdependencies // ... ... classpath 'com.kevin:mvvm-plugin:latest.release'
-
模块
build.gradle
添加组件apply plugin: 'mvvm'
-
模块build.gradle开启dataBinding
dataBinding enabled true
-
模块build.gradle引入架构组件及rx依赖
dependencies // ... ... implementation 'android.arch.lifecycle:extensions:1.1.1' // latest.release annotationProcessor 'android.arch.lifecycle:compiler:1.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.3' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
简单使用
在2个数据仓库获取数据,在ViewModel层
处理然后显示在View层
,特别简单的Demo。
尽管框架中使用的Kotlin
编写,这里方便大家理解仍然使用Java
,令人欣喜的是Java
调用Kotlin
也是一样的顺畅。
编写XML
布局中仅有一个TextView
,用于显示文案。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data class=".MainActivityBinding"></data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
</layout>
编写First数据仓库
在first数据仓库中仅仅有一个获取框架名称的方法。
注意:该类需要使用@Repository
进行注解。
@Repository
public class FirstDataRepository extends DataRepository
public FirstDataRepository(@NotNull CompositeDisposable subscriptions)
super(subscriptions);
public String getFrameName()
return "Android-MVVMEasy";
编写Second数据仓库
在数据仓库中仅仅有一个获取框架描述的方法。
注意:该类需要使用@Repository
进行注解。
@Repository
public class SecondDataRepository extends DataRepository
public SecondDataRepository(@NotNull CompositeDisposable subscriptions)
super(subscriptions);
public String getFrameDesc()
return "An elegant way to use MVVM in Android.";
编写ViewMode
在ViewModel
中对FirstDataRepository
和SecondDataRepository
实例化。
注意:
1. 该类需要使用@ViewModel
注解;
2. 成员变量使用@Autowired
注解。
@ViewModel
public class MainViewModel extends BaseViewModel
@Autowired
private FirstDataRepository mFirstRepository;
@Autowired
private SecondDataRepository mSecondRepository;
public String getText()
return mFirstRepository.getFrameName() + ": " + mSecondRepository.getFrameDesc();
编写Activity
在Activity
中对MainActivityBinding
和MainViewModel
实例化。
注意:成员变量使用@Autowired
注解。
public class MainActivity extends BaseActivity
@Autowired
private MainActivityBinding mBinding;
@Autowired
private MainViewModel mViewModel;
@Override
protected void onResume()
super.onResume();
String text = mViewModel.getText();
mBinding.textView.setText(text);
至此,最简单的Demo就完成了,只是使用了几个注解就完成了各层之间的关系管理及初始化。当然View层
可以方便地使用多个ViewModel
,ViewModel层
可以方便地使用多个DataRepository
。既可以省去了复杂的模板代码,又可以防止搭档把代码写错层次。
以上是关于优雅地处理MVVM中各层次关系的主要内容,如果未能解决你的问题,请参考以下文章