Compose实战-以MVI的方式写Compose
一.几种常见架构模式简介
MVC架构:
M指的是Model,数据模型;
V指的是界面,一般是xml布局,或者有些界面我们直接使用代码书写的,所以也包含Activity和Fragment;
C指的是Controller,控制逻辑,一般控制逻辑都在Activity,Fragment中。
优点:
把逻辑拆分开,避免耦合。
缺点:
我们通过上面的简单介绍就可以看到一些问题,首先Activity,Fragment担当的职责过多,违背职责单一原则。实际应用中,MVC架构下的Activity,Fragment代码往往几千行,极其臃肿。
MVP架构:
M指的是Model,还是数据模型;
V指的是IView,视图逻辑接口,而不是单纯的视图了;
P指的Presenter,向导交流中心,处理核心逻辑。
我们也总结一下MVP的优点和缺点:
优点:
1.完全的解耦。其原因是我们是面向接口编程的,职责十分清晰,属于设计上的依赖倒置。
2.Presenter和Model是可以复用的。单纯的界面变化,我们只需要修改IView的实现即可。
3.由于相互之间是完全解耦的,所以可以针对M,V,P分开单独测试。
4.三者之间相互不耦合,所以方便后续扩展和升级。
缺点:
1.面向接口编程,所以各种界面渲染场景比较多的时候,会造成接口爆炸的问题。
2.接口升级时,不单单要修改实现类,有时甚至需要修改接口。
MVVM架构:
M指的是Model,仍然是数据模型;
V指的是View,视图;
VM指得是ViewModel,属于关联视图和Model的。View和Model相互不关联,通过ViewModel进行双向绑定。修改Model后,View则对应发生变化。
优点:
1.代码书写上的简洁。因为代码中少了setText这样的代码。
2.职责的清晰。相关的业务逻辑都在每个ViewModel中,不同的ViewModel承担不同的了逻辑。
3.针对数据的改变会自动反馈到界面上,使用起来会方便的多。
缺点:
缺点这块也是个人的见解。
1.因为数据数双向自动绑定的,所以监控到底是哪个ViewModel最终去修改的text值变的困难。当然,可以通过监控数据的改变来解决这问题。
小结:
MVC和MVP都属于命令式的UI,所以都需要找到一个需要设置的对象,比如setText,setBackground等等。
而MVVM和MVI属于声明式UI,只需要修改对应的参数,而不需要给到被设置的对象,所以在前端交互上,会让我们感觉到更便捷。
二.MVI架构
MVI架构是面向意图编程,我们把各种诉求转化为一个个的意图,请求列表数据是一个意图,请求详细信息也是一个意图。
M:ViewModel,指的是可以被观测数据;
V:View,指的是视图;
I:Intent,指的是意图,比如获取列表数据,获取详情搜索数据等。
具体流程我们可以用下面这张图来概括:
三.使用MVI架构来写Compose
接下来,我们会按照上面图中的顺序,一步一步的来使用MVI框架编写一个完整的界面流程。
首先.我们先引入Compose框架。创建MVIComposeActivity。
ContentViewModel的话不着急,这一步只要先创建一个ContentViewModel的类即可。
class MVIComposeActivity : ComponentActivity() {private val viewModel by viewModels<ContentViewModel>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ContentView(viewModel)}}
}
接下来,我们就要开始构建我们的意图了。
ContentViewModel中,构建我们的意图,目前先定义两个,获取列表和获取详情。
sealed class ContentIntent {//传递消息object GetContent : ContentIntent()class GetItemDetail(val select: Int) : ContentIntent()
}
这里肯定有人会问,为什么一个是object类型,一个class类型。因为第二个是带参数的,所以每次都需要重新构建一个意图,而第一个无参,则复用同一个就可以了。
第三步,我们来构建ViewModel
创建信道newsChannel。信道来接受各种用户行为发过来的意图。
创建状态管理uiState,由其完成数据的绑定。
最后在初始化的时候,进行意图和获取数据的关系绑定(handleIntent方法)。
class ContentViewModel : ViewModel() {//Channel信道,意图发送别ViewModel,val newsChannel = Channel<ContentIntent>(Channel.UNLIMITED)//状态管理var uiState by mutableStateOf(NaviViewState())init {handleIntent()}private fun handleIntent() {viewModelScope.launch {newsChannel.consumeAsFlow().collect() {when (it) {is ContentIntent.GetContent -> getContent()is ContentIntent.GetItemDetail -> getNewsDetail(it);}}}}private val newsFlow: Flow<List<String>> = flow {val list = mutableListOf<String>()list.add("111")list.add("222")emit(list)}private val detailFlow: Flow<List<String>> = flow {val list = mutableListOf<String>()list.add("111-详情")list.add("222-详情")emit(list)}private fun getContent() {viewModelScope.launch {newsFlow.flowOn(Dispatchers.Default).collect { contents ->uiState = uiState.copy(dataList = contents)}}}private fun getNewsDetail(select: ContentIntent.GetItemDetail) {viewModelScope.launch {detailFlow.flowOn(Dispatchers.Default).collect { contents ->uiState = uiState.copy(detailContent = contents[select.select])}}}
}
第四步,回过头来,我们进入到View的流程。
进入到页面后,首先进行的意图是请求所有数据。
@Composable
fun ContentView(viewModel: ContentViewModel) {//请求数据val viewState = viewModel.uiState//请求数据的意图LaunchedEffect(key1 = true) {viewModel.newsChannel.send(ContentIntent.GetContent)}//数据与View的绑定Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {viewState.dataList!!.forEachIndexed { index, it ->NewsItem(viewModel, position = index, news = it)}}viewState.detailContent?.let {ToastUtil.showCenterToast(it)}
}
则此时我们上一步中构造的handleIntent方法中的newsChannel就会收到这个意图,并进行对应的处理:
private fun handleIntent() {viewModelScope.launch {newsChannel.consumeAsFlow().collect {when (it) {is ContentIntent.GetContent -> getContent()//这里进行处理is ContentIntent.GetItemDetail -> getNewsDetail(it);}}}}
getContent方法中去请求数据(这里可以进行网络请求,我这里就不进行了),返回结果后,把数据绑定到uiState上。
private fun getContent() {viewModelScope.launch {newsFlow.flowOn(Dispatchers.Default).collect { contents ->uiState = uiState.copy(dataList = contents)}}}
至此,流程上就结束了。MVI架构下,我们只需要修改uiState就可以了。
完整代码地址:
android_all_demo/MVIComposeActivity.kt at master · aa5279aa/android_all_demo · GitHub
Compose实战-以MVI的方式写Compose相关推荐
- 使用Compose实现基于MVI架构、retrofit2、支持 glance 小部件的TODO应用
前言 现在声明式 UI 已逐渐成为主流,在客户端上,已有成熟的 Flutter 和 SwiftUi ,而原生安卓上的声明式 UI 却在去年年底才姗姗来迟. 虽然 compose 姗姗来迟,但是关于它的 ...
- Docker技术入门与实战 第二版-学习笔记-9-Docker Compose 项目-2-Compose 命令说明
Compose 命令说明 1)命令对象与格式 对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中 的服务或者容器.如果没有特别的说明,命令对象将是项目,这意味着项目中所有 ...
- Compose Multiplatform结合MVI模式--初步尝试
写了个简单的界面,包含功能: 1,列表中动态增加行和删除行 2,根据列表中的数据生成 json. 包含的文件或类: 1,Main.kt, 程序入口,使用 Window 即是 desktop 端. 2, ...
- 【卷积神经网络CNN 实战案例 GoogleNet 实现手写数字识别 源码详解 深度学习 Pytorch笔记 B站刘二大人 (9.5/10)】
卷积神经网络CNN 实战案例 GoogleNet 实现手写数字识别 源码详解 深度学习 Pytorch笔记 B站刘二大人 (9.5/10) 在上一章已经完成了卷积神经网络的结构分析,并通过各个模块理解 ...
- rest风格使用两个变量_为什么要用Rest风格,接口应该怎么定义,除了Rest还可用什么方式写接口的?...
这里是修真院后端小课堂,每篇分享文从 深度思考中的知识点--为什么要用Rest风格,如果不用Rest的话,接口应该怎么定义,在使用Rest风格之前,大家都是用什么方式写接口的? 1.背景介绍 REST ...
- 深度学习实战——利用卷积神经网络对手写数字二值图像分类(附代码)
系列文章目录 深度学习实战--利用卷积神经网络对手写数字二值图像分类(附代码) 目录 系列文章目录 前言 一.案例需求 二.MATLAB算法实现 三.MATLAB源代码 参考文献 前言 本案例利用MA ...
- Pytorch实战1:LeNet手写数字识别 (MNIST数据集)
版权说明:此文章为本人原创内容,转载请注明出处,谢谢合作! Pytorch实战1:LeNet手写数字识别 (MNIST数据集) 实验环境: Pytorch 0.4.0 torchvision 0.2. ...
- javascript 编码规范 用更合理的方式写 javascript
目录 类型 引用 对象 数组 解构 Strings 函数 箭头函数 构造器 模块 Iterators and Generators 属性 变量 Hoisting 比较运算符和等号 代码块 注释 空白 ...
- JavaScript--------------------jQuery中.bind() .live() .delegate() .on()的区别 和 三种方式写光棒事件 动画...
bind(type,[data],fn) 为每个匹配元素的特定事件绑定事件处理函数. $("a").bind("click",function(){alert( ...
最新文章
- android平板 2017,2017年后平板电脑市场将复苏
- 面试的时候的要注意的case应该怎么分析
- 预告 | 旷视天元的前世今生与移动端推理优化@清华专场
- oracle千万级分页优化,oracle千万级数据分页存储过程优化
- jzoj1166-树中点对距离【点分治】
- 【语音去噪】基于matlab小波软阈值语音降噪【含Matlab源码 531期】
- java数据库连接access_java连接Access数据库的方法
- Qt优秀开源项目之十四:SortFilterProxyModel
- “用户体验及可用性测试”前三章:读书笔记
- python排名上升_11点告诉你,Python为什么这些年在编程语言排行榜上一直上升?...
- 2022Java学习笔记八十八(网络编程:UDP通信,一发一收,多发多收消息接收实现)
- qt creator 32位linux,Qt Creator下载mac版-Qt Creator Mac版下载 V4.14.2-PC6苹果网
- xshow-1. 项目简介
- 100人坐飞机,第一个乘客在座位中随便选一个坐下,第100人正确坐到自己坐位的概率是?
- 斯图飞腾Stratifyd发布《2021金融服务行业客户体验报告》
- 【c++】vector中删除元素
- 能阅读计算机英语,全阅读英语电脑版
- C#-数组截取的方法
- Django ckeditor自定义表情包
- 基于钣金工艺优化的钣金件结构设计