如何应对Android面试官 -> 玩转 MVx(MVC、MVP、MVVM、MVI)

手把手教你搭建 Android MVI架构: MVI + kotlin + Flow Android MVI是一种用于构建Android应用程序的架构模式,其核心思想在于实现单向数据流和唯一可信数据源。在MVI架构中,应用程序的状态管理得到简化,并且用户界面与业务逻辑之间的交互更加清晰和规范。UIState。 阅读详情

前言


image.png

本章主要基于以下几个方向进行 MVx 的讲解,带你玩转 MVx;

MVC、MVP、MVVM、MVI 它们到底是什么?


分文件、分模块、分模式

一个文件打天下

为什么不要用一个页面打天下?

页面是给用户看的,随着版本的迭代,页面上的交互需求也在不断的变化,导致页面不断的在修改,页面在不断的修改,那么这个文件中就不能放太多的代码,否则导致后续迭代起来就很困难;

页面很复杂怎么办?

一个复杂页面,也是由一个一个的 View 组成的,那么我们就可以将 View 拆成自定义 View。同时将这个 View 的相关逻辑,拆分到对应的逻辑处理层;

应用开发原则

  • 遵循面向对象的SOLID原则;
  • 视图、数据、逻辑分离;

六大原则

image.png

  • 单一职责

一个 class 完成一件事情;一个 class 只做一件事情,当有更多事情的时候,使用继承,那么就引申出了『开闭原则』;

  • 开闭原则

对继承开放,对修改关闭(不能改变基类中的逻辑);多个类文件完成多件事情的时候,使用继承,对文件尽量不做修改;

  • 里氏替换原则

子类覆写父类函数的时候,不能改变父类的逻辑;例如:「基类车 有一个 run 方法,子类覆写之后不能改成 fly 方法」

  • 依赖倒置原则

不依赖实现,只依赖接口;「例如车在路上跑,车不应该依赖路的具体实现,而是一个路的接口」;

  • 接口隔离原则

接口的粒度要小,接口的最小化;『例如,人和路的接口要分别定义,而不是定义成一个接口』;

  • 迪米特原则

最小支持原则;

视图、数据、逻辑分离

从静态角度的一个分离,以及生命周期的控制,数据需要在什么情况下进行销毁;

App 架构设计

image.png

整体遵循一个 层次化、模块化、控件化;

而模块化又可以细分成:组件化、插件化;

MacHi 2024-06-05 20-48-18.png
image.png
组件化一般针对的都是业务组件,业务组件之间互相不依赖,公共部分抽象成接口,下沉到通用组件;组件化的意义是通过 App 模块把这些组件拼装起来,组装成一个 APK,这种就是在编译期间,它们是一个一个的组件;

而插件化,本质上可以理解为,把这一个一个的组件打包成一个一个的无图标的 APK,然后通过后下载的方式集成到应用中,以此来缩小安装包的体积;

MVx

在 MVx 中,这个 M 基本都是一样的,V 的角色其实是有一些差异的;

MVC

数据层、视图层、控制层(逻辑层)如何定义?

image.png

  • 数据层

对数据 + 对数据进行的操作(不依赖视图的操作);例如我们利用 RxJava 对数据进行变换的操作,map、flatMap等等,放到 model 层执行;

  • 视图层

不同的模式有不同的定义,在 Android 中就是 Activity + xml + fragment;

  • 控制层

view 和 model 之间的通信和交互;

在 Andriod 中 xml 布局的功能性太弱, Activity 实际上负责了 View 层与 Controller 层两者的工作,所以在 Android 中 MVC 更像是这种形式,而 Model 层好多人直接把 bean 写在了 model 下,它俩并不能完全相等;

image.png

MVC 相对一个文件打天下

优点是:抽离了 model;

缺点是:controller 的权力太大,在 Android 中 Activity 承担了 Controller 的角色,如果这个 Activity 页面逻辑太多的话,这个 Activity 依然会变得特别臃肿,就又回到了一个文件打天下的场景了;

那么,怎么办呢?变! MVP 来了

MVP

image.png

  • Model

主要负责数据的处理,这个没有什么变化;

  • View

对应于 Activity 与 XML,只负责显示 UI,只与 Presenter 层交互,与 Model 层没有耦合;

  • Presenter

主要负责处理业务逻辑,通过接口回调 View 层;

MVP 相对 MVC 的

优点是:在 MVC 的基础上通过 Interface 彻底分离了 View 和 Model,Activity 只剩下了 View,Presenter 承担了 View 和 Model 之间的交互,满足了单一职责,对视图数据的逻辑分离是清晰的

缺点是:引入了 interface,方法增多,引入一个方法要改很多地方,并且要面临着回调地狱;

那怎么办呢?变,MVVM 来了;

MVVM

引入 MVVM 的模式,本质上就是在 MVP 的基础上的一次变革,就是要把引入的 interface 给干掉!

image.png

可以看出 MVVM 与 MVP 最大的区别是,不用主动去刷新 UI 了,只要 model 层数据变了,就会自动更新到 UI 上;

那么在 Android 上实现数据的双向绑定是通过 DataBinding 实现的,通过 DataBinding 就是要把数据自动刷新到 View 上,View 上面有什么事件,要绑定到 ViewModel 上来;

不过,很多开发者并不喜欢使用 DataBinding,因为它需要你在 xml 中写一些逻辑,并且要在 gradle 中引入 DataBinding 的支持,就会导致文件路径的更改,DataBinding 不能自动更新等一些 ide 上比较难用的问题;

引入 DataBinding 很简单,只需要在使用它的 module 中加入下面这个代码就行:

dataBinding {
    enable true
}

这里额外插入一个 viewBinding,viewBinding 和 DataBinding 还是有区别的,viewBinding 只能省略 findViewById 的操作;DataBinding 除了 viewBinding 的能力之外,还能绑定数据,同时需要修改 xml,而 viewBinding 不需要修改 xml;

Android 上的 MVVM 一定要结合 DataBinding 来实现,否则就会变成下面的这种

image.png

View 观察 ViewModel 的数据变化并自我更新,这其实是单一数据源而不是双向数据绑定,所以其实 MVVM 的这一大特性并没有用到;

View 通过调用 ViewModel 提供的方法来与 ViewModel 交互;

View 通过 LiveData 来观察 ViewModel 的数据变化并自我更新,但这是单一数据源而不是双向数据绑定;

MVVM 相对 MVP 的

优点是:更彻底的解放了Activity,数据驱动是 MVVM 的关键词,ViewModel 不再主动调用方法去更新界面,而是主动更新数据,同时界面采用观察数据的方式,等待被更新,由于MVVM不需要写契约类Contract,也不存在Presenter层,也就不存在接口方法众多的问题;

缺点是:有问题的时候 不好找到原因;对外暴露的 LiveData 是不可变的,需要添加不勺模板代码且容易被遗忘;View 层与 ViewModel 层的交互比较分散零乱,不成体系;并且编译的时候会导致编译速度变慢,会额外生成ViewModel 文件;

这里额外插两个问题:

  • Jetpack 中的 ViewModel 和 MVVM 中的 ViewModel 它俩是一回事吗?

Jetpack 的 ViewModel 并不是 MVVM 中的 ViewModel,简单来说,MVVM 和 MVP 的最大区别在于数据绑定,而数据绑定本质上一种框架特性而非框架风格,区别在于数据绑定不能用一句简单的『建议大家使用』来推荐,只能由推荐方去实现这么一个功能,然后告诉大家『快用这个功能,你就能 MVVM 了』,也就是说,对于 Android 开发者来说,用了 JetPack 的 DataBinding 库,你才能叫用了 MVVM,光用 JetPack 的 ViewModel 是没用的,只要用了 DataBinding 库,基本架构也做了完美拆分(而不是把业务代码和界面逻辑全部放进 Activity),那么就算你不用 Jetpack 的 ViewModel,也是 MVVM (注意,不是勉强算 MVVM)而是确确实实的就是 MVVM。

  • 在 MVVM 模式中,ViewModel 是与 View 一一对应还是可以被其他 View 复用?

ViewModel 在 MVVM 中起到的作用是数据源,它内部包含的数据源恰好就是它所对应的 View 中所需要的数据。View 不需要对数据的获取做管理,也不需要对用户交互所导致的数据变化做管理,只用展示就可以;

所以,MVVM 中的 ViewModel 在原则是不被复用的;

同时,这里的 View 是 MVVM 中的 View,而不是 Android 系统的中的 View.java,每个 ViewModel 应该和每个 Activity/Fragment 做一一对应,并让它们在把数据分发到各个 View,

针对 MVVM 的缺陷,怎么办呢?变,MVI 来了;

MVI

说 MVI 之前,先来说两个概念,什么是响应式编程、命令式编程?

  • 响应式编程

持续性地赋值;响应式编程是一种面向数据流和变化传播的声明式编程范式 "数据流"和"变化传播"是相互解释的:有数据流动,就意味着变化会从上游传播到下游,变化从上游传播到下游,就形成了数据流。

  • 命令式编程

一次性赋值;

MVI 与 MVVM 比较相似,借鉴了前端框架思想,更加强调数据的单向流动和唯一数据源;

MVI 强调用响应式变成的方式进行事件到状态的变换,并且还得保证界面状态有唯一的可信的数据源,这样界面的刷新就形成了单向数据流,数据永远在一个环形结构中单向流动,不能反向流动;不管使用的是 ViewModel 还是 Presenter,MVI 关心的不是界面状态的持有者,而是整个更新界面数据链路的流动方式和方向;

image.png

单向数据流

单向数据流,界面变化是数据流的末端,界面消费上游产生的数据,并随上游数据的变化进行刷新;

用户操作以 Intent 的形式通知 Model;

Model 基于 Intent 更新 State;

View 接收到 State 的变化刷新 UI;

整个流程中包含两个数据:

数据一:从界面发出的事件(意图),即 MVI 中 I(Intent)。在 MVP 和 MVVM 中,界面发出的事件是通过一个 Presenter/ViewModel 的函数调用实现的,这是命令式的。为了实现响应式编程,需把这个函数调用转换成一个数据,即 Intent。

数据二:返回给界面的状态,即 MVI 中的 M(Model),它通常被称为状态 State,从字面就可以感觉到界面状态是会时刻发生变化的;

MVI 相对 MVVM 的

优点是:强调数据单向流动,很容易对状态变化进行跟踪和回溯;State 的集中管理,只需要订阅一个 ViewState 就可以获取界面的所有状态;

基于 MVI + Flow 的实战登录模块

image.png

intent 模块定义了 LoginViewEvent 用来分发数据,LoginViewState 用来响应数据,LoginViewAction 来更新 ViewState;

sealed class LoginViewEvent {
    data class ShowToast(val message: String): LoginViewEvent()
    object ShowLoadingDialog: LoginViewEvent()
    object DismissLoadingDialog: LoginViewEvent()
}

LoginViewState

data class LoginViewState(val userName: String= "", val password: String = "") {
    val isLoginEnable: Boolean
        get() = userName.isNotEmpty() && password.length >= 6

    val passwordTipVisible: Boolean
        get() = password.length in 1..5
}

LoginViewAction

sealed class LoginViewAction {
    data class UpdateUserName(val userName: String): LoginViewAction()
    data class UpdatePassword(val password: String): LoginViewAction()
    object Login: LoginViewAction()
}

我们使用 ViewModel 来承载 MVI 的 model 层,总结结构和 MVVM 也比较类似,主要区别在于 Model 与 View 的交互部分;

LoginViewModel 中承载 UI 状态,并暴露 ViewState 供 View 订阅;

View 层通过 LoginViewAction 更新 LoginViewState;

class LoginViewModel: ViewModel() {

    private val _viewState =  MutableStateFlow(LoginViewState())
    val viewStates = _viewState.asStateFlow()

    private val _viewEvent = MutableSharedFlow<LoginViewEvent>()
    val viewEvents = _viewEvent.asSharedFlow()


    fun dispatch(loginViewAction: LoginViewAction) {
        when(loginViewAction) {
            is LoginViewAction.UpdateUserName -> {
                updateUserName(loginViewAction.userName)
            }

            is LoginViewAction.UpdatePassword -> {
                updatePassword(loginViewAction.password)
            }

            is LoginViewAction.Login -> {
                login()
            }
        }
    }

    private fun updateUserName(userName: String) {
        _viewState.value = _viewState.value.copy(userName = userName)
    }

    private fun updatePassword(passWord: String) {
        _viewState.value = _viewState.value.copy(password = passWord)
    }

    private fun login() {
        viewModelScope.launch(Dispatchers.Main) {
            flow {
                if (loginLogic()){
                    emit("登录成功")
                } else {
                    emit("登录失败")
                }
            }.flowOn(Dispatchers.IO).onStart {
                _viewEvent.emit(LoginViewEvent.ShowLoadingDialog)
            }.onEach {
                _viewEvent.emit(LoginViewEvent.DismissLoadingDialog)
                _viewEvent.emit(LoginViewEvent.ShowToast(it))
            }.catch {
                _viewState.value = _viewState.value.copy(userName = "")
                _viewState.value = _viewState.value.copy(password = "")
                _viewEvent.emit(LoginViewEvent.DismissLoadingDialog)
                _viewEvent.emit(LoginViewEvent.ShowToast(it.message.toString()))
            }.collect()
        }
    }

    private suspend fun loginLogic(): Boolean {
        viewStates.value.let {
            // 执行 http 请求
            if (it.userName == "Kobe" && it.password == "123456") {
                delay(2000)
                return true
            } else {
                return false
            }
        }
    }
}

image.png

登录页面也比较简单,就是两个输入框和一个登录按钮;

class LoginActivity: AppCompatActivity() {

    private val userName: EditText by lazy { findViewById(R.id.userName) }
    private val passWord: EditText by lazy { findViewById(R.id.passWord) }
    private val login: Button by lazy { findViewById(R.id.login) }
    private val pwdTips: TextView by lazy { findViewById(R.id.pwdTips) }
    private val viewModel: LoginViewModel = LoginViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        initView()
        initViewStates()
        initViewEvents()
    }

    private fun initView() {
        userName.addTextChangedListener {
            viewModel.dispatch(LoginViewAction.UpdateUserName(userName = it.toString()))
        }
        passWord.addTextChangedListener {
            viewModel.dispatch(LoginViewAction.UpdatePassword(password = it.toString()))
        }
        login.setOnClickListener {
            viewModel.dispatch(LoginViewAction.Login)
        }
    }

    private fun initViewStates() {
        viewModel.viewStates.let { states ->
            lifecycleScope.launch {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    states.map {
                        it.userName
                    }.distinctUntilChanged().collect{
                        userName.setText(it)
                        userName.setSelection(it.length)
                    }
                }
            }

            lifecycleScope.launch {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    states.map {
                        it.password
                    }.distinctUntilChanged().collect{
                        passWord.setText(it)
                        passWord.setSelection(it.length)
                    }
                }
            }

            lifecycleScope.launch {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    states.map {
                        it.isLoginEnable
                    }.distinctUntilChanged().collect {
                        login.isEnabled = it
                        login.alpha = if (it) 1f else 0.5f
                    }
                }
            }

            lifecycleScope.launch {
                repeatOnLifecycle(Lifecycle.State.STARTED) {
                    states.map {
                        it.passwordTipVisible
                    }.distinctUntilChanged().collect {
                        pwdTips.visibility = if (it) { View.VISIBLE } else { View.GONE }
                    }
                }
            }
        }
    }

    private fun initViewEvents() {
        viewModel.viewEvents.let {
            lifecycleScope.launchWhenStarted {
                it.collect {
                    when(it) {
                        is LoginViewEvent.ShowLoadingDialog -> {
                            showLoadingDialog()
                        }
                        is LoginViewEvent.DismissLoadingDialog -> {
                            dismissLoadingDialog()
                        }
                        is LoginViewEvent.ShowToast -> {
                            Toast.makeText(this@LoginActivity, it.message, Toast.LENGTH_SHORT).show()
                        }
                    }
                }
            }
        }
    }

    private var progressDialog: ProgressDialog? = null

    private fun showLoadingDialog() {
        if (progressDialog == null)
            progressDialog = ProgressDialog(this)
        progressDialog?.show()
    }

    private fun dismissLoadingDialog() {
        progressDialog?.takeIf { it.isShowing }?.dismiss()
    }
}

好了,今天 NVx 就到这里吧,感谢您的观看

下一章预告


基于 MVI 的一个新闻列表客户端实战;

欢迎三连


来都来了,点个关注,点个赞吧,你的支持是我最大的动力

Android 架构MVI、MVVM、MVC、MVP 简单了解一下MVI、MVC、MVP和MVVM。 阅读详情

相关推荐

Android~MVC、MVP、MVVM和MVI对比

本文梳理记录MVC、MVP、MVVM、MVI概念和演进,以及自己的理解。 面试时经常被问到,MVC MVP MVVM他们到底是什么?如何搭建一个页面?

Hynson的学习笔记 2268

MVC、MVP、MVVM、MVI区别概述

MVC、MVP、MVVM、MVI简单概述

lengjiye的博客 1217

Android 架构模式 MVC、MVP、MVVM、MVI

理想状态 Activity / Fragment 是只处理表现逻辑的部分 ,但现实是 Activity 天然不可避免要处理 UI,也要处理用户交互,说明 Activity 本身天然承担了 View 的角色。那么这个架构就会造成 Activity 里糅合了视图和业务的代码,分离程度不够。对可预测性和可调试性要求极高的项目,与 Jetpack Compose 高度契合。小型项目,功能非常少、UI逻辑简单的页面。中大型项目,结构清晰,易于模块化和维护。

随手纪念的博客 1027

MVC、MVP、MVVM、MVI架构

**Model(模型):**负责业务数据管理和处理,包括增删改查。Model必须提供外部可以操作模型数据的接口,同时在数据发生变化后能够通知外部 **View(视图):**用户界面。View需要感知Model的变化,数据变化时,更新用户界面 **Controller(控制器):**业务逻辑层。Controller需要感知View的事件,调用Model提供的接口对数据进行操作

star_nwe的博客 1132

别再手动画柜子了!用EPLAN导入STEP模型,5分钟搞定一个标准机柜

本文详细介绍了如何利用EPLAN Pro Panel的3D布局功能,快速将STEP模型转化为智能机柜宏,大幅提升电气工程设计效率。通过实战步骤解析,包括模型导入、智能解析、安装面定义及拼柜技巧,帮助工程师5分钟内完成标准机柜设计,避免传统手动建模的尺寸偏差问题。

weixin_26875109的博客 433

CTF实战:从ECDSA签名漏洞到flag获取

本文详细解析了ECDSA签名算法在CTF比赛中的实战应用,重点探讨了随机数重用和参数泄露等常见漏洞。通过PWNHUB六月赛的例题,展示了如何从签名数据中恢复私钥并获取flag,同时提供了防御措施和实战技巧,帮助参赛者提升解题能力。

weixin_42706667的博客 275

VGGT 快速三维重建指南:从十几张照片到 3D 点云只要几秒

你拍了一趟室内空间,手机里存了几十张照片,想把它们变成能转动的 3D 模型——老流程要跑好几小时,还常在配准那一步崩掉重来。VGGT(Visual Geometry Grounded Transformer,牛津大学视觉几何组与 Meta AI 联合开发,CVPR 2025 最佳论文奖)就是为这个痛点做的:它直接把照片喂进去,几秒内吐出相机参数、深度图、点云和三维点轨迹,省掉传统多视角几何里最耗时

gitblog_00980的博客 162

Gromacs实战:从蛋白质-小分子对接到分子动力学模拟全流程解析

本文详细解析了使用Gromacs进行蛋白质-小分子对接和分子动力学模拟的全流程,包括文件准备、拓扑生成、复合物构建、溶剂化处理、能量最小化及平衡模拟等关键步骤。通过实战案例,帮助研究者验证药物分子与靶标蛋白的结合稳定性,提升分子动力学模拟的效率和准确性。

weixin_29216353的博客 380

MVI架构理解

上面提到,intent指的是用户意图,在代码层面上来说他其实就是个枚举类,在kotlin中可以通过sealed关键字来生成封闭类,这个关键字生成的封闭类在when语句中可以不用写elseobject GetBanners: UserIntent() //定义了一个用户获取Banner的意图State是UI状态,MVI的一个特点就是数据状态统一管理,state是个和Intent一样的枚举,但是不同的是intent是个事件流,state是个状态流。

weixin_43243916的博客 2401

别再凭感觉估算!用ADS的EBOND库精确仿真Bonding线寄生电感(附完整参数设置指南)

本文详细介绍了在ADS中使用EBOND库精确仿真Bonding线寄生电感的方法,帮助射频工程师在毫米波频段实现高精度建模。通过三维物理参数和电磁场求解算法,EBOND模型能将误差控制在5%以内,显著提升电路性能。文章还提供了完整的参数设置指南和实战操作步骤,适用于5G毫米波前端模块等高频应用场景。

weixin_28730131的博客 504

机器视觉工程师如何进行高速运动物体捕获

硬件方面使用高帧率相机(如工业相机、高速摄像机)确保充足的光照条件使用高性能计算平台减少处理延迟软件方面背景减除技术快速分离运动物体卡尔曼滤波预测物体轨迹ROI技术减少计算量多线程处理提高实时性实际应用中的关键考量针对特定场景调整参数(如阈值、滤波器参数等)平衡计算资源与实时性要求结合特定领域知识优化算法(如物体运动特性)上述示例代码实现了一个基础的高速物体跟踪系统,通过背景减除和卡尔曼滤波实现对高速物体的检测和跟踪。

zhangzhechun_02的博客 624

MVX总结

汽车要运载人,人还要携带物品,汽车和人直接交互==要有限的和其他对象交互(不要了解其内部细节,不和不直接对象交互):迪米特原则,最少知识原则。View:视图,不同架构中充当角色不同 xml(java)/composable/kotlin。Android平台的实现中,有大量的View的操作在COntroller(Activity)中。思考:如果Presenter可以操作View和Model,不就等于是之前的Activity吗?MVI流程:ui,intent,viewmodel,uistate,ui。

feather(猎羽) 965

华为OD机考双机位C卷 - AI处理器组合 (Java & Python & JS & GO & C++ & C)

> 2026华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 华为OD机试双机位C卷真题目录点击查看: [【全网首发】2026华为OD机位C卷 机考真题题库含考点说明以及在线OJ(OD上机考试双机位C卷)](https://blog.youkuaiyun.com/banxia_frontend/article/details/155503868)

算法大师! 232

Android 架构 - MVI

命名采用:功能+UiState。界面刷新用到的状态全都定义成子类,集中在一起实现唯一可信数据源。子类不带参数就定义成 object 方便复用(因为创建的实例无状态区别)。一般同 ViewModel 写在同一个 .kt 文件中,也可以单独写在一个 .kt 文件中。

HugMua的博客 5220

OpenClaw 子代理配对失败问题分析与解决方案

OpenClaw子代理配对失败问题分析与解决方案 问题核心在于agent设备权限不足,仅有operator.approvals权限而缺少operator.admin权限,导致子代理启动时形成权限死锁。本文提供三种解决方案: 直接编辑设备配对文件(推荐):修改本地JSON文件,为agent设备添加完整operator.admin权限,操作简单快捷。 标准重新配对流程:删除现有agent设备后重新配对,通过正常审批流程获取完整权限,符合系统安全设计。 修改源码降低权限要求(仅限开发环境):调整子代理代码避免强制

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 148

Logstash安装部署

1. 部署及安装所用的用户不能是root。

lmj3732018的博客 4290

MVC和MVP MVVM MVI

MVC: M:model,模型(业务逻辑):进行数据库的读写等等 V:view,视图(控件)等等:进行数据的显示 C:controller(控件的方法(界面逻辑),给model传递参数之类); 比如:你点个BUTTON,隐藏个控件,这个控件决定了model某个属性.如果隐藏这个方法是在view中写的,就是MVC,如果这个方法不是在View中写的就是MVP 其中:button是view...

emmmsuperdan的博客 1057

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

浅浅的理解MVI

官网解释:hl=zh-cnMVI在架构分层上和MVP没有本质区别,但区别主要体现在架构风格和编程思想上。MVI 是 Model-View-Intent 的缩写,它也是一种响应式 + 流式处理思想的架构。MVI 的 Model 代表一种可订阅的状态模型的概念,添加了 Intent 概念来代表用户行为,采用单向数据流来控制数据流动和各层依赖关系。在代码的层面上它分为三层:UI Layer(界面层) :在屏幕上展示应用界面和数据Domian Layer(网域层) :封装复杂或可复用的业务逻辑。

shuai_ge_feng的博客 2203

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android 架构模式的演变(MVC、MVP、MVVM、MVI)

本文系统梳理了Android架构模式的演进历程,从MVC、MVP到MVVM,再到新兴的MVI模式。重点分析了每种架构的特点:MVC简单但耦合度高,MVP通过接口解耦但接口膨胀,MVVM利用数据绑定实现响应式编程,MVI则强调单向数据流和状态管理。文章指出架构演进的核心在于解决"上帝类"问题,实现职责分离、提高可测试性,并通过状态管理优化开发体验。现代架构模式如MVVM和MVI结合LiveData等组件,能更好地处理生命周期和异步操作,适应日益复杂的应用需求。

indeedes的博客 2657

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

MVI设计模式

mvc:activity即是v也是c,代码臃肿mvp:接口太多,p层容易内存泄漏mvvm:databinding生成的代码态度,livedata数据倒灌/粘性mvi:复杂页面,state膨胀面试官问:非常小的项目用什么?MVC。

qq_34178710的博客 3163

MVI 架构详解

MVI 架构 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客: 全新的界面设计 ,将会带来全新的写作体验; 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示; 增加了 图片拖拽 功能,你可以

fct_mark的博客 7164

Android架构学习之路三-MVX

前言 这是架构学习系列的第三篇,主要介绍一下 MVC, MVP 以及 MVVM 架构,至于 MVI 后面会单独介绍。这些 MVX 的目的都是为了将业务和视图分离,松耦合,作为 Android 程序猿,大多不陌生了。 一个 App 离不开 Model 和 View 这两个角色, Model 决定了 App 的数据,而 View 决定怎么向用户展示这些数据,大多框架或组件基本上都是用来处理这两者之间的交互关系的。 因此一个 App 的架构需要处理两个任务: 更新 Model —— 如何处理...

chuyouyinghe的专栏 1019

Android MVC、MVP、MVVM、MVI的架构的区别

MVC、MVP、MVVM、MVI 是软件架构设计中的几种不同模式,主要用于组织代码结构,使开发更加模块化、可维护和可测试。在实际开发中,理解每种架构模式的核心优势和局限性,并根据项目需求灵活选择或组合使用,是实现最优架构设计的关键。没有绝对的“最优”架构设计,选择哪种架构模式取决于项目需求、团队经验和技术栈。

Joey 的工作空间~ 1034

安卓应用开发中的MVC、MVP、MVVM、MVI(2)

接下来,我们需要定义Model类,用于表示计算器应用的状态。val expression: String = "", // 表达式字符串,例如 "2+3*4-5="val result: String = "", // 结果字符串,例如 "9.0"val error: String = "" // 错误信息字符串,例如 "Invalid input."这段代码定义了一个数据类Model,其中包含三个属性:expression、result和error。

大模型训练 811

Android UI 架构演进:从 MVC 到 MVP、MVVM、MVI

为了优化代码设计,业界先后提出了 MVC、MVP、MVVM 和 MVI 等架构设计。这四个模式讨论是 “如何管理 UI” 这个话题,采用的手段都是 “关注点分离”,只是实现的细节不同。最开始是没有采用任何模式的状态,不管是视图代码还是表现逻辑全都写在 Activity 里面,很明显这样的代码耦合度非常高,难以进行维护和测试,可读性也不好。MVC 其实是 Android 默认的设计,MVC 里将代码分为三个部分:MVC 初步解决了 Activity 代码太多的问题,但也有缺点:我们的初衷 Activity /

m0_64365896的博客 1334

详细解析 MVC/MVP/MVVM/MVI 架构

在 Android 开发中,架构设计的核心目标始终是解耦代码、提升可维护性、降低测试成本,而从最初的 MVC 到如今主流的 MVVM、MVI,每一种架构的诞生都是为了解决前一种架构在实际开发中暴露的痛点。我们下面从核心定义、角色分工、优缺点三个维度全面解析 MVC、MVP、MVVM、MVI 四种架构的区别,并用同一个极简业务场景实现四种架构的核心代码,所有示例基于 Kotlin 编写。

. 1780
上一篇: 如何应对Android面试官 -> 玩转 Fragment
下一篇: 如何应对 Android 面试官 -> MVVM 实战一个新闻客户端 (上)
老A说
博客等级 码龄14年 1181粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值