Jetpack mvvm 终
2020-10-15 本文已影响0人
三月四晴
- 前面3篇讲完了mvvm必要的3个库ViewModel LiveData DataBinding
- 本篇将讲解如果用这3者结合组成mvvm的架构
- 在此之前让我们先了解下mvc mvp 最后再来说下mvvm
MVC
image.png- 其实Android本身就有mvc的例子了
- 比如直接新建一个Activity 在xml布局定义一个按钮实现点击事件 当用户点击按钮的时候我们在按钮的OnClickListener接口回掉中做一个1+1的运算Toast弹出1+1的结果 这一个简单的步骤就包含了mvc的结构了
- M指Model(模型层)在上述的例子指1+1的运算结果
- V指View是视图 用户看到的页面
- C指Controller控制器在上述例子对应点击时间
- 当View视图被用户点击,然后Controller监听到了这个操作进行Mode操作得到2的值反馈给View,然后用户看到了View所弹出Toast消息
- 这个例子很好的对应了上图Model、View、Controller的关系
优点:
1、业务逻辑全部分离到Controller中,模块化程度高。
2、观察者模式可以做到多视图同时更新。
缺点:
1、Model和View之间是直接进行交互,就必然会导致Model和View之间的耦合。
2、所有逻辑都写在Controller层,导致Controller层特别臃肿。 - 优缺点这个百度一大堆我就直接抄了😂
- 适用场景:适用于功能较少、业务逻辑简单、界面不复杂的小型项目。
MVP
- mvp这个呢很多大型的商业app啥的大都用的这个结构,可以说在android算是主流的架构模式
- M 还是那个Model
- V 还是那个视图View
- P 就是Presenter(主持人) 在下面的关系图中P持有M和V做M、V之间的交互桥梁
- 对比mvc可以发现由于Presenter的存在直接处理了V、M直接的耦合,所以比mvc 降低了耦合。
- 具体实现就不扯了网上可以搜罗一大堆
- 下面放个我之前写的Demomvp-retrofit2-rxjava2
优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型。
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部。
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。
缺点:Presenter作为桥梁协调View和Model,就会导致Presenter变得很臃肿,维护比较困难。
- 哈哈哈,这个优缺点我也是抄的
MVVM
-
这才是本文肉戏
image.png -
M不在是前面的Model了,MVVM的Model主要是封装数据存储或操作的一些逻辑(工具类),还会提供一系列的实体类用于UI绑定。
-
V View指做处理界面数据的展示,不做数据处理、业务逻辑等操作。
-
VM ViewModel做数据的获取业务逻辑操作然后通过数据绑定(DatabBinding)和xml进行绑定
优点:
- 低耦合,数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和View层打交道。
- 可重用性,你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
- 可分开独立开发,MVVM的分工是非常明显的,由于View和ViewModel之间是松散耦合的:一个是处理业务和数据、一个是专门的UI处理。所以,完全可以由两个人分工来做,一个做UI(XML和Activity)一个写ViewModel,效率更高。
- 由于各层分工明确,极便于单元测试;
- 相对于MVP而言,MVVM不需要我们手动的处理大量的View和Model相关操作,也非常完美的解耦了View层和ViewModel。
缺点:
-
数据绑定使得 Bug 很难被调试,你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
-
对于过大的项目,数据绑定需要花费更多的内存,而对与过于简单的界面,使用MVVM无异是杀鸡用牛刀。
-
正式开始敲代码
-
网络的部分我用的retrofit2+Rxjava组合,用了wanandroid的公开的接口。
public class MvvMViewmodel extends ViewModel {
public MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
public MutableLiveData<State> state = new MutableLiveData<>();
public LiveData<List<WanAndroidNewsBean.ItemDetailsBean>> listLiveData;
private int page =0;
public List<WanAndroidNewsBean.ItemDetailsBean> list = new ArrayList<>();
public CompositeDisposable dis;
public MvvMViewmodel() {
dis = new CompositeDisposable();
listLiveData = Transformations.switchMap(mutableLiveData, new Function<Integer, LiveData<List<WanAndroidNewsBean.ItemDetailsBean>>>() {
@Override
public LiveData<List<WanAndroidNewsBean.ItemDetailsBean>> apply(Integer input) {
return getData(input);
}
});
}
public void getPageUpData(){
mutableLiveData.setValue(page>0?page-1:page);
}
public void getPageNextData(){
mutableLiveData.setValue(page+1);
}
private LiveData<List<WanAndroidNewsBean.ItemDetailsBean>> getData(int page){
MediatorLiveData<List<WanAndroidNewsBean.ItemDetailsBean>> mediatorLiveData = new MediatorLiveData<>();
HttpData.getRetrofit().create(PathApiService.class).getNes(page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<WanAndroidNewsBean>() {
@Override
public void onSubscribe(Disposable d) {
state.setValue(new State(StateType.LOADING,""));
dis.add(d);
}
@Override
public void onNext(WanAndroidNewsBean wanAndroidNewsBean) {
state.setValue(new State(StateType.SUCCESS,""));
mediatorLiveData.setValue(wanAndroidNewsBean.getData().getDatas());
}
@Override
public void onError(Throwable e) {
state.setValue(new State(StateType.ERROR,e.getMessage()));
}
@Override
public void onComplete() {
Log.e("TAGSS","onComplete");
}
});
return mediatorLiveData;
}
@Override
protected void onCleared() {
super.onCleared();
if(dis!=null){
dis.dispose();
}
}
}
public class MvvmMainActivity extends BaseActivity {
private MvvMViewmodel viewmodel;
private ActivityMvvmMainBinding activityMvvmMainBinding;
private RecycleAdapter recycleAdapter;
private Dialog diglog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void init() {
diglog = MyDiglog.createLoadingDialog(this,"请求中");
activityMvvmMainBinding= getViewDataBinding();
viewmodel = new ViewModelProvider(this).get(MvvMViewmodel.class);
diglog.show();
activityMvvmMainBinding.setLifecycleOwner(this);
viewmodel.getPageUpData();
activityMvvmMainBinding.setViewModel(viewmodel);
activityMvvmMainBinding.recycle.setLayoutManager(new LinearLayoutManager(this));
recycleAdapter = new RecycleAdapter(viewmodel.list);
activityMvvmMainBinding.recycle.setAdapter(recycleAdapter);
viewmodel.listLiveData.observe(this, new Observer<List<WanAndroidNewsBean.ItemDetailsBean>>() {
@Override
public void onChanged(List<WanAndroidNewsBean.ItemDetailsBean> itemDetailsBeans) {
viewmodel.list.clear();
viewmodel.list.addAll(itemDetailsBeans);
recycleAdapter.notifyDataSetChanged();
}
});
viewmodel.state.observe(this, new Observer<State>() {
@Override
public void onChanged(State state) {
diglog.dismiss();
switch (state.getCode()){
case ERROR:
Toast.makeText(MvvmMainActivity.this,state.getMsg(),Toast.LENGTH_LONG).show();
break;
case LOADING:
diglog.show();
break;
}
}
});
}
@Override
public int getLayout() {
return R.layout.activity_mvvm_main;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(diglog!=null){
diglog.dismiss();
}
diglog= null;
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="viewModel"
type="com.sanyue.jetpakcdemonew.mvvm.MvvMViewmodel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".mvvm.MvvmMainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycle"
android:layout_width="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/page_up"
android:layout_height="0dp"/>
<Button
android:id="@+id/page_up"
android:layout_width="wrap_content"
android:text="上一页"
android:onClick="@{()->viewModel.getPageUpData()}"
android:enabled="@{viewModel.mutableLiveData>0?true:false}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/pageIndex"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/pageIndex"
android:layout_width="wrap_content"
android:textColor="@android:color/holo_red_dark"
android:textSize="18sp"
app:layout_constraintLeft_toRightOf="@+id/page_up"
app:layout_constraintRight_toLeftOf="@+id/page_down"
app:layout_constraintTop_toTopOf="@+id/page_down"
app:layout_constraintBottom_toBottomOf="@+id/page_down"
android:text="@{String.valueOf(viewModel.mutableLiveData)}"
android:layout_height="50dp"/>
<Button
android:id="@+id/page_down"
android:layout_width="wrap_content"
android:text="下一页"
android:onClick="@{()->viewModel.getPageNextData()}"
app:layout_constraintLeft_toRightOf="@id/pageIndex"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
效果图
- 从代码上可以把我的MvvMViewmodel类中的变量mutableLiveData
、listLiveData、list看作是Moldel部分,通过ViewModel部分用LiveData跟DataBinding对View部分(activity和xml)进行了绑定,当用户点击下一页就回去执行getPageNextData方法,当listLiveData获取到数据就去更新适配器的数据。 - 大概就这么多了
- 代码都在jetpackDemo欢迎Star
-
可以说的是认真看完这个系列的文章,在自己根据文章的源码解读部分看下源码,相信你在看网上的MVVM列子什么的就不会让你一看懵逼二看还是懵逼了