安卓面经(25/30)MVC、MVP、MVVM全解析

牛客高级系列专栏:

安卓(安卓系统开发也要掌握)

嵌入式

本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对常见安卓高频开发面试题的理解;

网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,承诺提供专栏内容免费技术答疑,直接咨询即可。助您提高安卓面试准备效率,为您面试保驾护航!

正文开始⬇

MVC、MVP、MVVM是安卓开发中使用较多的软件架构设计模式,特别是MVVM更是面试官喜欢提问的模式。面试官可能会问:

  1. 说说你对MVC、MVP、MVVM的理解,他们有什么区别和联系,如何演变的? ⭐⭐⭐⭐⭐
  2. MVVM的优点和缺点 ⭐⭐⭐⭐
  3. 为什么Activity旋转屏幕后ViewModel可以恢复数据 ⭐⭐
  4. ViewModel 的实例缓存到哪儿了 ⭐⭐
  5. 什么时候 ViewModel#onCleared() 会被调用 ⭐⭐

看完以下的解析,一定可以让面试官眼前一亮。

目录

  • 1、MVC
  • 2、MVP
  • 3、MVVM
  • 4、MVVM总结
    • 4.1 MVVM的优点和缺点
    • 4.2 ViewModel 的实例缓存到哪儿了
    • 4.3 为什么Activity旋转屏幕后ViewModel可以恢复数据
    • 4.4 什么时候 ViewModel#onCleared() 会被调用

1、MVC

如果需要设计一套人脸识别系统,用户输入自己姓名后,站在机器前面,机器就可以自动拍照并识别出用户的性别。程序员小帅不考虑软件架构,直接动手撸代码,最终功能虽然实现了,但是各种业务代码都写在同个文件里。过了一段时间,领导让小帅新增识别年龄的功能时,才发现代码非常臃肿,不利于二次开发。看过本文后,小帅重新设计了代码的架构:

  • View:界面相关,将计人脸识别系统UI界面相关代码抽离出来,对应于xml布局文件
  • Controller:控制相关,将和UI界面操作相关以及判断相关的业务逻辑也抽离出来,比如判断用户是否输入姓名了,没有输入则不会开始采集用户照片等,对应于Activity
  • Model:数据相关,具体的性别识别算法运算等较为复杂的运算,本地数据的维护等;

其中View和Model,一个单独负责界面,一个负责处理数据,比较好理解。而Controller就是接收View的指令,做一些逻辑判断或者调用Model做复杂运算后,再把结果反馈给View。通过如此分工,写页面,写业务逻辑,数据运算三者相互分离。

alt

然而,在实际的Android开发中,View层对应xml布局文件,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller,使得Activity变得臃肿。

而且如果不涉及复杂的运算,那么Controller处理完简单的逻辑判断后就直接返回给View。甚至View可以跳过Controller,直接和Model进行交互。这就造成,本来Controller本来就应该依赖View和Model,而现在View也依赖Controller和Model,耦合性还是太高了,于是我们希望View和Model之间要彻底断开联系。

2、MVP

在MVC的基础上,用Presenter代替Controller,此时MVP具体为:

  • View:不仅负责UI界面相关操作,还要负责用户交互,相当于Activity
  • Model:依然主要负责数据处理;
  • Presenter:负责View和Model之间的交互;

首先,明确下理解,在MVC中的View相当于布局文件,而在MVP中View相当于Activity;此时View和Model不再直接交互,而是使用Presenter作为二者交互的桥梁。Presenter里同时持有View和Model的引用,View也有Presenter的引用,但没有Model的引用。

当View层中某个UI界面发起某个数据操作时,会调用Presenter的引用,Presenter再调用Model层的引用请求数据。当Model层的数据处理完后会调用Presenter层的回调通知Presenter层数据加载情况,最后Presenter层再调用View层的接口将加载后的数据展示给用户看。Model层是不会直接和View层产生通讯的。

alt

如果想看看基于MVP写的代码,可以看看这个:MVPdemo

3、MVVM

如果你是做安卓系统开发,那么你可能会接触到MVC或者MVP,而不需要用到MVVM。但如果你是安卓应用开发,那么一定要懂得MVVM。

在MVP中,Presenter更新View也需要很多烦人的代码,有没有什么方法,告诉View去监听某个数据,该数据发生变化时,View自动跟着变化呢?当然是有的,ViewModel随之诞生。ViewModel代替了Presenter,MVP变成了MVVM:Model-View-ViewModel。

  • Model:和MVP中的Model一样,还是用来处理数据的;
  • View:还是用来更新和处理UI界面以及和用户交互,相当于Activity。和MVP里的View的区别在于,View界面的更新从由Presenter驱动,变成了自动监听数据,随着数据变化而自动更新;
  • ViewModel:Model和View的中介,处理逻辑中转任务的媒介。一个View可以绑定多个不同的ViewModel,一个ViewModel也可以被多个View同时绑定。

那么,MVVM是如何做到View的自动更新呢?在此之前,需要掌握几个新概念:

  • LifeCycle:LifeCycle是Google官方提供的管理生命周期事件的方法,可以更加方便地监听声明周期变化事件;
  • LiveData:这是一种用来持有数据的对象,可以设置多个观察者监听LiveData对象,当数据发生改变时,会通知处于active状态(Started和Resume状态)的观察者,并执行设置好的动作。举个例子,我们可以提前设置ActivityA,ActivityB,FragmentA,这三者分别都去监听一个变量名为studentName的LiveDataA对象,也就是在这几个类里,都有下面几行代码。当studentName数值发生改变时,就设置tv_name控件的text改为studentName的新值。那么,当studentName发生改变时,假设ActivityA和FragmentA刚好处于active状态,则ActivityA和FragmentA就会执行tv_name.text = it,自动的修改控件的文本内容。而ActivityB不处于active状态,则不会有任何操作。
myViewModel.studentName.observe(this) {
    tv_name.text = it
    }
  • DataBinding:这是Googel发布的一个用来支持MVVM模式的框架。主要用来降低布局和逻辑的耦合度。以前都是在xml布局文件写好控件后,在Activity通过findViewById()方法绑定控件后,对控件做操作。而随着DataBinding的产生,我们可以直接在xml布局文件里直接将控件和数据绑定在一起,当数据改变时,控件自动更新。

结合LiveData+DataBinding举个例子增进理解,以下例子参考于MVVMDemo ,读者可以下载该开源项目代码实际运行看看:

首先看看布局文件:

<?xml version="1.0" encoding="utf-8"?>
<layout 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"
    >

    <data>
        <!-- 1 -->
        <variable
            name="image"
            type="com.zw.mvvmdemo.bean.ImageUrlBean.UrlBean"/>
    </data>


    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/myImageView"
            <!-- 2 -->
            imageUrl="@{image.BASE_IMAGE_ADDRESS_URL + image.url}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="match_parent"
            android:layout_height="200dp" />
    </android.support.constraint.ConstraintLayout>

</layout>

首先看[注释1],可以创建<data>变量:

<variable
    name="image"
    type="com.zw.mvvmdemo.bean.ImageUrlBean.UrlBean"/>

在variable标签中:

  • name:需要绑定数据的名称,名称可自定义;
  • type:需要绑定数据的类型;

所以,我们创建了一个名为“image”的<data>变量,并在[注释2]中使用,赋值给了另一个叫imageUrl的属性。imageUrl属性是我们自定义的一个属性,是通过DataBinding的注解BindingAdapter来声明的。

public class GetBingImageAdapter {

    @BindingAdapter("imageUrl")
    public static void setImage(ImageView iv, String url) { //3
        Glide.with(iv)
                .load(url)
                .into(iv);
    }
}

所以当[注释1]名为“image”的变量发生改变时,[注释2]名为“myImageView”的控件中的imageUrl属性就会随着改变,并调用[注释3]的回调函数setImage()方法,最终执行Glide这个第三方图片加载框架来刷新图片。

那么就剩下最后一个问题,[注释1]名为“image”的变量什么时候会发生改变呢?我们可以在Activity中监听从网络获取到的图片的url地址,当url地址改变时,修改“image”变量的值:

 mViewModel.getImageUrl().observe(this, new Observer<UrlData<ImageUrlBean.UrlBean>>() {
            @Override
            public void onChanged(@Nullable UrlData<ImageUrlBean.UrlBean> data) {
                mDialog.dismiss();
                if(data.getErrorMsg() != null) {
                    Toast.makeText(BingImageActivity.this, data.getErrorMsg(),
                            Toast.LENGTH_LONG).show();
                }
               
                binding.setImage(data.getData()); //4
            }
        });

其中getImageUrl()返回的变量的定义是:

    private MutableLiveData<UrlData<ImageUrlBean.UrlBean>> mData;

没错,就是一个LiveData对象。最后通过[注释4]来修改“image”变量的值。有一个知识点要注意,如果“image”的名字改为“myImageValue”,则[注释4]的函数名应该同步改为setMyImageValue()。

具体的项目源码有兴趣的同学自行下载研究:MVVMDemo 。讲到这里,大家应该就明白所谓的View能自动更新,其实是因为我们提前设置好了监听,也设置好了监听的对象数值改变后,View需要完成什么动作。

4、MVVM总结

4.1 MVVM的优点和缺点

总的来说,MVVM还是一个非常优秀的模式,主要体现在:

  • 数据和布局双向绑定,我们只需要关心数据数值的改变,而不再需要操心findViewById和setText()等繁复的代码的工作;
  • 一个View可以绑定多个ViewModel,一个ViewModel可以供多个View绑定,提高复用性和灵活性;
  • 自带生命周期管理,不用担心因为生命周期而出现bug;

当然,MVVM也有缺点:

  • View和ViewModel独立,如果出现bug定位问题较难;
  • Binding类的生成可能需要多次Rebuild的操作,若Binding类过多,则编译工作会耗时很大;
  • 需要提前设置好数据监听,如果数值多,那就要设置很多监听;
  • 如果ViewModel层数据未改变,但却强制要View层改变UI控件时,就比较麻烦;

4.2 ViewModel 的实例缓存到哪儿了

如果我们修改了系统语言再回到App,或者旋转了屏幕,都会发现APP中当前Activity重新创建了,但是ViewModel里面的LiveDa

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Android高频面试题全解析 文章被收录于专栏

#提供免费售后答疑!!花一杯奶茶的钱获得安卓面试答疑服务,稳赚不赔# Android发展已经很多年,安卓资料网上千千万,本专栏免费提供专栏内容技术答疑!!私聊当天必回。在阅读过程或者其他安卓学习过程有疑问,都非常欢迎私聊交流。

全部评论
一直没搞清MVC这几个,正好学习学习
点赞 回复 分享
发布于 2023-03-05 12:54 江西
谢谢大佬分享
点赞 回复 分享
发布于 2023-03-05 13:06 湖南

相关推荐

大一刚进校时,我对AI的理解还停留在科幻电影里会说话的机器人。查找资料还需要百度浏览器相关的工具,获得的信息也是十分有限和冗余,没想到近两年这个庞然大物早已渗透进现实。部分高性能的模型甚至能够取代初级程序员完成代码的编写和开发,完成建模和取代部分人工操作。 &nbsp;后来开始接触编程课,可以实现提取关键词完成自动回复的聊天机器人。但真正让我焦虑的是秋招季,往年招20人的银行柜员岗今年缩招到5个,HR直言:&amp;quot;现在ATM机都能办80%的现金业务&amp;quot;。 &nbsp;Ai不仅仅作用于基础服务业还有部分文员工作,把文案丢进AI,半小时就生成30版海报初稿,这在过去需要整个团队加班三天。但也是在这里,我发现真正值钱的是能把AI方案翻译给甲方的能力,是懂得在500个方案里挑出最能打动人的那个眼光。 &nbsp;如今大四求职,我的简历里藏着小心思:用ChatGPT优化过的自我评价,用Notion&nbsp;AI整理的项目经历。面试时我带着自己训练的智能面经库,把500条面经喂给AI做针对性训练。双非学历的短板还在,但当我展示用AI分析出的行业趋势报告时,面试官会更愿意停留&nbsp;AI正在重塑所有行业的起跑线。合理运用ai&nbsp;带来了打破阶级的可能性,或许我们双非生反而更早体会到了:与其担心被取代,不如成为最早驯服AI的那批人。毕竟在新技术浪潮里,985的游泳姿势未必比我们野路子的狗刨更管用。#聊聊我眼中的AI#
点赞 评论 收藏
分享
评论
2
19
分享

创作者周榜

更多
牛客网
牛客企业服务