目录
一、介绍
本节讲的主要针对Android开发人员。对于Android开发者肯定会遇到这样一个问题,就是随着项目复杂度的提升,我们的项目将会变得非常难以维护,为了解决这个问题,我们就需要学习Android中常用的一些框架来达到模块内部的高聚合和模块间的低耦合性,提高项目的可维护性和可扩展性。Android中常用的框架主要有三种:MVC、MVP以及MVVM。学习框架可以帮助我们优化代码,提高我们日常开发的效率。下面就以理论+代码的方式来依次讲解这三种框架,篇幅有点长:
二、无框架示例
本节我们先做一个小需求,先看一下在不使用任何框架的情况下,怎样通过代码来实现需求,然后在后面的章节再通过使用不同框架实现此需求进行对比。
2.1 需求(查询用户信息)
输入用户名,点击查询按钮查询用户信息,如果查询成功,则将查询成功的数据展示在页面上,如果查询失败,则在页面上提示查询失败。
2.2 需求整理
我们先简要整理下需求,这样看起来会更清晰明了
2.3 代码实现
2.3.1 准备工作
1、首先创建NormalActivity与activity_normal.xml布局文件,并做出基本的初始化,页面效果如下:
代码如下:
activity_normal.xml 页面上主要部分有文本输入框,查询按钮和显示结果的文本框。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="MVC、MVP与MVVM"
android:gravity="center"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="18sp"
android:background="@color/title_bag"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="46dp"
android:layout_margin="20dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="用户名:"
android:textSize="15sp"
android:textColor="@color/black"/>
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/tv_name"
android:layout_marginLeft="15dp"/>
</RelativeLayout>
<Button
android:id="@+id/btn_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="查询"
android:background="@color/title_bag"
android:textColor="@color/white"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:textSize="15sp"
android:textColor="@color/black"
android:text="查询结果:"/>
</LinearLayout>
NormalActivity的基本代码如下:
package com.wjy.mdemo.normal;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.wjy.mdemo.R;
/**
* created by WangJinyong
*/
public class NormalActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_name;//输入要查询的用户名
private Button btn_search;//查询按钮
private TextView tv_result;//显示查询结果
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_normal);
initView();
}
//初始化View
private void initView(){
et_name = findViewById(R.id.et_name);
btn_search = findViewById(R.id.btn_search);
btn_search.setOnClickListener(this);
tv_result = findViewById(R.id.tv_result);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_search:
break;
}
}
}
2、我们还需要一些准备工作,先封装一个实体类来封装我们的用户信息,在这里我给他起名叫UserInfo,为了方便演示这里只用了三个最基本的信息,姓名:name、性别:sex、年龄:age。
package com.wjy.mdemo.bean;
/**
* created by WangJinyong
* 封装用户信息实体类
*/
public class UserInfo {
private String name;//用户名
private String sex;//性别
private int age;//年龄
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3、还需要一个接口,来通知用户查询的结果,这里起名叫MCallBack。需要提供两个方法:查询成功的方法:onSuccess(UserInfo userInfo)和查询失败的方法:onFailed()。
package com.wjy.mdemo.callback;
import com.wjy.mdemo.bean.UserInfo;
/**
* created by WangJinyong
* 通知用户查询结果的接口
*/
public interface MCallBack {
void onSuccess(UserInfo userInfo);//查询成功
void onFailed();//查询失败
}
以上准备工作就做完了,下面正式开始做需求部分。
2.3.2 获取用户名
获取的用户名就是我们在输入框输入的内容,我们在NormalActivity里写一个方法获取用户输入的信息getUserInfo()
/**
* 获取用户输入的信息,直接返回输入框的信息就可以了
*/
private String getInputData(){
return et_name.getText().toString();
}
2.3.3 获取数据成功
当获取数据成功的时候,展示获取到的用户信息。
/**
* 获取数据成功
*/
private void showSuccess(UserInfo userInfo){
tv_result.setText("查询结果:\n用户名:"+userInfo.getName()+"\n性别:"+userInfo.getSex()+"\n年龄:"+userInfo.getAge());
}
2.3.4 获取数据失败
当获取数据失败的时候,直接展示获取数据失败。
/**
* 获取数据失败
*/
private void showFailed(){
tv_result.setText("查询结果:\n获取数据失败!");
}
2.3.5 查询用户数据
这个就是获取用户数据的方法,这里需要参数,一个是用户输入的用户名:inputName,另一个就是MCallBack来通知用户查询结果。(注:为了方便,这里就通过模拟的方式来获取用户数据了,实际项目开发中需要通过网络查询或者从数据库中获取数据)
/**
* 模拟 获取用户数据(先不通过网络或者数据库查询来获取数据了)
*/
private void getUserInfo(String inputName, MCallBack mCallBack){
Random random = new Random();
boolean isSuccess = random.nextBoolean();//通过Random随机获取师傅成功
if (isSuccess){
//如果是成功,设置模拟数据
UserInfo userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setSex("男");
userInfo.setAge(25);
mCallBack.onSuccess(userInfo);//调取成功方法,通知用户,并把模拟数据传递过去
}else {
//如果失败 调取失败方法,直接通知用户失败
mCallBack.onFailed();
}
}
2.3.6 业务逻辑
在点击查询按钮查询用户信息
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_search://查询用户信息
getUserInfo(getInputData(), new MCallBack() {
@Override
public void onSuccess(UserInfo userInfo) {
showSuccess(userInfo);//成功
}
@Override
public void onFailed() {
showFailed();//失败
}
});
break;
}
}
以上就实现了我们在前面提出的查询用户信息的需求。在此我们发现了一个问题,就是NormalActivity承担的任务比较多,,比如他既要获取数据,查询数据,又要展示数据(展示成功页面、展示失败页面),还有一部分的逻辑处理(成功的时候展示成功,失败的时候展示失败)。这样写的问题是比较多的,如果我们以后的项目越来越复杂的话,这个NormalActivity的代码会越来越臃肿,非常不好维护,要解决这样的问题,可以使用MVC模式,MVC模式可以很好的对代码进行解耦,将数据的获取和页面的展示分隔开。下一节我们就来看一下MVC模型。
三、MVC模型
3.1 MVC模型简介
MVC的全名的Model View Controller,即模型(model)-视图(view)-控制器(controller)。下面附上一张MVC比较常见的模型图:
Controller:Activity、Fragment
View:layout、View控件
Model:数据处理(网络请求,SQL等)
箭头代表事件传递方向。
1.将数据的获取与界面的展示分离(将查询用户信息从Activity中分离到Model中即可)
2.解决各层之间的通信问题(Activity通知Model获取数据,Model通知Activity更新界面)
3.2 MVC代码实现
1.创建MVCActivity和布局文件activity_mvc.xml,因为布局没有改变直接拷贝上面的布局就可以了,基本控件的初始化也与上面一样,这里就不再写了。
2.根据需求分析,MVCActivity里要实现的获取用户数据、展示成功页面、展示失败页面,上面在NormalActivity里面已经写过了,直接拷贝过来就行。
/**
* 获取用户输入的信息,直接返回输入框的信息就可以了
*/
private String getInputData(){
return et_name.getText().toString();
}
/**
* 获取数据成功
*/
private void showSuccess(UserInfo userInfo){
tv_result.setText("查询结果:\n用户名:"+userInfo.getName()+"\n性别:"+userInfo.getSex()+"\n年龄:"+userInfo.getAge());
}
/**
* 获取数据失败
*/
private void showFailed(){
tv_result.setText("查询结果:\n获取数据失败!");
}
3.还需要新建一个MVCModel类文件,在里面实现查询用户信息功能。(也是直接使用之前写的模拟获取用户数据的方法)
/**
* 模拟 获取用户数据(先不通过网络或者数据库查询来获取数据了)
*/
public void getUserInfo(String inputName, MCallBack mCallBack){
Random random = new Random();
boolean isSuccess = random.nextBoolean();//通过Random随机获取师傅成功
if (isSuccess){
//如果是成功,设置模拟数据
UserInfo userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setSex("男");
userInfo.setAge(25);
mCallBack.onSuccess(userInfo);//调取成功方法,通知用户,并把模拟数据传递过去
}else {
//如果失败 调取失败方法,直接通知用户失败
mCallBack.onFailed();
}
}
到这里就实现了第一步数据的获取与界面的展示分离;接下来就来实现第二步,解决各层之间的通信问题。
package com.wjy.mdemo.mvc;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.wjy.mdemo.R;
import com.wjy.mdemo.bean.UserInfo;
import com.wjy.mdemo.callback.MCallBack;
/**
* created by WangJinyong
*/
public class MVCActivity extends AppCompatActivity implements View.OnClickListener {
……
private MVCModel mvcModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
……
mvcModel = new MVCModel();//对MVCModel初始化
}
//初始化View
private void initView(){
……
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_search://查询用户信息
mvcModel.getUserInfo(getInputData(), new MCallBack() {
@Override
public void onSuccess(UserInfo userInfo) {
showSuccess(userInfo);//成功
}
@Override
public void onFailed() {
showFailed();//失败
}
});
break;
}
}
}
3.3 MVC的优缺点
优点:
一定程度上实现了Model与View的分离,降低了代码耦合性。
缺点:
Controller与View难以完全解耦,并且随着项目复杂度的提升,Controller将越来越臃肿。
四、MVP模型
4.1 MVP模型简介
MVP即Model-View-Presenter模型
MVP与MVC的差别:
1.Model与View不再直接进行通信,而是通过中间层Presenter来实现。
2.Activity的功能被简化,不再充当控制器,主要负责View层面的工作。
1.MVPActivity负责提供View层面的功能(采用实现接口的方式)
2.MVPModel负责提供数据方面的功能
3.Model与View不再直接通信,通过Presenter来实现 ,让Presenter持有View和Model的引用
4.2 MVP代码实现
1.创建MVPActivity和布局文件activity_mvp.xml,因为布局没有改变直接拷贝上面的布局就可以了,基本控件的初始化也与上面一样,这里就不再写了。
2.提供对应的视图功能,需要创建一个接口IMVPView,在接口中声明对应的视图功能:获取用户输入、展示成功界面、展示失败界面。
package com.wjy.mdemo.mvp;
import com.wjy.mdemo.bean.UserInfo;
/**
* created by WangJinyong
* 提供视图功能的接口
*/
public interface IMVPView {
/**
* 获取用户输入的信息
*/
String getInputData();
/**
* 获取数据成功
*/
void showSuccess(UserInfo userInfo);
/**
* 获取数据失败
*/
void showFailed();
}
3.在MVPActivity里实现IMVPView接口里面功能
package com.wjy.mdemo.mvp;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.wjy.mdemo.R;
import com.wjy.mdemo.bean.UserInfo;
/**
* created by WangJinyong
*/
public class MVPActivity extends AppCompatActivity implements View.OnClickListener,IMVPView {
……
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
……
}
//初始化View
private void initView(){
……
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_search://查询用户信息
break;
}
}
@Override
public String getInputData() {
return et_name.getText().toString();
}
@Override
public void showSuccess(UserInfo userInfo) {
tv_result.setText("查询结果:\n用户名:"+userInfo.getName()+"\n性别:"+userInfo.getSex()+"\n年龄:"+userInfo.getAge());
}
@Override
public void showFailed() {
tv_result.setText("查询结果:\n获取数据失败!");
}
}
4.还需要新建一个MVPModel类文件,在里面实现查询用户信息功能。(直接拷贝MVCModel里面的内容就行了)到此数据层面的功能就完成了。
5.还需要一个中间层将他们关联起来。创建一个MVPPresenter(持有View和Model层的引用)
package com.wjy.mdemo.mvp;
import com.wjy.mdemo.bean.UserInfo;
import com.wjy.mdemo.callback.MCallBack;
/**
* created by WangJinyong
* 中间关联层 持有View和Model层的引用
*/
public class MVPPresenter {
private IMVPView imvpView;
private MVPModel mvpModel;
public MVPPresenter(IMVPView imvpView) {
this.imvpView = imvpView;
mvpModel = new MVPModel();
}
//获取数据
public void getData(String inputName){
mvpModel.getUserInfo(inputName, new MCallBack() {
@Override
public void onSuccess(UserInfo userInfo) {
imvpView.showSuccess(userInfo);//成功
}
@Override
public void onFailed() {
imvpView.showFailed();//失败
}
});
}
}
6.点击按钮通过调用MVPPresenter里的查询获取用户信息方法获取用户信息。
public class MVPActivity extends AppCompatActivity implements View.OnClickListener,IMVPView {
……
private MVPPresenter mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
mvpPresenter = new MVPPresenter(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_search://查询用户信息
mvpPresenter.getData(getInputData());
break;
}
}
}
4.3 MVP的优缺点
优点:
解决了MVC中Controller与View的过渡耦合的缺点,职责划分明显,更加易于维护。
缺点:
接口数量多,项目复杂度升高。随着项目复杂度的提升,Presenter层将越来越臃肿。
4.4 MVP使用的建议
1.接口规范化(封装父类接口以减少接口的使用量)
2.使用第三方插件自动生成MVP代码
3.对于一些简单的页面,可以选中不使用框架
4.根据项目复杂程度,部分模块可以选中不使用接口
五、MVVM模型
5.1 MVVM模型简介
在这里我们发现MVVM的模型图和MVP的模型图是非常相似的,唯一的区别就是将Presenter替换成了ViewModel。MVVM和MVP在思想上是非常接近的,但是在代码和逻辑上MVVM会显得更加简洁。
MVVM是Model-View-ViewModel的简写,MVVM在MVP的基础上实现了数据视图的绑定(DadaBinding),当数据变化时,视图会自动更新;反之,当视图发生变化时,数据也会自动更新。相对于MVP减少了接口数量、告别繁琐的findViewById操作。
5.2 DataBinding的基本用法
5.2.1 DataBinding是什么
DataBinding是谷歌官方发布的一个实现数据绑定的框架(实现数据与视图的双向绑定),DataBinding可以帮助我们在Android中更好的实现MVVM模式。
5.2.2 DataBinding使用步骤
1.启用DataBinding
在app的build.gradle的android下添加下面这句,这样项目就支持DataBinding 了。
android {
……
dataBinding {
enabled = true
}
}
2.修改布局文件为DataBinding布局
选中布局文件的最外层,按住Alt+Enter键,选择 Convert to data binding layout。如下图所示:
这样,布局就变成了data binding布局了,我们发现布局的最外层已经变成了layout。
当布局转化成data binding布局后,系统会为我们自动生成一个类Activity**Binding(**为我们创建的类名) 。我这里写的例子的类名为DataBindingActivity,因此自动生成的类名为ActivityDataBindingBinding。
使用DataBinding的话,我们的setContentView也需要做一下改变,需要使用DataBindingUtil.setContentView,这样就可以直接使用databinding直接使用控件,免去了繁琐的findViewById操作。
public class DataBindingActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDataBindingBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding);
binding.tvData.setText("");//直接使用,避免繁琐的findViewById
}
}
3.绑定数据
在布局的data中我们可以声明对象,例如我们用到UserInfo数据,那么就声明变量userInfo,以及类型。
<data>
<variable
name="userInfo"
type="com.wjy.mdemo.bean.UserInfo" />
</data>
下面就可以在布局中直接使用userInfo,比如在文本框中使用,就像下面这样使用:
android:text="@{userInfo.name+'|'+userInfo.age}"
绑定数据
private UserInfo userInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDataBindingBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_data_binding);
userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setAge(25);
binding.setUserInfo(userInfo);
}
这样就能将数据展示在页面上了,这些工作都是DataBinding帮我们做的,因为我们上面在文本框中引用了userInfo的属性。
下面我们再添加一个功能:点击按钮的时候将年龄(age)加1。这样我们需要给按钮添加点击事件。因此我们在data下再引入一个对象
<data>
……
<variable
name="activity"
type="com.wjy.mdemo.databinding.DataBindingActivity" />
</data>
然后为按钮设置点击事件android:onClick="@{activity.onClick}" 同时需要在Activity里提供这个onClick方法,同时需要初始化一下activity
@Override
protected void onCreate(Bundle savedInstanceState) {
……
binding.setActivity(this);
}
public void onClick(View view){}
这里提醒一下:如果在绑定activity的时候找不到.setActivity方法的话,clean一下project就好了,这是由于DataBinding的编译问题。
下面再onClick方法里实现点击年龄加1。
public void onClick(View view){
Toast.makeText(this,"点击了按钮",Toast.LENGTH_SHORT).show();
int age = userInfo.getAge();
userInfo.setAge(age+1);
binding.setUserInfo(userInfo);
}
这时我们会发现,仅仅改变数据,视图也是会更新的,这就是DataBinding帮我们做的。
我们还可以优化一下,因为每次都要调用binding.setUserInfo()方法。优化的话,我们可以这样写,我们用到的UserInfo数据类,让它继承BaseObservable;我们希望年龄在发生变化的时候自动更新视图,就在getAge()方法上面加一个注解@Bindable,同时在setAge方法里更新年龄notifyPropertyChanged(BR.age); 这里的BR是DataBinding生成的,和我们平常使用的R的功能比较相似。这时在点击事件里的binding.setUserInfo绑定就可以去掉了。这样我们就实现了当用户年龄发生变化的时候自动更新视图。
package com.wjy.mdemo.bean;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import com.wjy.mdemo.BR;
/**
* created by WangJinyong
* 封装用户信息实体类
*/
public class UserInfo extends BaseObservable {
……
@Bindable
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age);
}
}
public void onClick(View view){
Toast.makeText(this,"点击了按钮",Toast.LENGTH_SHORT).show();
int age = userInfo.getAge();
userInfo.setAge(age+1);
// binding.setUserInfo(userInfo);
}
这样我们的功能就实现了,当我们改变数据的时候,视图自动更新。需要注意的一点是,到目前为止,我们的数据绑定都是单向绑定的(就是数据改变,视图自动更新)。
DataBinding也支持双向绑定,下面简单说一下双向绑定。就拿刚才的按钮点击事件来说,在@后面加一个等号(=)一般就代表双向绑定。在视图更新的时候也会自动更新数据。双向绑定一般用于输入框。对于DataBinding的使用,功能比较多,这里就不多说了,可以到DataBinding的官方网站学习。
android:onClick="@={activity.onClick}"
5.3 MVVM代码实现
1.提供View、ViewModel以及Model三层
新建三个文件,MVVMActivity(页面布局跟之前的一样,直接拷贝过来就行)、MVVMViewModel(创建一个构造器)、MVVMModel(跟MVCModel一样,直接拷贝过来);
package com.wjy.mdemo.mvvm;
import android.app.Application;
public class MVVMViewModel {
/**
* 一般需要传入Application对象,方便在ViewModel中使用application
* 比如SharedPreferences需要使用
* @param application
*/
public MVVMViewModel(Application application){}
}
2.将布局修改为DataBinding布局
参考上面5.2.2 DataBinding的使用步骤,这里就不再说了。
3.View与ViewModel之间通过DataBinding进行通信
首先在布局文件中声明ViewModel
<data>
<variable
name="viewModel"
type="com.wjy.mdemo.mvvm.MVVMViewModel" />
</data>
然后在Activity中创建MVVMViewModel对象并初始化
MVVMViewModel mvvmViewModel = new MVVMViewModel(getApplication());
binding.setViewModel(mvvmViewModel);
之后给按钮添加点击事件,在点击按钮时候调用ViewModel中的getData方法,android:onClick="@{viewModel.getData}",就是将按钮与ViewModel中的getData方法绑定,因此需要在ViewModel中提供getData方法,这样在点击按钮的时候就会调用ViewModel中的getData方法。
public void getData(View view){ }
将文本框的展示信息与 ViewModel中的result绑定android:text="@{viewModel.result}",因此需要在ViewModel提供result字段。
public class MVVMViewModel extends BaseObservable {
private String result;
……
@Bindable
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
notifyPropertyChanged(BR.result);
}
}
4.获取数据并展示在界面上
点击按钮调取MVVMViewModel里点击事件getData方法,使用binding获取数据。
/**
* created by WangJinyong
* 业务逻辑处理
* 数据更新
*/
public class MVVMViewModel extends BaseObservable {
private ActivityMvvmBinding binding;
private MVVMModel mvvmModel;
……
/**
* getData方法里mvvmModel.getUserInfo需要获取inputName,所以这里再写一个构造方法,把ActivityMvvmBinding传过来,通过binding获取输入框数据
* @param application
* @param binding
*/
public MVVMViewModel(Application application, ActivityMvvmBinding binding){
mvvmModel = new MVVMModel();
this.binding = binding;
}
public void getData(View view){
String inputName = binding.etName.getText().toString();
mvvmModel.getUserInfo(inputName, new MCallBack() {
@Override
public void onSuccess(UserInfo userInfo) {
String info = "查询结果:\n"+userInfo.getName()+"|"+userInfo.getAge();
setResult(info);
}
@Override
public void onFailed() {
setResult("查询结果:\n获取信息失败!");
}
});
}
}
上面是在ViewModel里使用binding获取数据,我们还可以做下改进。就是将输入框与ViewModel中的一个数据绑定(双向绑定)。android:text="@={viewModel.userInput}"。和上面result一样,在ViewModel提供userInput字段。这样当输入框内容发生变化的时候,就会自动更新页面,就不需要binding获取数据了。
/**
* created by WangJinyong
* 业务逻辑处理
* 数据更新
*/
public class MVVMViewModel extends BaseObservable {
private String userInput;
public void getData(View view){
mvvmModel.getUserInfo(userInput, new MCallBack() {
@Override
public void onSuccess(UserInfo userInfo) {
String info = "查询结果:\n"+userInfo.getName()+"|"+userInfo.getAge();
setResult(info);
}
@Override
public void onFailed() {
setResult("查询结果:\n获取信息失败!");
}
});
}
@Bindable
public String getUserInput() {
return userInput;
}
public void setUserInput(String userInput) {
this.userInput = userInput;
notifyPropertyChanged(BR.userInput);
}
}
这样在ViewModel里就用不到binding了,可以把binding去掉,实现更完全的解耦。
最后还有一点需要说明的是,我们的View可以向ViewModel传递信息,,比如当点击的时候可以调用ViewModel的getData方法;ViewModel也可以通过改变数据通知视图自动更新。但是有些功能我们需要放在Activity中去做,这样就会出现问题,比如我们需要在Activity中,当点击按钮的时候,需要在Activity中进行一些申请权限的操作,这个时候ViewModel要如何通知Activity去做这样一件事呢?要实现这个功能可以直接让ViewModel持有Activity的引用,或者是借助第三方库,比如EventBus,这样做的话都不够好。对于MVVM使用LiveData+ViewModel的方式实现比较好。这样做的原因如下:
- LiveData是一个可以被观察的数据持有者,它可以通过添加观察者的方式来让其他组件观察他的变更。
- LiveData遵从应用程序的声明周期(如果LiveData的观察者已经是销毁状态,LiveData就不会通知该观察者。)
5.4 MVVM的优缺点
优点:
实现了数据和视图的双向绑定,极大简化代码。
缺点:
bug难以调试,并且dataBinding目前还存在一些编译问题。
六、总结
MVC:学习简单但是不够彻底
MVP:解耦更加彻底,学习起来也相对比较简单,但是代码相对比较繁琐。
MVVM:代码逻辑非常简洁,但是学习成本较大
至于如何选择框架模型,我们需要根据自己的项目选择最合适自己的框架,如果有些界面或者项目比较简单,我们就可以不使用框架或者使用MVC框架,项目如果比较复杂,需要频繁的维护的话,需要在MVP或者MVVM中进行选择。 这篇文章到此就结束了,有不足之处欢迎大家多多指正,我们共同探讨交流。
下面附上demo地址:
CSDN下载地址:https://download.csdn.net/download/u013184970/11976091
gitee下载地址:https://gitee.com/AlaYu/mvc_mvp_and_mvvm