android mvvm 简书,Android DataBinding & MVVM

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

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

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

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

Demo效果图:

314f203459f2

Demo效果图

1.MVVM

先来张图感受一下:

314f203459f2

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实践

首先看下项目结构:

314f203459f2

项目结构

做一些准备工作

定义一个请求参数接口:

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);

}

});

}

}

首先看下:

// 是否显示Loading

public 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层

布局文件:

name="expressViewModel"

type="com.yl.mvvmdemo.viewmodel.ExpressViewModel" />

name="clickListener"

type="android.view.View.OnClickListener" />

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="10dp"

android:layout_marginRight="10dp"

android:layout_marginTop="10dp"

android:text="@{expressViewModel.expressInfo.data[0].context}" />

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_marginBottom="10dp"

android:layout_marginLeft="10dp"

android:layout_marginRight="10dp"

android:onClick="@{clickListener}"

android:text="获取快递信息" />

定义变量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一下吧!

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值