先说说两者的关系,DataBinding是一个实现数据和UI绑定的框架,而MVVM是一种架构模式,实现MVVM模式需要借助DataBinding来完成。

本文将以《Android 谈谈我所理解的MVP》中的例子为基础,使用MVVM模式重新实现一下。

嗯,本篇博客也是有副标题的:Retrofit + RxJava + RxLifecycle + MVVM

本文涉及到的一些基础知识:

《Android DataBinding使用详解(一)》

《Android DataBinding使用详解(二)》

《Android Retrofit + RxJava使用详解》

《Android 使用RxLifecycle解决RxJava内存泄漏》

Demo效果图:

Demo效果图

Demo下载地址

1.MVVM

先来张图感受一下:

MVVM
  • View

    View层只负责UI相关的工作,不进行逻辑处理,并且不需要在Activity/Fragment中做更新UI的操作,更新UI通过Binding实现,在ViewModel中更新数据源即可。如果UI和业务逻辑没有关系,比如点击按钮显示或隐藏控件,是可以在Activity/Fragment中进行UI更新的。

  • ViewModel

    ViewModel层只做和逻辑处理相关的工作,在ViewModel中不会持有View层的引用,双方通过Binding方式通信,只需要在ViewModel层对数据进行操作,View层就会自动更新UI。

  • Model

    Model层的职责和MVC、MVP类似,基本上就是实体类(Bean)和Retrofit的Service。

    在MVVM中Bean是继承BaseObservable的,有些文章里把Bean归到了ViewModel层中,因为Bean中实现的观察者模式才是View和Model沟通的桥梁,嗯,有道理,不过我还是想把它归到Model层,关于这个问题不在过多讨论,仁者见仁,智者见智吧。

2.MVVM实践

首先看下项目结构:

项目结构

做一些准备工作

定义一个请求参数接口:

public interface RetrofitService {    /**     * 获取快递信息     * Rx方式     *     * @param type   快递类型     * @param postid 快递单号     * @return Observable     */    @GET(Constant.UrlOrigin.get_express_info)    Observable getExpressInfoRx(@Query("type") String type, @Query("postid") String postid);}

定义Retrofit帮助类,用于Retrofit与RetrofitService的初始化:

public class RetrofitHelper {    private static RetrofitHelper retrofitHelper;    private RetrofitService retrofitService;    public static RetrofitHelper getInstance() {        return retrofitHelper == null ? retrofitHelper = new RetrofitHelper() : retrofitHelper;    }    private RetrofitHelper() {        // 初始化Retrofit        Retrofit retrofit = new Retrofit.Builder()                .baseUrl(Constant.SERVER_URL)                .addConverterFactory(GsonConverterFactory.create()) // json解析                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 支持RxJava                .build();        retrofitService = retrofit.create(RetrofitService.class);    }    public RetrofitService getRetrofitService() {        return retrofitService;    }}

Model层

Retrofit相关:

public class DataManager {    private static DataManager dataManager;    private RetrofitService retrofitService;    public static DataManager getInstance() {        return dataManager == null ? dataManager = new DataManager() : dataManager;    }    private DataManager() {        retrofitService = RetrofitHelper.getInstance().getRetrofitService();    }    /**     * 获取快递信息     *     * @param type   快递类型     * @param postid 快递单号     * @return Observable     */    public Observable getExpressInfo(String type, String postid) {        return retrofitService.getExpressInfoRx(type, postid);    }}

使用了单例模式,在构造方法中获取RetrofitService实例,定义getExpressInfo方法,返回泛型为ExpressInfo的被观察者对象,稍后在ViewModel中会用到。

实体类:

public class ExpressInfo extends BaseBean {    private String message;    private String nu;    private String ischeck;    private String condition;    private String com;    private String status;    private String state;    private List data;    public void setExpressInfo(ExpressInfo expressInfo) {        setMessage(expressInfo.getMessage());        setNu(expressInfo.getNu());        setIscheck(expressInfo.getIscheck());        setCondition(expressInfo.getCondition());        setCom(expressInfo.getCom());        setStatus(expressInfo.getStatus());        setState(expressInfo.getState());        setData(expressInfo.getData());    }    @Bindable    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;        notifyPropertyChanged(BR.message);    }    @Bindable    public String getNu() {        return nu;    }    public void setNu(String nu) {        this.nu = nu;        notifyPropertyChanged(BR.nu);    }        ...}

BaseBean:

public class BaseBean extends BaseObservable {}

主要看下setExpressInfo方法,setExpressInfo方法是为了在ViewModel层中获取到数据之后,方便更新数据源的,其中的操作也可以放在ViewModel中进行处理。

ViewModel层

首先看下BaseViewModel:

public class BaseViewModel {    private LifecycleProvider provider;    public BaseViewModel(LifecycleProvider provider) {        this.provider = provider;    }    public LifecycleProvider getProvider() {        return provider;    }}

由于使用了RxLifecycle框架来管理RxJava,而RxLifecycle与RxJava的绑定是在ViewModel中进行的,所以就需要在构造ViewModel时传入LifecycleProvider接口的实例。MainActivity最终继承了RxAppCompatActivity,在RxAppCompatActivity内部又实现了LifecycleProvider接口,所以在构造ViewModel时直接传入this就可以了。

public class ExpressViewModel extends BaseViewModel {    public ExpressInfo expressInfo;    private DataManager dataManager;    // 是否显示Loading    public final ObservableBoolean isShowLoading = new ObservableBoolean();    // 错误信息    public final ObservableField errorMessage = new ObservableField<>();    public ExpressViewModel(LifecycleProvider provider, ActivityMainBinding binding) {        super(provider);        expressInfo = new ExpressInfo();        binding.setExpressViewModel(this);        dataManager = DataManager.getInstance();    }    /**     * 获取快递信息     *     * @param type   快递类型     * @param postid 快递单号     */    public void getExpressInfo(String type, String postid) {        isShowLoading.set(true);        dataManager.getExpressInfo(type, postid)                .subscribeOn(Schedulers.io()) // 在子线程中进行Http访问                .observeOn(AndroidSchedulers.mainThread()) // UI线程处理返回接口                .compose(getProvider().bindUntilEvent(ActivityEvent.DESTROY)) // onDestroy取消订阅                .subscribe(new DefaultObserver() {  // 订阅                    @Override                    public void onNext(@NonNull ExpressInfo expressInfo) {                        ExpressViewModel.this.expressInfo.setExpressInfo(expressInfo);                    }                    @Override                    public void onError(@NonNull Throwable e) {                        errorMessage.set(e.getMessage());                        isShowLoading.set(false);                    }                    @Override                    public void onComplete() {                        isShowLoading.set(false);                    }                });    }}

首先看下:

// 是否显示Loadingpublic final ObservableBoolean isShowLoading = new ObservableBoolean();// 错误信息public final ObservableField errorMessage = new ObservableField<>();

由于Dialog和Toast不是定义在布局文件中的,所以不能通过Binding的方式显示Dialog和Toast,而Activity中不能处理逻辑,所以无法在Activity中判断是否显示Dialog和Toast,在ViewModel中又不能显示UI,那该怎么办呢?别着急,可以在ViewModel中定义一个观察者对象isShowLoading,在Activity中对isShowLoading进行监听,true时显示Dialog,false时关闭Dialog就可以了,errorMessage同理。

然后看下构造方法:

public ExpressViewModel(LifecycleProvider provider, ActivityMainBinding binding) {    super(provider);    expressInfo = new ExpressInfo();    binding.setExpressViewModel(this);    dataManager = DataManager.getInstance();}

在构造方法中传入LifecycleProvide和Binding的实例,调用Bindin的setExpressViewModel方法将ViewModel层与View层进行绑定。

接下来定义一个getExpressInfo方法,在其中调用DataManager类的getExpressInfo方法(根据实际需求命名),返回被观察者对象,然后进行订阅,在onNext方法中设置数据,在onError和onComplete方法中设置Dialog和Toast的状态。

View层

布局文件:

<?xml version="1.0" encoding="utf-8"?>                                            

定义变量expressViewModel,会在Binding中生成对应的set方法,就是我们在ViewModel的构造方法中调用的setExpressViewModel方法,然后在TextView中显示快递信息。

Activity:

public class MainActivity extends BaseActivity {    private ProgressDialog progressDialog;    private ExpressViewModel expressViewModel;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);        expressViewModel = new ExpressViewModel(this, binding);        binding.setClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                expressViewModel.getExpressInfo("yuantong", "11111111111");            }        });        // 显示Loading        progressDialog = new ProgressDialog(this);        progressDialog.setMessage("正在获取快递信息...");        expressViewModel.isShowLoading.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {            @Override            public void onPropertyChanged(Observable observable, int i) {                if (expressViewModel.isShowLoading.get()) {                    progressDialog.show();                } else {                    progressDialog.dismiss();                }            }        });        // 显示错误信息        expressViewModel.errorMessage.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {            @Override            public void onPropertyChanged(Observable observable, int i) {                Toast.makeText(MainActivity.this, expressViewModel.errorMessage.get(), Toast.LENGTH_SHORT).show();            }        });    }}

BaseActivity:

public class BaseActivity extends RxAppCompatActivity {}

在Activity中创建ExpressViewModel对象,在点击方法中调用getExpressInfo方法获取快递信息,重点看下下面的方法:

expressViewModel.isShowLoading.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {    @Override    public void onPropertyChanged(Observable observable, int i) {        if (expressViewModel.isShowLoading.get()) {            progressDialog.show();        } else {            progressDialog.dismiss();        }    }});

对isShowLoading添加一个属性改变的监听,如果为true就显示loading框,为false就关闭。

3.写在最后

到这里,MVVM模式就学习完了,DataBinding确实很强大,但有个致命的问题,就是出现问题的时候不好debug,因为UI更新都在布局文件中完成了,而且不论出现什么错误,都会提示找不到Binding文件,没有具体的提示,这个相当头疼。

源码已托管到GitHub上,欢迎Fork,觉得还不错就Start一下吧!

GitHub传送门

欢迎同学们吐槽评论,如果你觉得本篇博客对你有用,那么就留个言或者点下喜欢吧(^-^)

更多相关文章

  1. 【Android】快速切换到主线程更新UI的几种方法
  2. Android(安卓)N Settings 架构剖析
  3. Android(安卓)四大组件流程、Handler、AsyncTask、IntentService
  4. Android(安卓)插件化分析(3)- Activity启动流程
  5. android 手机欢迎界面不显示的问题?
  6. Android理解:IntentService
  7. 使用 Eclipse 调试 android 程序(debug 方法)
  8. 个人android工具包——androidkit更新
  9. Android中AppWidget加载流程(二)

随机推荐

  1. android显示和隐藏键盘
  2. Android application Software Engineer
  3. Understanding the takePicture in Andro
  4. Android 禁止 ListView 上下滑动
  5. Android程序员指南(12)
  6. Android实现打开系统照相机&相册图片展示
  7. Android生成签名文件并用其对apk文件进行
  8. Android实现引导页并滑动跳转到主界面
  9. Android自由选择TextView的文字
  10. eclipse运行android时Console最后提示Don