面试问到烂的MVC、MVP以及MVVM

浩浩 等级 461 1 1

MVP

什么是MVP?

在了解MVP之前可以先观察MVC的架构模式。

面试问到烂的MVC、MVP以及MVVM

MVC中三个组成部分:1. View,即视图中的各个控件;2. Controller,即Activity、Fragment;3. Model,即数据源。

但是日常开发中能够发现,对View层的控制也是在Activity中,这时引入Model层数据源的获取再与Controller层发生交互时,不难发现MVC三层互相都存在持有关系,也就产生了严重的耦合。

面试问到烂的MVC、MVP以及MVVM

图片

而MVP的架构实现就是将控制层下移,View层充当Activity、Fragment的存在,Model层保持原样作为数据源的获取层存在,而View和Model层的通信通过中间人Presenter来完成数据的传递,通过这样的方式达到了解耦的目的。

通信的方式就是互相持有,但中间人对于View层的持有使用弱引用的方式实现,以保证View的及时释放。

内存泄漏

解耦的思想在上面已经有所表述,但解耦的背后还有一个我们非常关注的点 --内存泄漏。这个小模块可以分为两个问题进行阐述:1. 什么是内存泄漏?;2. 使用MVP框架能不能帮我们解决内存泄漏的问题?

什么是内存泄漏?

想来这也是老生常谈的问题了,简单了说原本该释放的东西最后竟然没有释放掉,而引起问题可能是一个变量、一个任务等等。

使用MVP框架能不能帮我们解决内存泄漏的问题?

其实这个问题我们应该这样去进行发问MVP框架能不能帮我们解决View层内存泄漏的问题?如果使用标题的问题,其实这算是一个错误的命题,

那是否能够解决这样的问题呢?可以通过一个非常简单的方法直接进行验证。下面是一段代码示例,一个简单的异步线程延迟任务。

new Thread(new Runnable() {
    @Override
    public void run() {
        SystemClock.sleep(200000);
    }
}).start(); 

通过Android Studio集成的Profiler能力,在运行期间就可以直接分析内存信息。

面试问到烂的MVC、MVP以及MVVM

图片

面试问到烂的MVC、MVP以及MVVM

图片

其中有一个打了红框的按钮,点击后可以打印出一段时间的内存分配情况。 你同样可以直接点击对某个时间点进行分析。

这是有两个信息我们去进行关注,View层、Prensenter层、Model层所占用的内存大小。

  1. View层

面试问到烂的MVC、MVP以及MVVM

图片

  1. Presenter层和Model层

面试问到烂的MVC、MVP以及MVVM

图片

上述的Presenter层中已经开启了异步线程,能够明显发现View层所占用的内存明显大于Presenter层和Model层。而如果这个时候使得让我去选择内存泄漏的类,最后的选择肯定是倾向是少的一方,而MVP给我们带来了选择的空间,这也是MVP架构下为我们带来的一大好处。

这里也得出了一个标题的结论,MVP架构能够缓解内存泄漏问题,但不能解决它。

手撸一个MVP架构

在MVP架构中,我们会存在两种代码风格,M层或P层做复杂的逻辑处理,选择其中一层做复杂的逻辑处理,在这里我更喜欢这些事情由M层负责完成。

M-V-P

首先是Presenter层,这一层作为中间人,和Model层以及View层同时存在通信,也就需要对两者同时进行持有,另外为了能够在View层销毁不用时,Presenter层能够不发生内存泄漏问题,对于View层引用方法采用的是弱引用的方式书写。

abstract class BaseMvpPresenter<V: IMvpView, M: IMvpModel> : IMvpPresenter {
    private var vWeakReference: WeakReference<V>? = null
    protected val model: M by lazy { createModel() } 
    override fun bindView(mvpView: IMvpView) {
        vWeakReference = WeakReference(mvpView as V)
    }
    override fun unBindView() {
        if (vWeakReference != null) {
            vWeakReference?.clear()
            vWeakReference = null
        }
    }
    fun getView(): V? = vWeakReference?.get()
    abstract fun createModel(): M
} 

接下来是Model层,这一层是数据源的存在,而数据源的获取方法都是用户自定义,这里Model层只需要在持有Presenter层的前提下做能力预留即可。

abstract class BaseMvpModel<P: IMvpPresenter> (val p: P): IMvpModel 

最后是View层,更具体一点就是Activity、Fragment这些类,一个同样避不开的话题就是持有,View层同样需要先对Presenter层进行持有,也就有了如下的初版代码。

abstract class BaseMvpActivity<P : BaseMvpPresenter<*, *>> : AppCompatActivity(), IMvpView {
    protected var p: P? = null

    abstract fun getPresenter(): P
} 

但是需要思考的一个问题Activity、Fragment什么时候应该和Presenter层发生通信呢?为了能够适应全生命周期的变化,自然最后的考虑就是Activity的onCreate()和onDestroy(),Fragment的onAttach()和onDetach()方法中了。

abstract class BaseMvpActivity<P : BaseMvpPresenter<*, *>> : AppCompatActivity(), IMvpView {
    protected var p: P? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        p = getPresenter()
        p?.bindView(this)
    }
    override fun onDestroy() {
        super.onDestroy()
        p?.unBindView()
    }
    abstract fun getPresenter(): P
} 

abstract class BaseMvpFragment<P : BaseMvpPresenter<*, *>> : Fragment(), IMvpView {
    protected var p: P? = null
    override fun onAttach(context: Context) {
        super.onAttach(context)
        p = getPresenter()
        p?.bindView(this)
    }
    override fun onDetach() {
        super.onDetach()
        p?.unBindView()
    }
    abstract fun getPresenter(): P
} 

完成以上的一系列步骤,其实已经完成了整个架构的构建,但是如果直接继承去玩这套框架的时候是不是感觉还欠缺了什么东西?通信

建立通信

在刚刚全部代码基础上,不论Model层、Presenter层还是View层都已经做好了最基础的事情,就是持有。但是通信一定需要持有,持有不一定能够通信。显然在现有的代码基础上,通信设施是当务之急。

这也就引出了新的通信层Contract,当然它的本名应该说是协议层,就像TCP / UDP啥的,在不同的层次之间引出了这样一个接口类,他负责的事情就是MVP三层的通信是什么样的。以下便是一段协议层的示例:

interface MainContract {
    interface Model {
        fun execute()
    }
    interface View<T: IMvpModel> {
        fun handleResponse(data: T)
    }
    interface Presenter<T: IMvpModel> {
        fun request()
        fun response(data: T)
    }
} 

通过在不同的层次引入这些接口,并完成其具体实现,最后就实现了一套完整的MVP架构。

MVVM

面试问到烂的MVC、MVP以及MVVM

MVVM架构其实和MVP架构整体上相似,但是ViewModel层和View层属于双向通信,使用了DataBinding的能力,使得ViewModel的生成、与View层的绑定完全由系统直接完成简化了开发的流程。但是从设计上出发的时候,MVP更有利于我们对于整体架构的理解。

入门MVVM

  1. 能力引入

android {
  dataBinding {
        enabled true
    }
} 

使用Kotlin编程的开发者需要引入kotlin-kapt

  1. 使用

使用方面可以分为两个小部分:布局使用、绑定使用

  • 布局使用

在布局使用中和平常的XML编写会有一定的出入,需要使用进行第一层的包裹,而其中的代码编写又可以分为数据区布局区两个部分。布局区和平常的书写方式保持一致,重点关注数据区,他需要以做第一层包裹,用于标示数据区,是对变量的定义,其中标签是定义该变量变量名,标签是定义该变量的类型。

<?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">
    <data>
        <variable
            name="user"
            type="com.clericyi.android.helper.LoginModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.responseCode}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout> 
  • 绑定使用

完成布局绑定以后需要Sync,这一套能力实现和ButterKnife一样都会产生一个新的Binding文件,但是这个Binding拥有更为强大的数据绑定能力。另外这个Binding文件的命名是和XML文件保持一致的,比如activity_main.xml => ActivityMainBinding,activity_main_1.xml => ActivityMain1Binding。

val ac = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
ac.user = p?.let { LoginModel(it, "200") } 

以上一套非常简单的代码过后就已经完成了这套生态下的大部分事项,从开发成本上来讲是优于MVP架构的。

学习最忌盲目,无计划,零碎的知识点无法串成系统。学到哪,忘到哪,面试想不起来。这里我整理了Android面试中最常问的核心知识点以及Android架构、Flutter、KT、framework中最核心的几块知识,分享在我的【Github】,如有面试需要的朋友欢迎前往阅读;

收藏
评论区

相关推荐

快速入门 WePY 小程序
一、WePY介绍 WePY 是 _腾讯_ 参考了Vue 等框架对原生小程序进行再次封装的框架,更贴近于 MVVM 架构模式, 并支持ES6/7的一些新特性。 二、WePY 使用 1、WePY的安装或更新都通过npm进行: npm install g wepyc
基于jsoneditor二次封装一个可实时预览的json编辑器组件(react版)
前言 做为一名前端开发人员,掌握vue/react/angular等框架已经是必不可少的技能了,我们都知道,vue或react等MVVM框架提倡组件化开发,这样一方面可以提高组件复用性和可扩展性,另一方面也带来了项目开发的灵活性和可维护,方便多人开发协作.接下来文章将介绍如何使用react,开发一个自定义json编辑器组件.我们这里使用了jsoneditor
《精通react/vue组件设计》之快速实现一个可定制的进度条组件
前言 这篇文章是笔者写组件设计的第四篇文章,之所以会写组件设计相关的文章,是因为作为一名前端优秀的前端工程师,面对各种繁琐而重复的工作,我们不应该按部就班的去"辛勤劳动",而是要根据已有前端的开发经验,总结出一套自己的高效开发的方法.作为数据驱动的领导者react/vue等MVVM框架的出现,帮我们减少了工作中大量的冗余代码, 一切皆组件的思想深得人心.所以
《精通react/vue组件设计》之5分钟实现一个Tag(标签)组件和Empty(空状态)组件
前言 本文是笔者写组件设计的第五篇文章,之所以会写组件设计相关的文章,是因为作为一名前端优秀的前端工程师,面对各种繁琐而重复的工作,我们不应该按部就班的去"辛勤劳动",而是要根据已有前端的开发经验,总结出一套自己的高效开发的方法.作为数据驱动的领导者react/vue等MVVM框架的出现,帮我们减少了工作中大量的冗余代码, 一切皆组件的思想深得人心.所以,
面试问到烂的MVC、MVP以及MVVM
MVP 什么是MVP? 在了解MVP之前可以先观察MVC的架构模式。 MVC中三个组成部分:1. View,即视图中的各个控件;2. Controller
Android -- Fragment 基本用法、生命周期与细节注意
引言:这篇文章,大概分析下Fragment的生命周期、实际应用方法以及使用Fragment时需要注意的地方,算是Fragment的入门级文章,理解透Fragment生命周期和一些细节,就容易理解Fragment如何与外界通信等问题了。至于对其的源码分析等更加深入的内容,本文涉及不多。 Fragment的写法就不多说了,一般是继承Fragment,然后重
前端面试题自检 Vue 网络 浏览器 性能优化部分
框架Vue MVVM是什么?ModelViewViewModel , Model 表示数据模型层。view 表示视图层, ViewModel 是 View 和 Model 层的桥梁,数据绑定到 viewModel 层并自动渲染到页面中,视图变化通知 viewModel 层更新数据。 Vue 的生命周期
前端面试系列——Vue面试题
Vue 双向绑定原理 mvvm 双向绑定,采用数据劫持结合发布者订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。 双
Vue.js——60分钟快速入门
是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的。相比于Angular.js,Vue.js提供了更加简洁、更易于理解的API,使得我们能够快速地上手并使用Vue.js。
Vue进阶(四十七):面试必备:2021 Vue经典面试题总结(含答案)_IT全栈 华强工作室
面试必备:2021 Vue经典面试题总结(含答案)一、什么是MVVM?
最新最全的 Vue 面试题 ➕详解答案
前言本文整理了高频出现的 Vue 相关面试题并且附带详解答案 难度分为简单 中等 困难 三种类型 大家可以先不看答案自测一下自己的 Vue 水平哈 整理不易 如果觉得本文有帮助 记得点赞三连哦 十分感谢! 简单 1 MVC 和 MVVM 区别 MVCMVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器
前端开发进化之路
初级程序员仅能完成简单模块和项目的开发工作,难以胜任复杂模块的开发。通常是入行不久, 1 年及以下工作经验的同学。 能力要求1. 熟悉前端基础知识如 HTML、JS、CSS 。 2. 能够使用一门 MVVM 框架进行简单的业务开发。 3. 遇到复杂的组件和模块,会找现有的轮子使用。 4. 会使用百度、google 等检索工具搜索问题
前端培训-Vue专题之Vue基础
简介特点:MVVM框架,双向绑定,数据驱动,单页面,组件化。 区别Vue 和 jQuery 的区别:不直接操作DOM,而是操作数据。案例:Hello World 你好,世界HTML代码:xml<h1msg</h1jQuery实现javascript$("h1").text("你好,世界");Vue 实现javascriptthis.msg '你好,世界'
移动开发技术总结!看我如何一步一步攻克面试官
Android开发者该如何提升自己?我有两点建议: 1、横向发展:(适合有自己专精技术的人) 在自己擅长领域深入钻研的同时,会Hybrid开发(Flutter,RN,H5)之外,还要会web开发、微信小程序。但是,这确实比较难,全栈容易发展成全菜,啥都知道,啥都不精。 2、纵向发展:(适合大多数人) 如果做应用开发,就往应用架构方向发展;如果做系统层开发,就
这是一份用心整理的Android面试总结,涨姿势!
Android Jetpack组件的作用是什么? Navigation:一个用于管理Fragment切换的工具类,可视化、可绑定控件、支持动画等是其优点。 Data Binding:不用说,都知道,加速MVVM的创建。 Lifecycle:他是我们能够处理Activity和Fragment的生命周期的重要原因,在AndroidX的Fragment和Activ