在接触MVP之后也了解了MVVM的部分,当时DataBinding还未成熟,也就没有细致的看过,而现在DateBinding也已经很完善,相比较于MVP我更加推荐使用MVVM进行开发,而其也需要搭配一些其他的几个框架,首先就是他的本质框架,DataBinding,而还有一个也非常合适于MVVM和MVP的框架,Dagger2。
关于Dagger2的部分之后再单独进行介绍。

MVVM架构思想

  • M(Model):控制着架构上的逻辑等的实体模型。
  • VM(ViewModel):与mvp不同的是ViewModel也是中间桥梁,但他并不需要契约类进行连接,而是使用DataBinding着一框架进行单向或双向的绑定。
  • V(View):代表着Activity与xml的ui界面内容。

MVVM的关键思想还是在于数据绑定,原本的mvc或是mvp每次的ui更新,其上的数据也需要随着变化都是从代码中跟随逻辑进行变化,而使用MVVM将使控件与其需要的内容进行绑定,每一次的数据更新将自动进行ui的更新。

依旧还是需要动手实践一下:

准备工作

MVVM框架由谷歌推荐,所以也拥有它特别的位置,从databinding开启的位置就可以看出来:
在gradle文件中添加下面几行代码即可开启databinding:

1
2
3
dataBinding {
enabled true
}

非常简单的开启方式。

xml界面

简单写一个界面作为示范,选用一个每日一文的接口:https://interface.meiriyiwen.com/article/today

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.xblydxj.mvvmdemo.Article.DataBean"/>
<variable
name="article"
type="DataBean"/>
</data>
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xblydxj.mvvmdemo.MainActivity">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/time + article.date.curr}"
android:layout_margin="5dp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/author + article.author}"
android:layout_margin="5dp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/title + article.title}"
android:layout_margin="5dp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/digest + article.digest}"
android:layout_margin="5dp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/content + article.content}"
android:layout_margin="5dp"/>
</LinearLayout>
</layout>

xml的变化非常明显,首先最外层的包裹已经变化了,根节点为标签,而上方使用标签将本页面所需要的内容提供出来,下方即为正常的ui界面。

bean的提供

根据每日一文的json提供,我们先写出他对应的bean类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Article {
public DataBean data;
public static class DataBean {
public DateBean date;
public String author;
public String title;
public String digest;
public String content;
public int wc;
public static class DateBean {
public String curr;
public String prev;
public String next;
}
}
}

去除了一些set,get方法,改为public,以便调用。
转向xml部分:
在上部分的data标签中,我们需要提供变量,以供显示:

1
2
3
4
5
6
<data>
<import type="com.xblydxj.mvvmdemo.Article.DataBean"/>
<variable
name="article"
type="DataBean"/>
</data>

在其中使用import标签注入,在定义一个变量以供调用。
而下方每一个用于显示内容的textview,其text的显示属性将上方提供的数据进行绑定:

1
android:text="@{article.content}"

用于显示bean中content内容的TextView也将上方data提供的article中的content进行绑定,格式也真是这样。

Activty部分

这一部分完成双向绑定的工作,但和mvp不同的是,由于大部分ui是由数据控制进行改变的,所以mvvm中的activity已经节省了许多工作,所以这里的activity也包含着model层的工作,依旧是集中的。

其实这一点是值得吐槽的:MVP架构整体的解耦非常彻底,但是用于工程时很明显的一个弊端就是,类文件增加翻倍,而mvvm并没有这样的问题,他更自由。

这里为了方便,我再gradle中添加了rxjava与retrofit的依赖用于网络请求部分的编写。

1
2
3
4
5
6
7
8
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okio:okio:1.9.0'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.2.1'
compile 'com.artemzin.rxjava:proguard-rules:1.2.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'

在进行activity书写前,我们确保了xml文件的正确性,在studio中提前进行一次编译,这时databinding将为我们生成一个文件,原xml文件名为:activity_main.xml 而对应的生成的类文件名即为:ActivityMainBinding。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MainActivity extends AppCompatActivity {

ActivityMainBinding mBinding;

Api mRetrofit = new Retrofit.Builder()
.baseUrl("https://interface.meiriyiwen.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient.Builder().build())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(Api.class);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mRetrofit.getArticle()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Article>() {
@Override
public void call(Article article) {
mBinding.setArticle(article.data);
}
});
}
}

如上,非常的精简,除了绑定部分的两行代码以外,剩余部分都是网络请求的一些初始化,一些mvc和mvp中需要做的关于ui与数据的显示已经全部由databinding帮我们做了。

在activity中首先我们在onCreate里去除了原有的setContentView的这行代码,而改为:

1
ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

抽取为成员变量以便调用。这一步即是替代了原有的setContentView。

之后便是直接开始retrofit的网络请求部分,先进行他的api定义:

1
2
3
4
public interface Api {
@GET("article/today")
Observable<Article> getArticle();
}

后续步骤就在activity中进行,成员变量的初始化,然后由retrofit进行请求,之后再在订阅成功后的next中将databinding需要的数据提供给他:

1
mBinding.setArticle(article.data);

这样即完成了一个简单的MVVM的构建。

总结

一个比较关键的使用,是关于recyclerview或是listview在这里的使用。databinding的recyclerview部分有些麻烦,但思想依旧是相同的,具体可以看这篇博客:精通DataBinding

但说回来,再某些时候,如果觉得使用databinding不熟练或是不方便,可以立马就转回原有的mvc写法,只是数据的部分再次归还给了activity,但不影响我们项目整体的编写。

如今databinding已经成熟,从项目的角度讲,相比较于MVP我更加推荐MVVM的写法。ui与逻辑的分离更加彻底。并且没有更多的文件增加。熟练掌握databinding之后会很舒服。