美文网首页Android开发经验谈Android技术知识Android开发
Android体系结构设计模式:“MVI—Model-View-

Android体系结构设计模式:“MVI—Model-View-

作者: 程序老秃子 | 来源:发表于2022-05-27 15:35 被阅读0次

    全称

    MVC:Model-View-Controller

    MVP:Model-View-Presenter

    MVVM:Model-View-ViewModel

    MVI:Model-View-Intent

    与MVC,MVP或MVVM一样,MVI是一种体系结构设计模式,可帮助我们更好地组织代码以创建健壮且可维护的应用程序

    它与Flux或Redux属于同一家族,由AndréMedeiros首次引入;该首字母缩略词是由“模型”,“视图”和“意图”这两个词的紧缩形成的

    为什么推荐使用MVI,MVI是基于什么提出的

    答:主要为了ViewModel层和View层的交互由双向转化为单向,并且规范交互数据传输

    android端由mvc到mvp再到mvvm最后到mvi,每一次的变化都让代码分层更加清晰,目前MVVM的缺点是ViewModel和view的交互还是属于双向交互,viewModel和Model的处理界限也比较模糊,所以提出MVI,MVI其实是基于MVVM, 在View和ViewModel中增加了Intent来作为中间传输,通过响应编程更新UI实现的

    这样不仅规范View与ViewModel交互,且将交互顺序由View—>ViewModel->View 的双向交互变为View->Intent->ViewModel->State->View的环形交互,通过Intent和State来解决ViewModel与Model的界限模糊问题

    ● 也就是说ViewModel现在可以不关心如何被view触发,如何刷新UI,也不关心当前有多少

    数据模型,只用来维护Intent和state管理(再直白些就是intent就是view调用viewModel的中间层,state就是viewModel回调view的中间层,model通过intent和state去管理,看起来会更加简洁)

    为何使用MVI模式

    我曾经有一个瞬间觉的个人Model定义全都是错的;通过在各类安卓开发论坛也好主题也罢的讨论和头疼的研究

    ● 不管如何,最终我选择使用rxjava和Model-View-Intent(MVI)的方式构建响应式的安卓应用程序,就像这种组合我之前是没有尝试过同样,我建立是十分被动的

    ● 固然,你也会,可是,你会比我好不少

    Model 的定义

    定义的诚然,有不少模式将"View"和"Model"分离

    ● 定义的在安卓开发领域最出名的当属Model-View-Controller(MVC),Model-View_Presenter(MVP)和Model-View-ViewModel(MVVM)。你能够从名字看出什么东西么?他们都有Model;可是,我发现大多数时间,我根本没有用Model

    例子:仅仅是在后台加载一个persons的列表,一个传统的MVP模式的代码是这样的:

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n379" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class PersonsPresenter extends Presenter<PersonsView> {
    
     public void load(){
     getView().showLoading(true); // 显示一个加载进度条
    
     backend.loadPersons(new Callback(){
     public void onSuccess(List<Person> persons){
     getView().showPersons(persons); // 显示人列表
     }
    
     public void onError(Throwable error){
     getView().showError(error); // 显示错误信息
     }
     });
     }
    }</pre>
    

    可是到底什么是"Model"?后台请求是Model?不是,Model应当是业务逻辑;它是做为结果的列表?不是,它仅仅只作一件事情,就是咱们View显示所须要的东西,像加载指示器或错误信息;所以,真正的Model“”什么样的?

    若是按照我对View的理解,那么,Model类应当是这样的:

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n382" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class PersonsModel {
     // 在真实的项目中,须要定义为私有的
     // 而且咱们须要经过getter和setter来访问它们
     final boolean loading;
     final List<Person> persons;
     final Throwable error;
    
     public(boolean loading, List<Person> persons, Throwable error){
     this.loading = loading;
     this.persons = persons;
     this.error = error;
     }
    }</pre>
    

    那么Presenter应该“长”这样的:多线程

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n384" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class PersonsPresenter extends Presenter<PersonsView> {
    
     public void load(){
     getView().render( new PersonsModel(true, null, null) ); // 显示一个加载进度条
    
     backend.loadPersons(new Callback(){
     public void onSuccess(List<Person> persons){
     getView().render( new PersonsModel(false, persons, null) ); // 显示人列表
     }
    
     public void onError(Throwable error){
     getView().render( new PersonsModel(false, null, error) ); // 显示错误信息
     }
     });
     }
    }</pre>
    

    如今在屏幕上的View有了一个将被渲染上去的Model;这个概念其实不是什么新概念;最开始的被Trygve Reenskaug在1979年定义的MVC模式的Model的定义几乎一致:View观察Model的变化

    ● 不幸的是,MVC这个术语被滥用来描述太多不一样的模式,它们与最原始的MVC定义有了出入。例如,后端工程师使用MVC框架,iOS工程师有ViewController,在安卓开发中MVC的真正含义是什么?Activities是Controller?那么ClickListener意味着什么?如今MVC与最初被Reenskaug定义的MVC来说,这个术语被误解,滥用和错误使用

    Model须要解决咱们在安卓开发中常常遇到的问题:框架

    ● 状态问题

    ● 屏幕方向问题

    ● 在页面堆栈中导航

    ● 进程死亡

    ● 单向数据流的不变性

    ● 可调试和可重现的状态

    ● 测试

    MVI详解

    1、Intent 是为了让 ViewViewModel 更加解耦。这一点连自圆其说都做不到。View 依然持有 ViewModel,解耦从何谈起?反倒是现在不仅持有了 ViewModel,还会和一群 Intent 耦合,这明显是增加耦合。

    2、Intent使得ViewViewModel的契约更加清晰。说的没错,ViewViewModel 发送命令的全集能通过 Intent 一览无余,但浏览ViewModel的公共方法不是有同样的效果吗?

    3、MVI 强调对UI State的集中管理,只需要订阅一个 ViewState便可获取页面的所有状态,相对 MVVM 减少了不少模板代码。

    对于复杂界面只订阅一个 State 的话会痛苦不堪的(详见“真唯一数据源”小节)。MVI 整出个“唯一数据源”原来是为了减少模板代码?完美避开了重点~

    4、对于 State 来说添加状态只需要添加一个属性,降低了ViewModel与View层的通信成本,将业务逻辑集中在ViewModel中,View层只需要订阅状态然后刷新即可

    难道 MVVM 中增加状态不是添加一个属性?难道 MVVM 中 View 层不是订阅状态即可?难道 MVVM 中业务逻辑不是集中在 ViewModel 中?

    5、MVVM 的痛点之一:当页面复杂时,需要定义很多 State,并且需要定义可变与不可变两种,状态会以双倍的速度膨胀,模板代码较多且容易遗忘 这不是 MVVM 的痛点,而是使用不当造成的副作用。MVVM 中的 M 被错误的理解并使用,如果它能做到唯一可信数据源,就不存在该痛点了。另外 MVI 中数据持有者也有可变和不可变两个版本,这样做是为了确保唯一可信数据源

    MVI架构次要有以下长处

    1、强调数据单向流动,很容易对状态变动进行跟踪和回溯,在数据一致性,可测试性,可维护性上都有肯定劣势

    2、强调对UI State的集中管理,只须要订阅一个ViewState便可获取页面的所有状态,绝对 MVVM 缩小了不少模板代码

    3、增加状态只须要增加一个属性,升高了ViewModelView层的通信老本,将业务逻辑集中在ViewModel中,View层只须要订阅状态而后刷新即可

    ● 当然在软件开发中没有最好的架构,只有最合适的架构,各位可依据状况选用适宜我的项目的架构,实际上在我看来Google在指南中举荐应用MVI而不再是MVVM,很可能是为了对立AndroidCompose的架构

    ● 因为在Compose中并没有双向数据绑定,只有单向数据流动,因而MVI是最适宜Compose的架构。

    ● 当然如果你的我的项目中没有应用DataBinding,或者也能够开始尝试一下应用MVI,不应用DataBindingMVVM架构切换为MVI老本不高,切换起来也比较简单,在易用性,数据一致性,可测试性,可维护性等方面都有肯定劣势,后续也能够无缝切换到Compose

    小结

    ● MVI 用数据流来理解界面刷新:界面是数据流的起点(生产者)也是终点(消费者),界面发出的数据叫事件,事件会用响应式编程的方式被变换为状态,最终状态又流向界面,界面通过消费状态完成刷新;在这个流动的过程中,若保证了唯一可信数据源,就能实现单向数据流

    ● 所以 MVI 和 MVP, MVVM 不同,它关心的不是具体的界面状态持有者,而是整个更新界面的数据链路流动方式和方向

    尾述

    点击 底层源码 即可 免费获取 完整代码 以及 更多学习笔记 面试视频

    技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

    Android 架构师之路还很漫长,与君共勉

    PS:有问题欢迎指正,可以在评论区留下你的建议和感受;
    欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下

    相关文章

      网友评论

        本文标题:Android体系结构设计模式:“MVI—Model-View-

        本文链接:https://www.haomeiwen.com/subject/edomprtx.html