MVVM和MVI架构模式合并为一个最好的架构,为任何Android项目提供了完美的架构。
有太多可用的体系结构模式,每种模式都有其优缺点。所有这些模式都试图实现相同的架构基本原理:
1、关注点分离(SoC) :这是一种设计原则,用于将计算机程序分为不同的部分,以便每个部分都可以解决一个单独的关注点。关注点是提供问题解决方案时重要的事情。
该原则与面向对象编程的“ 单一责任原则”密切相关,后者 指出“每个模块,类或功能都应对软件提供的功能的一部分负责,而责任应由第三方完全封装。类,模块或功能。”
2、由模型驱动的UI :应用程序应从模型(最好是持久性模型)驱动UI。模型独立于View对象和应用程序组件,因此它们不受应用程序生命周期和相关问题的影响。
让我们来看看一些流行的架构模式的总结:
Trygve Reenskaug 的模型-视图-控制器体系结构是所有现代体系结构模式的基础。让我们看看在Wikipedia页面上定义的每个组件的责任
因此,模型负责表示视图的状态,结构和行为,而视图仅表示该模型。
在Model-View-ViewModel体系结构中,视图具有ViewModel的实例,并且它根据用户输入/操作调用相应的函数。此外,视图会观察ViewModel的不同可观察属性以进行更改。ViewModel根据业务逻辑处理用户输入,并修改各自的可观察属性。
在Model-View-Intent体系结构中,视图公开视图事件(用户输入/操作),并观察模型以了解视图状态的变化。我们处理视图事件,并将其转换为各自的意图,并将其传递给模型。模型层使用intent和previous view-state创建一个新的不可变视图状态。因此,这种方式遵循单向数据流原理,即数据仅在一个方向上流动:“视图”>“意图”>“模型”>“视图”。
总而言之,MVVM体系结构的最佳部分是ViewModel,但我认为它不遵循MVC模式中定义的模型的概念,因为在MVVM中,本地数据库会被视为模型,并且视图从ViewModel观察到状态变化多个可观察的属性。视图并不是直接由模型驱动的。此外,ViewModel这些多个可观察属性可能导致状态重叠问题(两个不同的状态意外显示)。
MVI模式通过添加一个实际的“模型”层来解决此问题,该层Intent可以通过视图观察状态变化。由于此模型是当前视图状态的不变的单一真相来源,因此不会发生状态重叠。
在以下架构中,我尝试将最佳的MVVM和MVI模式结合起来,以获得任何Android项目的更好架构,此外,我还通过为View和ViewModel创建基类来抽象出尽可能多的内容。
在继续之前,让我们重新强调MVI体系结构的一些基本术语:ViewState:顾名思义,这是模型层的一部分,并且我们的视图观察该模型的状态变化。ViewState应该表示任何给定时间的视图的当前状态。因此,此类应具有我们视图所依赖的所有变量内容。每次有任何用户输入/操作时,我们都将公开此类的修改后的副本(以保持未修改的先前状态)。我们可以使用Kotlin的数据 类创建此模型。
data class MainViewState(val fetchStatus: FetchStatus, val newsList: List)
sealed class FetchStatus {
object Fetching : FetchStatus()
object Fetched : FetchStatus()
object NotFetched : FetchStatus()
}
ViewEffect:在Android中,我们有某些更像是一劳永逸的操作,例如Toast,在这种情况下,我们无法使用ViewState来维护状态。这意味着,如果我们使用ViewState显示Toast,则它将在配置更改时或每次出现新状态时再次显示,除非并且直到我们通过传递“ toast show”事件重置其状态为止。而且,如果您不希望这样做,则可以使用ViewEffect,因为它基于 SingleLiveEvent 并且不维护状态。ViewEffect也是我们模型的一部分,我们可以使用Kotlin的密封类来创建它。
sealed class MainViewEffect {
data class ShowSnackbar(val message: String) : MainViewEffect()
data class ShowToast(val message: String) : MainViewEffect()
}
ViewEvent:它表示用户可以在视图上执行的所有操作/事件。这用于将用户输入/操作传递给ViewModel。我们可以使用Kotlin的密封类创建此事件集。
sealed class MainViewEvent {
data class NewsItemClicked(val newsItem: NewsItem) : MainViewEvent()
object FabClicked : MainViewEvent()
object OnSwipeRefresh : MainViewEvent()
object FetchNews : MainViewEvent()
}
我建议您将这三个类保存在一个文件中,因为它将使您对目标视图正在处理的所有可行操作和可变内容有一个总体了解。
现在,让我们更深入地研究架构:
上图可能为您提供了此体系结构的核心思想。此体系结构的核心思想是,我们将实际的不可变模型层包括在MVVM体系结构中,并且我们的视图依赖于该模型进行状态更改。这样,ViewModel负责修改并公开此单个模型。
为了避免冗余并简化在多个地方使用此体系结构,我创建了两个抽象类,一个用于我们的视图(活动,片段,自定义视图是独立的),另一个是用于ViewModel。
AacMviViewModel:创建ViewModel的通用基类。它需要STATE,EFFECT和EVENT三个类。上面我们已经看到了这些类的示例。
open class AacMviViewModel(application: Application) :
AndroidViewModel(application), ViewModelContract {
private val _viewStates: MutableLiveData = MutableLiveData()
fun viewStates(): LiveData = _viewStates
private var _viewState: STATE? = null
protected var viewState: STATE
get() = _viewState
?: throw UninitializedPropertyAccessException("\"viewState\" was queried before being initialized")
set(value) {
Log.d(TAG, "setting viewState : $value")
_viewState = value
_viewStates.value = value
}
private val _viewEffects: SingleLiveEvent = SingleLiveEvent()
fun viewEffects(): SingleLiveEvent = _viewEffects
private var _viewEffect: EFFECT? = null
protected var viewEffect: EFFECT
get() = _viewEffect
?: throw UninitializedPropertyAccessException( "\"viewEffect\" was queried before being initialized")
set(value) {
Log.d(TAG, "setting viewEffect : $value")
_viewEffect = value
_viewEffects.value = value
}
@CallSuper
override fun process(viewEvent: EVENT) {
Log.d(TAG, "processing viewEvent: $viewEvent")
}
}
它有viewModel,renderViewState()而且renderViewEffect()我们需要实现抽象的特性/功能。此外,它创建viewStateObserver,viewEffectObserver内部LiveData-观察员并开始观察viewStates(),并viewEffects()在由所述视图模型暴露活动activity中onCreate()。因此,此抽象活动activity将执行我们必须在每个活动activity中进行的所有操作。此外,它记录每个观察到的viewState和viewEffect。
现在,为这个架构创建一个新的活动activity非常容易:
class MainActivity : AacMviActivity() {
override val viewModel: MainActVM by viewModels()
override fun renderViewState(viewState: MainViewState) {
//Handle new viewState
}
override fun renderViewEffect(viewEffect: MainViewEffect) {
//Show effects
}
}
仅此而已,我们就可以将所有内容准备就绪,无缝工作,记录我们正在处理的每个动作和内容。状态不会重叠,因为模型是视图状态更改的唯一事实来源。
注意:如果您不熟悉这种“模型驱动的用户界面”,那么您可能会认为我们比直接处理要增加更多的复杂性,因为对于某些复杂的视图,ViesState数据类将具有如此多的属性,因为每个小部件及其内容必须具有内容可见度等。但是请相信我,它将很有意义,因为追查任何问题/崩溃的原因将非常容易。
1、许多人建议使用 ConflatedBroadcastChannel, 因为它仅发布最新值,但仍处于试验阶段,因此我更喜欢使用LiveData。而且无论如何,即使我们将来决定使用ConflatedBroadcastChannel / Flow,也将只需要修改抽象类。
2、如果我们有一个活动和多个片段或自定义视图,该怎么办?我们如何在活动到片段之间,片段到片段之间进行交流?欢迎大家评论区讨论。
分享一份由几位大佬一起收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料分享出来,希望可以帮助到大家提升进阶。
喜欢本文的话,不妨给我点个小赞、评论区留言或者转发支持一下呗~
作为系统架构师,必须成为所在开发团队的技术路线指导者;具有很强的系统思维的能力;需要从大量互相冲突的系统方法和工具中区分出哪些是有效的,哪些是无效的。架构师应当是一个成熟的、丰富的、有经验的、有良好教育的、学习快捷、善沟通和决策能力强的人。
知识要点:
随着互联网企业的不断发展,产品项目中的模块越来越多,用户体验要求也越来越高,想实现小步快跑、快速迭代的目的越来越难,插件化技术应用而生。如果没有插件化技术,美团、淘宝这些集成了大量“app”的应用,可能会有几个g那么大。
知识要点:
在不同层次的开发工程师手里,因为技术水平的参差不齐,即使很多手机在跑分软件性能非常高,打开应用依然存在卡顿现象。
另外,随着产品内容迭代,功能越来越复杂,UI页面也越来越丰富,也成为流畅运行的一种阻碍。综上所述,对APP进行性能优化已成为开发者该有的一种综合素质,也是开发者能够完成高质量应用程序作品的保证。
启动速度
流畅度
抖音在APK包大小资源优化的实践
优酷响应式布局技术全解析
网络优化
手机淘宝双十一性能优化项目揭秘
高德APP全链路源码依赖分析
彻底干掉OOM的实战经验分享
微信Android终端内存优化实践
Android框架体系架构
Android框架体系架构(高级UI+FrameWork源码) 这块知识是现今使用者最多的,我们称之Android2013~2016年的技术。
Android开发者也往往因为网上Copy代码习惯了而导致对这块经常“使用”的代码熟悉而又陌生:熟悉的是几乎天天在和它们打交道, 天天在复制这些代码 ;陌生的是虽然天天和这些代码打交道,但是并没有深入研究过这些代码的原理,代码深处的内涵。
本篇知识要点:
在Android中,NDK是一系列工具的集合,主要用于扩展Android SDK。NDK提供了一系列的工具可以帮助开发者快速的开发C或C++的动态库,并能自动将so和Java应用一起打包成apk。
每一个移动开发者都在为 Flutter 带来的“快速开发、富有表现力和灵活的 UI、原生性能”的特色和理念而痴狂,从超级 App 到独立应用,从纯 Flutter 到混合栈,开发者们在不同的场景下乐此不疲的探索和应用着 Flutter 技术,也在面临着各种各样不同的挑战。
本篇知识要点:
微信小程序开发
微信小程序作为现在比较火的编程开发应用场景之一,深受市场的青睐,这让不少开发者眼馋不已。但是对于初学者来说,就完全摸不着头脑了,不知道微信小程序开发制作需要学习那些知识,有需要的朋友可以参考本篇。
本篇知识要点:
Android相关源码解读
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。