Jetpack mvvm 终

  • 前面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之间的交互桥梁
image.png
  • 对比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列子什么的就不会让你一看懵逼二看还是懵逼了


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335