MVVM初体验

WHAT?

mvvm is ?

  • 先来张图吧!

图片来自csdn某个老哥时间久远找不到作者了如果侵权了可以私信我我删掉自己画一哈(懒死我了)

mvvm
  • 什么是mvvm?
    很简单,Model(数据)View(视图)ViewModel(数据视图管理器)
  • 能不能具体点呢?
    Model:bean(实体类)、Data Source(Http请求相关、数据库相关)
    View:xml、View、Activity、Fragment 等UI相关
    ViewModel:先简单理解为管理器

mvc mvp ?

  • mvc
  • mvc
  • 该MVC框架虽然也是把代码逻辑和UI层分离,但是View层能做的事情还是很少的,很多对于页面的呈现还是交由C实现,这样会导致项目中C的代码臃肿,如果项目小,代码臃肿点还是能接受的,但是随着项目的不断迭代,代码量的增加。
  • M-Model : 业务逻辑和实体模型(biz/bean)
    V-View : 布局文件(XML)
    C-Controller : 控制器(Activity)
  • mvp
  • mvp
  • 该MVP框架相对于MVC框架做了较大的改变,将Activity当做View使用,代替MVC框架中的C的是P,对比MVC和MVP的模型图可以发现变化最大的是View层和Model层不在直接通信,所有交互的工作都交由Presenter层来解决。既然两者都通过Presenter来通信,那么不难理解MVP框架给予接口设计的理念就是为了复用和可扩展性。

WHY?

优点

  • 数据驱动
    在MVVM中,以前开发模式中必须先处理业务数据,然后根据的数据变化,去获取UI的引用然后更新UI,通过也是通过UI来获取用户输入,而在MVVM中,数据和业务逻辑处于一个独立的ViewModel中,ViewModel只要关注数据和业务逻辑,不需要和UI或者控件打交道。由数据自动去驱动UI去自动更新UI,UI的改变又同时自动反馈到数据,数据成为主导因素,这样使得在业务逻辑处理只要关心数据,方便而且简单很多。
  • 低耦合度
    MVVM模式中,数据是独立于UI的,ViewModel只负责处理和提供数据,UI想怎么处理数据都由UI自己决定,ViewModel 不涉及任何和UI相关的事也不持有UI控件的引用,即使控件改变(TextView换成EditText)ViewModel 几乎不需要更改任何代码,专注自己的数据处理就可以了,如果是MVP遇到UI更改,就可能需要改变获取UI的方式,改变更新UI的接口,改变从UI上获取输入的代码,可能还需要更改访问UI对象的属性代码等等。
  • 更新 UI
    在MVVM中,我们可以在工作线程中直接修改ViewModel的数据(只要数据是线程安全的),剩下的数据绑定框架帮你搞定,很多事情都不需要你去关心。
  • 团队协作
    MVVM的分工是非常明显的,由于View和ViewModel之间是松散耦合的。一个是处理业务和数据,一个是专门的UI处理。完全有两个人分工来做,一个做UI(xml 和 Activity)一个写ViewModel,效率更高。
  • 可复用性
    一个ViewModel复用到多个View中,同样的一份数据,用不同的UI去做展示,对于版本迭代频繁的UI改动,只要更换View层就行,对于如果想在UI上的做AbTest 更是方便的多。
  • 单元测试
    ViewModel里面是数据和业务逻辑,View中关注的是UI,这样的做测试是很方便的,完全没有彼此的依赖,不管是UI的单元测试还是业务逻辑的单元测试,都是低耦合的。

缺点

  • 代码量增多
  • 代码变复杂,交接难度增加
  • 巨型ViewModel的出现

HOW?

使用Android Architecture Components(aac)框架

  • 什么是aac框架
  • Architecture Components是在2017年GoogleI/O大会上,Google官方推出的一个构建Android应用架构的库。它可以帮你避免在Android应用开发中常见的一些问题,比如:内存泄露,管理组件生命周期等等。
  • ViewModel 负责调用 Model(可以称之为数据源),拿到结果后,更新自身。而 View 与 ViewModel 双向绑定(后面会讲怎么实现绑定的),所以 View 就会自动更新。这就是 MVVM 大致的思想。
  • aac
    • Lifecycle
      生命周期管理,把原先Android生命周期的中的代码抽取出来,如将原先需要在onStart()等生命周期中执行的代码分离到Activity或者Fragment之外。
    • LiveData
      一个数据持有类,持有数据并且这个数据可以被观察被监听,和其他Observer不同的是,它是和Lifecycle是绑定的,在生命周期内使用有效,减少内存泄露和引用问题。
    • ViewModel
      用于实现架构中的ViewModel,同时是与Lifecycle绑定的,使用者无需担心生命周期。可以在多个Fragment之间共享数据,比如旋转屏幕后Activity会重新create,这时候使用ViewModel还是之前的数据,不需要再次请求网络数据。
    • Room
      谷歌推出的一个Sqlite ORM库,不过使用起来还不错,使用注解,极大简化数据库的操作,有点类似Retrofit的风格。
  • 为什么使用aac框架
    • 应用开发者所面对的问题
      移动设备资源有限,任何时候系统都有可能杀死应用释放内存,这样的话相应的(activity/fragment/service等等)也会被销毁,他们的生命周期不再受开发者控制,而是受到系统控制,所以说尽量不要再应用程序组件中存储任何应用数据和状态,并且各个组件尽量独立不要依赖。
    • 常见的构建原则
      一个常见的错误是在 Activity 和 Fragment 中编写所有的代码。任何和UI或者操作系统交互无关的代码都尽量不要出现在这些类中,尽量保持这些类的精简会在生命周期由系统造成变化时避免很多麻烦,aac框架就是为了这些问题而生。
      通过model驱动ui即通过数据驱动ui(model最好是持久化的model)这样做在系统销毁应用释放资源后我们可以不用在费心的考虑数据丢失的问题,而且在网络不好的情况下由于持久化model的存在应用也可以继续运行。
  • 如何使用aac框架构建mvvm架构的Android程序

一: 初步配置环境

首先需要引入 Lifecycles, LiveData and ViewModel(该库存在于google的maven仓库中需要在项目gradle中引入google的maven)然后再在module模块下引入相应的库。需要注意的是lifecycle包内会依赖support包也许会和部分项目造成冲突,注意项目内需要使用什么版本。

allprojects {
    repositories {
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

需要注意的是目前低版本的support-fragment and appcompat-v7(version<26.0.0)必须使activity和fragment继承LifecycleActivity和LifecycleFragment,在高版本中FragmentActivity 和 AppCompatActivity 都实现了 LifecycleOwner接口不必再继承

    //aac
    compile "android.arch.lifecycle:runtime:$rootProject.lifecycleRuntime"
    compile("android.arch.lifecycle:extensions:${rootProject.lifecycle}")

            {
                exclude group: 'com.android.support'
            }

    annotationProcessor "android.arch.lifecycle:compiler:$rootProject.lifecycle"

二:java代码配置部分

model:普通的pojo类即可

public class User  {
    private int id;
    private String name;
// ...getter setter and toString...
}

view:普通的xml文件即可(此处暂时没有结合databinding)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:id="@+id/ll_root"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_id"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_name"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

viewmodel:viewmodel就很类似mvc模式中的controller和mvp种的presenter由于我们集成了aac框架所以直接写一个类集成viewmodel就行,它可以帮我们自动管理ui的生命周期

public class UserViewModel extends ViewModel {}

先写一个空方法,具体内部实现后面说。

livedata:轻松实现数据和view(ui)绑定的好工具,livedata可以自动响应生命周期,数据在改变的时候可以通知观察者更新ui,就是因为这点可以轻松实现model和view的绑定
具体用法:

首先创建可变的livedata即mutablelivedata

private MutableLiveData<String> mCurrentName;

public MutableLiveData<String> getCurrentName() {
    if (mCurrentName == null) {
        mCurrentName = new MutableLiveData<String>();
    }
    return mCurrentName;
}

其次在activity/fragment中观察livedata。也就是当livedata数据发生变化时,自动回调方法来更新 ui。

Observer<String> nameObserver = new Observer<String>() {
    @Override
    public void onChanged(@Nullable final String newName) {
        mNameTextView.setText(newName);
    }
};
getCurrentName().observe(this, nameObserver);

最后更新数据

mButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "licz";
        getCurrentName().setValue(anotherName);
    }
});

livedata+viewmodel:为了显示id和name我们需要一个livedata<user>,我们在viewmodel中暴露一些set/get方法来改变livedata<user>内的数据

public class UserViewModel extends ViewModel {
    private MutableLiveData<User> user;

    public LiveData<User> getUser() {
        if (user == null)
            user = new MutableLiveData<>();
        return user;
    }

    public void setUsername(String username) {
        user.setValue(new User(1, username));
    }
}

view+viewmodel:livedata它可以方便的被观察。viewmodel,可以自动响应生命周期。这两个东西的结合可以帮我们方便的实现数据绑定。下面我们来说说viewmodel怎么和view绑定,然后自动响应activity/fragment的生命周期

private UserViewModel userViewModel;
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initView();
        initData();

    }

    private void initData() {
        userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
        userViewModel.getUser().observe(this, new Observer<User>() {
            @Override
            public void onChanged(@Nullable User user) {
                updateView(user);
            }
        });

        userViewModel.setUsername("licz");
    }

    private void updateView(User user) {
        tvId.setText(user.getId() + "");
        tvName.setText(user.getName());
    }

通常来说,一个view只和一个viewmodel 绑定。所以我们需要一个activity级别的单例viewmodel,我们就是通过ViewModelProviders.of(this).get(UserViewModel.class) 来简单的实现。然后通过userViewModel取得LiveData并添加监听(绑定操作)。
三:aac框架实现总结
这样的一个框架的存在帮助我们解决了很多痛点问题,比如说以后就不需要为同一份数据n处都修改view的问题头疼了,再比如数据请求可以更为纯粹不需要考虑ui是怎么渲染和实现的。再做完数据请求或者完成数据更新以后,livedata会自动通知ui根据数据处理界面。
在aac框架的基础上结合databinding后我们可以完全不需要自己手动处理ui,而viewmodel和model通过livedata作为桥梁相连接,view和viewmodel双向绑定,由于viewmodel自动响应生命周期我们也可以很轻松处理view生命周期相关的ui需求。
最后aac框架不是实现mvvm的必要途径只是他可以为我们解决很多实现mvvm过程中的痛点

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

推荐阅读更多精彩内容