Android DataBinding & MVVM
来源:互联网 发布:淘宝卖家手写卡片内容 编辑:程序博客网 时间:2024/05/21 02:50
先说说两者的关系,DataBinding是一个实现数据和UI绑定的框架,而MVVM是一种架构模式,实现MVVM模式需要借助DataBinding来完成。
本文将以《Android 谈谈我所理解的MVP》中的例子为基础,使用MVVM模式重新实现一下。
嗯,本篇博客也是有副标题的:Retrofit + RxJava + RxLifecycle + MVVM
本文涉及到的一些基础知识:
《Android DataBinding使用详解(一)》
《Android DataBinding使用详解(二)》
《Android Retrofit + RxJava使用详解》
《Android 使用RxLifecycle解决RxJava内存泄漏》
Demo效果图:
Demo下载地址
1.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<ExpressInfo> */ @GET(Constant.UrlOrigin.get_express_info) Observable<ExpressInfo> 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<ExpressInfo> */ public Observable<ExpressInfo> 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<DataBean> 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<ActivityEvent> provider; public BaseViewModel(LifecycleProvider<ActivityEvent> provider) { this.provider = provider; } public LifecycleProvider<ActivityEvent> 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<String> errorMessage = new ObservableField<>(); public ExpressViewModel(LifecycleProvider<ActivityEvent> 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().<ExpressInfo>bindUntilEvent(ActivityEvent.DESTROY)) // onDestroy取消订阅 .subscribe(new DefaultObserver<ExpressInfo>() { // 订阅 @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<String> 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<ActivityEvent> 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"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="expressViewModel" type="com.yl.mvvmdemo.viewmodel.ExpressViewModel" /> <variable name="clickListener" type="android.view.View.OnClickListener" /> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView 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}" /> <Button 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="获取快递信息" /> </RelativeLayout></layout>
定义变量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传送门
欢迎同学们吐槽评论,如果你觉得本篇博客对你有用,那么就留个言或者顶一下吧(^-^)
- android MVVM DataBinding
- Android DataBinding && MVVM [U04]
- Android DataBinding & MVVM
- Android MVVM框架 DataBinding
- Android DataBinding(MVVM设计模式)
- MVVM + dataBinding
- Android DataBinding库(MVVM设计模式)
- 【Android】DataBinding库(MVVM设计模式)
- Android MVVM 之DataBinding,BindingAdapter及component
- Android DataBinding库(MVVM设计模式)
- Android开发模式MVVM之DataBinding
- Android DataBinding库(MVVM设计模式)
- Android MVVM+DataBinding结合Dagger2进行开发
- Android MVVM 初探之 DataBinding 显示数据
- Android MVVM 初探之 DataBinding 双向绑定
- Android DataBinding库(MVVM设计模式)
- Android MVVM之Databinding(二) 使用篇
- android MVVM DataBinding 数据绑定框架 注意问题
- leetcode--Binary Tree Upside Down
- ubuntu 下为php安转memcache和redis服务
- bzoj3437小P的牧场(斜率优化)
- 机器学习实战——K-近邻算法【1:从文本中解析数据并可视化】
- R语言svm预测股票走势
- Android DataBinding & MVVM
- [SMOJ1865]矩形相交面积
- java运用poi导入excel并存入数据库(内含自己用的选择路径方法)
- 《OpenGL v3.3》——(1)搭建glfw3与glew环境,并创建第一个窗口
- centos7下xgboost,python安装
- 第二章 第五节 :唯一约束UNIQUE KEY
- ios 跳系统设置页面
- HDU 1166 敌兵布阵
- Java的三大特性之多态__渣渣的Java学习之路