选择恐惧症的福音!教你认清MVC,MVP和MVVM(二)
2016-08-19 10:07
543 查看
接上文
get()和change()这两个方法是我们点击按钮以后执行的,可以看到,里面完完全全没有任何和model层逻辑相关的东西,只是简单的委托给了presenter,那我们再看看presenter层做了什么
public
class
ContributorPresenter
extends
MvpBasePresenter<ContributorView>
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorStart();
}
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
topContributor)
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorComplete(topContributor);
}
}
};
public
void
get(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
public
void
change(){
ContributorView
view
= getView();
if(view
!= null){
view.onChangeContributorName("zjutkz");
}
}
}
其实就是把刚才MVC中activity的那部分和model层相关的逻辑抽取了出来,并且在相应的时机调用ContributorView接口对应的方法,而我们的activity是实现了这个接口的,自然会走到对应的方法中。
好了,我们来捋一捋。
首先,和MVC最大的不同,MVP把activity作为了view层,通过代码也可以看到,整个activity没有任何和model层相关的逻辑代码,取而代之的是把代码放到了presenter层中,presenter获取了model层的数据之后,通过接口的形式将view层需要的数据返回给它就OK了。
这样的好处是什么呢?首先,activity的代码逻辑减少了,其次,view层和model层完全解耦,具体来说,如果你需要测试一个http请求是否顺利,你不需要写一个activity,只需要写一个java类,实现对应的接口,presenter获取了数据自然会调用相应的方法,相应的,你也可以自己在presenter中mock数据,分发给view层,用来测试布局是否正确。
MVVM
首先在看这段内容之前,你需要保证你对data binding框架有基础的了解。不了解的同学可以去看下这篇文章。在接下去让我们开始探索MVVM,MVVM最近在Android上可谓十分之火,最主要的原因就是谷歌推出了data binding这个框架,可以轻松的实现MVVM。但是,我在网上查阅关于Android的data binding资料的时候,发现国内有很多人都误解了,首先,我们从一篇错误的文章开始。当然我在这里引用这篇文章也是对事不对人,如果对文章的作者产生了不好的影响我这里说一声抱歉。
上面那篇文章是一个关于data binding的使用,看起来很美好,但是,其中有一个错误可以说是非常,非常,非常严重的。
它竟然说data binding的viewmodel层是binding类,其实不止是这篇文章,其他有一些开发者写的关于data binding的文章里都犯了一样的错误。大家如果也有这样的概念,请务必纠正过来!!
说完了错误的概念,那data binding中真正的viewmodel是什么呢?我们还是以之前MVC,MVP的那个例子做引导。
首先是view层,这没啥好说的,和MVP一样,只不过多了数据绑定。view层就是xml和activity。
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="contributor"
type="zjutkz.com.mvvm.viewmodel.Contributor"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
android:orientation="vertical"
android:fitsSystemWindows="true">
<Button
android:id="@+id/get"
android:text="get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="get"/>
<Button
android:id="@+id/change"
android:text="change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="change"/>
<TextView
android:id="@+id/top_contributor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="30sp"
android:text="@{contributor.login}"/>
</LinearLayout>
</layout>
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
如果你对data binding框架是有了解的,上面的代码你能轻松的看懂。
那model层又是什么呢?当然就是那些和数据相关的类,GithubApi等等。
重点来了,viewmodel层呢?好吧,viewmodel层就是是Contributor类!大家不要惊讶,我慢慢的来说。
public
class
Contributor
extends
BaseObservable{
private
String
login;
private
int
contributions;
@Bindable
public
String
getLogin(){
return
login;
}
@Bindable
public
int
getContributions(){
return
contributions;
}
public
void
setLogin(String
login){
this.login
= login;
notifyPropertyChanged(BR.login);
}
public
void
setContributions(int
contributions){
this.contributions
= contributions;
notifyPropertyChanged(BR.contributions);
}
@Override
public
String
toString()
{
return
login
+ ",
"
+ contributions;
}
}
我们可以看到,Contributor和MVP相比,继承自了BaseObservable,有基础的同学都知道这是为了当Contributor内部的variable改变的时候ui可以同步的作出响应。
我为什么说Contributor是一个viewmodel呢。大家还记得viewmodel的概念吗?view和viewmodel相互绑定在一起,viewmodel的改变会同步到view层,从而view层作出响应。这不就是Contributor和xml中那些组件元素的关系吗?所以,大家不要被binding类迷惑了,data binding框架中的viewmodel是自己定义的那些看似是model类的东西!比如这里的Contributor!
话说到这里,那binding类又是什么呢?其实具体对应到之前MVVM的那张图就很好理解了,我们想一下,binding类的工作是什么?
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
binding.setContributor(contributor);
首先,binding要通过DataBindingUtil.setContentView()方法将xml,也就是view层设定。
接着,通过setXXX()方法将viewmodel层注入进去。
由于这两个工作,view层(xml的各个组件)和viewmodel层(contributor)绑定在了一起。
好了,大家知道了吗,binding类,其实就是上图中view和viewmodel中间的那根线啊!!
真理在荒谬被证实以前,都只是暗室里的装饰
前面讨论了MVC,MVP和MVVM具体的实现方案,大家肯定都了解了它们三者的关系和使用方式。但是,这里我想说,不要把一个框架看作万能的,其实MVP和MVVM都是有自己的缺陷的!下面我一一来说。
MVP
MVP的问题在于,由于我们使用了接口的方式去连接view层和presenter层,这样就导致了一个问题,如果你有一个逻辑很复杂的页面,你的接口会有很多,十几二十个都不足为奇。想象一个app中有很多个这样复杂的页面,维护接口的成本就会非常的大。
这个问题的解决方案就是你得根据自己的业务逻辑去斟酌着写接口。你可以定义一些基类接口,把一些公共的逻辑,比如网络请求成功失败,toast等等放在里面,之后你再定义新的接口的时候可以继承自那些基类,这样会好不少。
MVVM
MVVM的问题呢,其实和MVC有一点像。data binding框架解决了数据绑定的问题,但是view层还是会过重,大家可以看我上面那个MVVM模式下的activity
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
大家有没有发现,activity在MVVM中应该是view层的,但是里面却和MVC一样写了对model的处理。有人会说你可以把对model的处理放到viewmodel层中,这样不是更符合MVVM的设计理念吗?这样确实可以,但是progressDialog的show和dismiss呢?你怎么在viewmodel层中控制?这是view层的东西啊,而且在xml中也没有,我相信会有解决的方案,但是我们有没有一种更加便捷的方式呢?
接下文
[b]接上文[/b]
get()和change()这两个方法是我们点击按钮以后执行的,可以看到,里面完完全全没有任何和model层逻辑相关的东西,只是简单的委托给了presenter,那我们再看看presenter层做了什么
public
class
ContributorPresenter
extends
MvpBasePresenter<ContributorView>
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorStart();
}
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
topContributor)
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorComplete(topContributor);
}
}
};
public
void
get(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
public
void
change(){
ContributorView
view
= getView();
if(view
!= null){
view.onChangeContributorName("zjutkz");
}
}
}
其实就是把刚才MVC中activity的那部分和model层相关的逻辑抽取了出来,并且在相应的时机调用ContributorView接口对应的方法,而我们的activity是实现了这个接口的,自然会走到对应的方法中。
好了,我们来捋一捋。
首先,和MVC最大的不同,MVP把activity作为了view层,通过代码也可以看到,整个activity没有任何和model层相关的逻辑代码,取而代之的是把代码放到了presenter层中,presenter获取了model层的数据之后,通过接口的形式将view层需要的数据返回给它就OK了。
这样的好处是什么呢?首先,activity的代码逻辑减少了,其次,view层和model层完全解耦,具体来说,如果你需要测试一个http请求是否顺利,你不需要写一个activity,只需要写一个java类,实现对应的接口,presenter获取了数据自然会调用相应的方法,相应的,你也可以自己在presenter中mock数据,分发给view层,用来测试布局是否正确。
MVVM
首先在看这段内容之前,你需要保证你对data binding框架有基础的了解。不了解的同学可以去看下这篇文章。在接下去让我们开始探索MVVM,MVVM最近在Android上可谓十分之火,最主要的原因就是谷歌推出了data binding这个框架,可以轻松的实现MVVM。但是,我在网上查阅关于Android的data binding资料的时候,发现国内有很多人都误解了,首先,我们从一篇错误的文章开始。当然我在这里引用这篇文章也是对事不对人,如果对文章的作者产生了不好的影响我这里说一声抱歉。
上面那篇文章是一个关于data binding的使用,看起来很美好,但是,其中有一个错误可以说是非常,非常,非常严重的。
它竟然说data binding的viewmodel层是binding类,其实不止是这篇文章,其他有一些开发者写的关于data binding的文章里都犯了一样的错误。大家如果也有这样的概念,请务必纠正过来!!
说完了错误的概念,那data binding中真正的viewmodel是什么呢?我们还是以之前MVC,MVP的那个例子做引导。
首先是view层,这没啥好说的,和MVP一样,只不过多了数据绑定。view层就是xml和activity。
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="contributor"
type="zjutkz.com.mvvm.viewmodel.Contributor"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
android:orientation="vertical"
android:fitsSystemWindows="true">
<Button
android:id="@+id/get"
android:text="get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="get"/>
<Button
android:id="@+id/change"
android:text="change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="change"/>
<TextView
android:id="@+id/top_contributor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="30sp"
android:text="@{contributor.login}"/>
</LinearLayout>
</layout>
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
如果你对data binding框架是有了解的,上面的代码你能轻松的看懂。
那model层又是什么呢?当然就是那些和数据相关的类,GithubApi等等。
重点来了,viewmodel层呢?好吧,viewmodel层就是是Contributor类!大家不要惊讶,我慢慢的来说。
public
class
Contributor
extends
BaseObservable{
private
String
login;
private
int
contributions;
@Bindable
public
String
getLogin(){
return
login;
}
@Bindable
public
int
getContributions(){
return
contributions;
}
public
void
setLogin(String
login){
this.login
= login;
notifyPropertyChanged(BR.login);
}
public
void
setContributions(int
contributions){
this.contributions
= contributions;
notifyPropertyChanged(BR.contributions);
}
@Override
public
String
toString()
{
return
login
+ ",
"
+ contributions;
}
}
我们可以看到,Contributor和MVP相比,继承自了BaseObservable,有基础的同学都知道这是为了当Contributor内部的variable改变的时候ui可以同步的作出响应。
我为什么说Contributor是一个viewmodel呢。大家还记得viewmodel的概念吗?view和viewmodel相互绑定在一起,viewmodel的改变会同步到view层,从而view层作出响应。这不就是Contributor和xml中那些组件元素的关系吗?所以,大家不要被binding类迷惑了,data binding框架中的viewmodel是自己定义的那些看似是model类的东西!比如这里的Contributor!
话说到这里,那binding类又是什么呢?其实具体对应到之前MVVM的那张图就很好理解了,我们想一下,binding类的工作是什么?
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
binding.setContributor(contributor);
首先,binding要通过DataBindingUtil.setContentView()方法将xml,也就是view层设定。
接着,通过setXXX()方法将viewmodel层注入进去。
由于这两个工作,view层(xml的各个组件)和viewmodel层(contributor)绑定在了一起。
好了,大家知道了吗,binding类,其实就是上图中view和viewmodel中间的那根线啊!!
真理在荒谬被证实以前,都只是暗室里的装饰
前面讨论了MVC,MVP和MVVM具体的实现方案,大家肯定都了解了它们三者的关系和使用方式。但是,这里我想说,不要把一个框架看作万能的,其实MVP和MVVM都是有自己的缺陷的!下面我一一来说。
MVP
MVP的问题在于,由于我们使用了接口的方式去连接view层和presenter层,这样就导致了一个问题,如果你有一个逻辑很复杂的页面,你的接口会有很多,十几二十个都不足为奇。想象一个app中有很多个这样复杂的页面,维护接口的成本就会非常的大。
这个问题的解决方案就是你得根据自己的业务逻辑去斟酌着写接口。你可以定义一些基类接口,把一些公共的逻辑,比如网络请求成功失败,toast等等放在里面,之后你再定义新的接口的时候可以继承自那些基类,这样会好不少。
MVVM
MVVM的问题呢,其实和MVC有一点像。data binding框架解决了数据绑定的问题,但是view层还是会过重,大家可以看我上面那个MVVM模式下的activity
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
大家有没有发现,activity在MVVM中应该是view层的,但是里面却和MVC一样写了对model的处理。有人会说你可以把对model的处理放到viewmodel层中,这样不是更符合MVVM的设计理念吗?这样确实可以,但是progressDialog的show和dismiss呢?你怎么在viewmodel层中控制?这是view层的东西啊,而且在xml中也没有,我相信会有解决的方案,但是我们有没有一种更加便捷的方式呢?
接下文
get()和change()这两个方法是我们点击按钮以后执行的,可以看到,里面完完全全没有任何和model层逻辑相关的东西,只是简单的委托给了presenter,那我们再看看presenter层做了什么
public
class
ContributorPresenter
extends
MvpBasePresenter<ContributorView>
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorStart();
}
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
topContributor)
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorComplete(topContributor);
}
}
};
public
void
get(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
public
void
change(){
ContributorView
view
= getView();
if(view
!= null){
view.onChangeContributorName("zjutkz");
}
}
}
其实就是把刚才MVC中activity的那部分和model层相关的逻辑抽取了出来,并且在相应的时机调用ContributorView接口对应的方法,而我们的activity是实现了这个接口的,自然会走到对应的方法中。
好了,我们来捋一捋。
首先,和MVC最大的不同,MVP把activity作为了view层,通过代码也可以看到,整个activity没有任何和model层相关的逻辑代码,取而代之的是把代码放到了presenter层中,presenter获取了model层的数据之后,通过接口的形式将view层需要的数据返回给它就OK了。
这样的好处是什么呢?首先,activity的代码逻辑减少了,其次,view层和model层完全解耦,具体来说,如果你需要测试一个http请求是否顺利,你不需要写一个activity,只需要写一个java类,实现对应的接口,presenter获取了数据自然会调用相应的方法,相应的,你也可以自己在presenter中mock数据,分发给view层,用来测试布局是否正确。
MVVM
首先在看这段内容之前,你需要保证你对data binding框架有基础的了解。不了解的同学可以去看下这篇文章。在接下去让我们开始探索MVVM,MVVM最近在Android上可谓十分之火,最主要的原因就是谷歌推出了data binding这个框架,可以轻松的实现MVVM。但是,我在网上查阅关于Android的data binding资料的时候,发现国内有很多人都误解了,首先,我们从一篇错误的文章开始。当然我在这里引用这篇文章也是对事不对人,如果对文章的作者产生了不好的影响我这里说一声抱歉。
上面那篇文章是一个关于data binding的使用,看起来很美好,但是,其中有一个错误可以说是非常,非常,非常严重的。
它竟然说data binding的viewmodel层是binding类,其实不止是这篇文章,其他有一些开发者写的关于data binding的文章里都犯了一样的错误。大家如果也有这样的概念,请务必纠正过来!!
说完了错误的概念,那data binding中真正的viewmodel是什么呢?我们还是以之前MVC,MVP的那个例子做引导。
首先是view层,这没啥好说的,和MVP一样,只不过多了数据绑定。view层就是xml和activity。
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="contributor"
type="zjutkz.com.mvvm.viewmodel.Contributor"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
android:orientation="vertical"
android:fitsSystemWindows="true">
<Button
android:id="@+id/get"
android:text="get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="get"/>
<Button
android:id="@+id/change"
android:text="change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="change"/>
<TextView
android:id="@+id/top_contributor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="30sp"
android:text="@{contributor.login}"/>
</LinearLayout>
</layout>
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
如果你对data binding框架是有了解的,上面的代码你能轻松的看懂。
那model层又是什么呢?当然就是那些和数据相关的类,GithubApi等等。
重点来了,viewmodel层呢?好吧,viewmodel层就是是Contributor类!大家不要惊讶,我慢慢的来说。
public
class
Contributor
extends
BaseObservable{
private
String
login;
private
int
contributions;
@Bindable
public
String
getLogin(){
return
login;
}
@Bindable
public
int
getContributions(){
return
contributions;
}
public
void
setLogin(String
login){
this.login
= login;
notifyPropertyChanged(BR.login);
}
public
void
setContributions(int
contributions){
this.contributions
= contributions;
notifyPropertyChanged(BR.contributions);
}
@Override
public
String
toString()
{
return
login
+ ",
"
+ contributions;
}
}
我们可以看到,Contributor和MVP相比,继承自了BaseObservable,有基础的同学都知道这是为了当Contributor内部的variable改变的时候ui可以同步的作出响应。
我为什么说Contributor是一个viewmodel呢。大家还记得viewmodel的概念吗?view和viewmodel相互绑定在一起,viewmodel的改变会同步到view层,从而view层作出响应。这不就是Contributor和xml中那些组件元素的关系吗?所以,大家不要被binding类迷惑了,data binding框架中的viewmodel是自己定义的那些看似是model类的东西!比如这里的Contributor!
话说到这里,那binding类又是什么呢?其实具体对应到之前MVVM的那张图就很好理解了,我们想一下,binding类的工作是什么?
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
binding.setContributor(contributor);
首先,binding要通过DataBindingUtil.setContentView()方法将xml,也就是view层设定。
接着,通过setXXX()方法将viewmodel层注入进去。
由于这两个工作,view层(xml的各个组件)和viewmodel层(contributor)绑定在了一起。
好了,大家知道了吗,binding类,其实就是上图中view和viewmodel中间的那根线啊!!
真理在荒谬被证实以前,都只是暗室里的装饰
前面讨论了MVC,MVP和MVVM具体的实现方案,大家肯定都了解了它们三者的关系和使用方式。但是,这里我想说,不要把一个框架看作万能的,其实MVP和MVVM都是有自己的缺陷的!下面我一一来说。
MVP
MVP的问题在于,由于我们使用了接口的方式去连接view层和presenter层,这样就导致了一个问题,如果你有一个逻辑很复杂的页面,你的接口会有很多,十几二十个都不足为奇。想象一个app中有很多个这样复杂的页面,维护接口的成本就会非常的大。
这个问题的解决方案就是你得根据自己的业务逻辑去斟酌着写接口。你可以定义一些基类接口,把一些公共的逻辑,比如网络请求成功失败,toast等等放在里面,之后你再定义新的接口的时候可以继承自那些基类,这样会好不少。
MVVM
MVVM的问题呢,其实和MVC有一点像。data binding框架解决了数据绑定的问题,但是view层还是会过重,大家可以看我上面那个MVVM模式下的activity
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
大家有没有发现,activity在MVVM中应该是view层的,但是里面却和MVC一样写了对model的处理。有人会说你可以把对model的处理放到viewmodel层中,这样不是更符合MVVM的设计理念吗?这样确实可以,但是progressDialog的show和dismiss呢?你怎么在viewmodel层中控制?这是view层的东西啊,而且在xml中也没有,我相信会有解决的方案,但是我们有没有一种更加便捷的方式呢?
接下文
[b]接上文[/b]
get()和change()这两个方法是我们点击按钮以后执行的,可以看到,里面完完全全没有任何和model层逻辑相关的东西,只是简单的委托给了presenter,那我们再看看presenter层做了什么
public
class
ContributorPresenter
extends
MvpBasePresenter<ContributorView>
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorStart();
}
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
topContributor)
{
ContributorView
view
= getView();
if(view
!= null){
view.onLoadContributorComplete(topContributor);
}
}
};
public
void
get(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
public
void
change(){
ContributorView
view
= getView();
if(view
!= null){
view.onChangeContributorName("zjutkz");
}
}
}
其实就是把刚才MVC中activity的那部分和model层相关的逻辑抽取了出来,并且在相应的时机调用ContributorView接口对应的方法,而我们的activity是实现了这个接口的,自然会走到对应的方法中。
好了,我们来捋一捋。
首先,和MVC最大的不同,MVP把activity作为了view层,通过代码也可以看到,整个activity没有任何和model层相关的逻辑代码,取而代之的是把代码放到了presenter层中,presenter获取了model层的数据之后,通过接口的形式将view层需要的数据返回给它就OK了。
这样的好处是什么呢?首先,activity的代码逻辑减少了,其次,view层和model层完全解耦,具体来说,如果你需要测试一个http请求是否顺利,你不需要写一个activity,只需要写一个java类,实现对应的接口,presenter获取了数据自然会调用相应的方法,相应的,你也可以自己在presenter中mock数据,分发给view层,用来测试布局是否正确。
MVVM
首先在看这段内容之前,你需要保证你对data binding框架有基础的了解。不了解的同学可以去看下这篇文章。在接下去让我们开始探索MVVM,MVVM最近在Android上可谓十分之火,最主要的原因就是谷歌推出了data binding这个框架,可以轻松的实现MVVM。但是,我在网上查阅关于Android的data binding资料的时候,发现国内有很多人都误解了,首先,我们从一篇错误的文章开始。当然我在这里引用这篇文章也是对事不对人,如果对文章的作者产生了不好的影响我这里说一声抱歉。
上面那篇文章是一个关于data binding的使用,看起来很美好,但是,其中有一个错误可以说是非常,非常,非常严重的。
它竟然说data binding的viewmodel层是binding类,其实不止是这篇文章,其他有一些开发者写的关于data binding的文章里都犯了一样的错误。大家如果也有这样的概念,请务必纠正过来!!
说完了错误的概念,那data binding中真正的viewmodel是什么呢?我们还是以之前MVC,MVP的那个例子做引导。
首先是view层,这没啥好说的,和MVP一样,只不过多了数据绑定。view层就是xml和activity。
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="contributor"
type="zjutkz.com.mvvm.viewmodel.Contributor"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
android:orientation="vertical"
android:fitsSystemWindows="true">
<Button
android:id="@+id/get"
android:text="get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="get"/>
<Button
android:id="@+id/change"
android:text="change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="change"/>
<TextView
android:id="@+id/top_contributor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="30sp"
android:text="@{contributor.login}"/>
</LinearLayout>
</layout>
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
如果你对data binding框架是有了解的,上面的代码你能轻松的看懂。
那model层又是什么呢?当然就是那些和数据相关的类,GithubApi等等。
重点来了,viewmodel层呢?好吧,viewmodel层就是是Contributor类!大家不要惊讶,我慢慢的来说。
public
class
Contributor
extends
BaseObservable{
private
String
login;
private
int
contributions;
@Bindable
public
String
getLogin(){
return
login;
}
@Bindable
public
int
getContributions(){
return
contributions;
}
public
void
setLogin(String
login){
this.login
= login;
notifyPropertyChanged(BR.login);
}
public
void
setContributions(int
contributions){
this.contributions
= contributions;
notifyPropertyChanged(BR.contributions);
}
@Override
public
String
toString()
{
return
login
+ ",
"
+ contributions;
}
}
我们可以看到,Contributor和MVP相比,继承自了BaseObservable,有基础的同学都知道这是为了当Contributor内部的variable改变的时候ui可以同步的作出响应。
我为什么说Contributor是一个viewmodel呢。大家还记得viewmodel的概念吗?view和viewmodel相互绑定在一起,viewmodel的改变会同步到view层,从而view层作出响应。这不就是Contributor和xml中那些组件元素的关系吗?所以,大家不要被binding类迷惑了,data binding框架中的viewmodel是自己定义的那些看似是model类的东西!比如这里的Contributor!
话说到这里,那binding类又是什么呢?其实具体对应到之前MVVM的那张图就很好理解了,我们想一下,binding类的工作是什么?
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
binding.setContributor(contributor);
首先,binding要通过DataBindingUtil.setContentView()方法将xml,也就是view层设定。
接着,通过setXXX()方法将viewmodel层注入进去。
由于这两个工作,view层(xml的各个组件)和viewmodel层(contributor)绑定在了一起。
好了,大家知道了吗,binding类,其实就是上图中view和viewmodel中间的那根线啊!!
真理在荒谬被证实以前,都只是暗室里的装饰
前面讨论了MVC,MVP和MVVM具体的实现方案,大家肯定都了解了它们三者的关系和使用方式。但是,这里我想说,不要把一个框架看作万能的,其实MVP和MVVM都是有自己的缺陷的!下面我一一来说。
MVP
MVP的问题在于,由于我们使用了接口的方式去连接view层和presenter层,这样就导致了一个问题,如果你有一个逻辑很复杂的页面,你的接口会有很多,十几二十个都不足为奇。想象一个app中有很多个这样复杂的页面,维护接口的成本就会非常的大。
这个问题的解决方案就是你得根据自己的业务逻辑去斟酌着写接口。你可以定义一些基类接口,把一些公共的逻辑,比如网络请求成功失败,toast等等放在里面,之后你再定义新的接口的时候可以继承自那些基类,这样会好不少。
MVVM
MVVM的问题呢,其实和MVC有一点像。data binding框架解决了数据绑定的问题,但是view层还是会过重,大家可以看我上面那个MVVM模式下的activity
public
class
MainActivity
extends
AppCompatActivity
{
private
Subscriber<Contributor>
contributorSub
= new
Subscriber<Contributor>()
{
@Override
public
void
onStart()
{
showProgress();
}
@Override
public
void
onCompleted()
{
}
@Override
public
void
onError(Throwable
e)
{
}
@Override
public
void
onNext(Contributor
contributor)
{
binding.setContributor(contributor);
dismissProgress();
}
};
private
ProcessDialog
dialog;
private
MvvmActivityMainBinding
binding;
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
binding
= DataBindingUtil.setContentView(this,
R.layout.mvvm_activity_main);
}
public
void
get(View
view){
getContributors("square",
"retrofit");
}
public
void
change(View
view){
if(binding.getContributor()
!= null){
binding.getContributor().setLogin("zjutkz");
}
}
public
void
showProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.showMessage("正在加载...");
}
public
void
dismissProgress(){
if(dialog
== null){
dialog
= new
ProcessDialog(this);
}
dialog.dismiss();
}
public
void
getContributors(String
owner,String
repo){
GitHubApi.getContributors(owner,
repo)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.map(new
Func1<List<Contributor>,
Contributor>()
{
@Override
public
Contributor
call(List<Contributor>
contributors)
{
return
contributors.get(0);
}
})
.subscribe(contributorSub);
}
}
大家有没有发现,activity在MVVM中应该是view层的,但是里面却和MVC一样写了对model的处理。有人会说你可以把对model的处理放到viewmodel层中,这样不是更符合MVVM的设计理念吗?这样确实可以,但是progressDialog的show和dismiss呢?你怎么在viewmodel层中控制?这是view层的东西啊,而且在xml中也没有,我相信会有解决的方案,但是我们有没有一种更加便捷的方式呢?
接下文
相关文章推荐
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM(三)
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 选择恐惧症的福音!教你认清MVC,MVP和MVVM
- 教你认清MVC,MVP和MVVM
- 认清Android框架 MVC,MVP和MVVM
- 认清Android框架 MVC,MVP和MVVM
- [我在学Android]认清Android框架 MVC,MVP和MVVM
- 教你认清MVC,MVP和MVVM