Redux 与 MVI:Android 应用的对比
Redux 与 MVI:Android 应用的对比
在为 Android 应用选择合适的状态管理架构时可能会感到困惑。在这个领域中,有两种流行的选择是 Redux 和 MVI(Model-View-Intent)。两者都有各自的优缺点,因此在深入研究之前了解它们的区别至关重要。
本指南将深入探讨 Redux 和 MVI 的核心原则,突出它们在 Android 开发中的关键区别。此外,我将提供一些有用的资源链接,这些资源提供了更深入的见解和实用的实现示例。
Redux
- 集中式状态存储:Redux 将所有应用程序状态存储在一个称为“store”的单个不可变数据结构中。这种集中式方法使状态转换可预测且易于调试。
- 单向数据流:行为(Actions)代表应用程序中的事件,是修改状态的唯一方式。行为被发送到存储库,触发减速器(Reducers)根据纯函数更新状态。这种单向流使得对状态变化的推理更加容易。
- 中间件处理副作用:虽然 Redux 专注于管理纯状态,但中间件函数可以处理诸如网络调用或本地存储更新等副作用。这种关注点分离保持了核心 Redux 逻辑的清晰度。
- 解耦的架构:MVI 将 UI(View)与业务逻辑(Model)和用户交互(Intent)分离。这种模块化促进了代码的可重用性和可测试性。
- 响应式状态更新:模型根据意图(Intent)发出新状态,通过响应式绑定机制自动更新视图。这消除了显式状态管理操作的需要。
- 不可变数据模型:与 Redux 类似,MVI 强调使用不可变数据结构来实现模型,确保可预测的状态变化和更简单的推理。
MVI
- 解耦的架构:MVI 将用户界面(View)与业务逻辑(Model)和用户交互(Intent)分离开来。这种模块化促进了代码的可重用性和可测试性。
- 响应式状态更新:模型通过发出新的状态来响应意图,这通过一种响应式绑定机制自动更新视图。这消除了需要显式状态管理操作的必要性。
- 不可变数据模型:与 Redux 类似,MVI 强调使用不可变数据结构来构建模型,确保状态变化可预测且更容易推理。
Android 特定考虑因素
- 库和框架:Redux 和 MVI 都有专门的 Android 库和框架,例如 redux-kotlin-android 和 arkivia-mvi。这些库简化了与 Android 组件的集成,并提供了管理状态和副作用的有用工具。
- 测试:这两种架构都有成熟的测试方法。对于 Redux,像 redux-mock-store 这样的测试框架可以实现高效的单元测试和集成测试。MVI 的响应式特性通常通过使状态更加显式来简化测试编写。
示例代码
Redux 示例代码
Action Types 定义
sealed class ActionType {object IncrementCounter : ActionType()object DecrementCounter : ActionType()
}
Action Creator 函数
fun incrementCounter(): ActionType = ActionType.IncrementCounter
fun decrementCounter(): ActionType = ActionType.DecrementCounter
Reducer 函数
fun reducer(state: Int, action: ActionType): Int {return when (action) {is ActionType.IncrementCounter -> state + 1is ActionType.DecrementCounter -> state - 1}
}
Store 创建与初始化
class Store(private val reducer: (Int, ActionType) -> Int) {private var state: Int = 0private val listeners: MutableList<() -> Unit> = mutableListOf()fun getState(): Int = statefun dispatch(action: ActionType) {state = reducer(state, action)listeners.forEach { it.invoke() }}fun subscribe(listener: () -> Unit) {listeners.add(listener)}
}
使用示例
fun main() {val store = Store(::reducer)val listener: () -> Unit = { println("Current counter value: ${store.getState()}") }store.subscribe(listener)store.dispatch(incrementCounter())store.dispatch(incrementCounter())store.dispatch(decrementCounter())
}
MVI 示例代码
Model 定义
data class CounterModel(val count: Int)
Intent 类型定义
sealed class CounterIntent {object Increment : CounterIntent()object Decrement : CounterIntent()
}
ViewModel 创建与初始化
class CounterViewModel : ViewModel() {private val _counterState = MutableLiveData<CounterModel>()val counterState: LiveData<CounterModel>get() = _counterStateinit {_counterState.value = CounterModel(0)}fun processIntent(intent: CounterIntent) {val currentCount = _counterState.value?.count ?: 0when (intent) {is CounterIntent.Increment -> _counterState.value = CounterModel(currentCount + 1)is CounterIntent.Decrement -> _counterState.value = CounterModel(currentCount - 1)}}
}
使用示例
fun main() {val viewModel = CounterViewModel()val observer = Observer<CounterModel> { counterModel ->println("Current counter value: ${counterModel.count}")}viewModel.counterState.observeForever(observer)viewModel.processIntent(CounterIntent.Increment)viewModel.processIntent(CounterIntent.Increment)viewModel.processIntent(CounterIntent.Decrement)
}
注意:
- Redux 示例中的 Store 是手动实现的简化版本,而在实际应用中通常会使用第三方库来管理 Redux Store。
- MVI 示例中使用了 Android 架构组件的 ViewModel 和 LiveData 来实现单向数据流。
有用的资源
Redux
- Redux 文档:https://redux.js.org/
- Kotlin Redux 教程:https://www.youtube.com/watch?v=BUAxqiGrKOc
- Android Redux 库:https://github.com/reduxkotlin/redux-kotlin
MVI
- MVI 文档:https://github.com/adidas/mvi
- Arkivia-MVI 库:https://github.com/badoo/MVICore
- MVI vs. Redux for Android:https://medium.com/@chessmani/yup-by-the-way-mvi-is-really-no-different-from-redux-its-just-a-different-name-which-i-wish-a3f3fe334fd9
结论
选择 Redux 还是 MVI 取决于您的特定需求和偏好。在做出决定时考虑诸如项目复杂性、开发人员经验和所需的模块化水平等
相关文章:
Redux 与 MVI:Android 应用的对比
Redux 与 MVI:Android 应用的对比 在为 Android 应用选择合适的状态管理架构时可能会感到困惑。在这个领域中,有两种流行的选择是 Redux 和 MVI(Model-View-Intent)。两者都有各自的优缺点,因此在深入研究之前了解它们…...
《MySQL是怎样运行的》读书笔记(三) B+树索引
前言 从前面数据存储结构中我们已经知道了页和记录的关系示意图: 其中页a、页b、页c ... 页n 这些页可以不在物理结构上相连,只要通过双向链表相关联即可。 在正式介绍索引之前,我们需要了解一下没有索引的时候是怎么查找记录的。下边先只讨论搜索条件…...
微信小程序基础工作模板
1.轮播图 点击跳转官方文档 简单例子 <!-- 顶部轮播图 --> <swiper indicator-dots"true" class"banner" autoplay"true" interval"2000"><swiper-item><image src"../../images/轮播图1.jpg" >…...
简单说一下STL中的map容器的特点、底层实现和应用场景【面试】
特点: 基于红黑树:std::map利用红黑树的自平衡特性,确保操作的平衡性。有序容器:元素根据键的顺序自动排序,排序依据是预定义的键比较函数。唯一键值:容器保证每个键的唯一性,不允许重复键存在…...
Ubuntu22.04之有道词典无法画词翻译替代方案(二百四十九)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…...
AnythingLLM 的 Docker 使用
AnythingLLM是使用大语言模型LLM的一站式简便框架。官网的介绍如下: AnythingLLM is the easiest to use, all-in-one AI application that can do RAG, AI Agents, and much more with no code or infrastructure headaches. 1. 使用官方docker 最方便的方法是使…...
数组还可以这样用!常用但不为人知的应用场景
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一…...
C++模板元编程:编译时的魔法
1. 引言 在C的世界中,模板元编程是一种在编译时执行计算的强大技术。它允许开发者编写高度灵活和高效的代码,这些代码可以在不牺牲性能的前提下,根据类型和值的不同而变化。本文将深入探讨模板元编程的奥秘,并展示如何在现代C开发…...
SQL进阶day10————多表查询
目录 1嵌套子查询 1.1月均完成试卷数不小于3的用户爱作答的类别 1.2月均完成试卷数不小于3的用户爱作答的类别 编辑1.3 作答试卷得分大于过80的人的用户等级分布 2合并查询 2.1每个题目和每份试卷被作答的人数和次数 2.2分别满足两个活动的人 3连接查询 3.1满足条件…...
debug调试_以Pycharm为例
文章目录 作用步骤打断点调试调试窗口 作用 主要是检查逻辑错误,而非语法错误。 步骤 打断点 在需要调试的代码行前打断点,执行后会停顿在断点位置(不运行) 调试 右键“debug”,或者直接点击右上角的小虫子 调试…...
wms第三方海外仓系统:如何为中小型海外仓注入新活力
对于中小型海外仓来说,想在大型集团海外仓同台竞争中获得优胜,提升其管理效率是非常关键的一环。 我们所熟知的wms系统,也就是第三方成熟海外仓系统,正是这些海外仓企业提升管理水平、降低成本的重要工具。 1、wms第三方海外仓系…...
html是什么?http是什么?
html Html是什么?http是什么? Html 超文本标记语言;负责网页的架构; http((HyperText Transfer Protocol)超文本传输协议; https(全称:Hypertext Transfer Protocol …...
L1-007 念数字js实现
异步解法 const readline require("readline"); const rl readline.createInterface({input: process.stdin,output: process.stdout, }); const input_arr [];//储存数据 rl.on(line, function (line) {input_arr.push(line); } ); rl.on(close, function () {/…...
Perl 运算符
Perl 运算符 Perl 是一种功能强大的编程语言,广泛应用于系统管理、网络编程、GUI 创建、数据库访问等众多领域。Perl 的语法灵活,支持多种编程范式,包括过程式、面向对象和函数式编程。在 Perl 中,运算符扮演着重要的角色&#x…...
语法04 C++ 标准输入语句
标准输入 使用格式:cin >> 输入的意思就是把一个值放到变量里面去,也就是变量的赋值,这个值是由我们自己输入的。 (注意:输入变量前要先定义,输入完之后要按Enter键。) 输入多个变量,与输出类似,…...
python数据分析--- ch6-7 python容器类型的数据及字符串
python数据分析---ch6-7 python容器类型的数据及字符串 1. Ch6--容器类型的数据1.1 序列1.1.1 序列的索引操作1.1.2 加和乘操作1.1.3 切片操作1.1.4 成员测试 1.2 列表1.2.1 创建列表1.2.2 追加元素1.2.3 插入元素1.2.4 替换元素1.2.5 删除元素1.2.6 列表排序(1&…...
【Linux取经路】守护进程
文章目录 一、前台进程和后台进程二、Linux 的进程间关系三、setsid——将当前进程设置为守护进程四、daemon——设置为守护进程五、结语 一、前台进程和后台进程 Linux 中每一次用户登录都是一个 session,一个 session 中只能有一个前台进程在运行,键盘…...
Nginx之文件下载服务器
1.概述 在对外分享文件时,利用Nginx搭建一个简单的下 载文件管理服务器,文件分享就会变得非常方便。利 用Nginx的诸多内置指令可实现自动生成下载文件列表 页、限制下载带宽等功能。配置样例如下: server {listen 8080;server_name localhos…...
OpenCV学习(4.11) OpenCV中的图像转换
1. 目标 在本节中,我们将学习 使用OpenCV查找图像的傅立叶变换利用Numpy中可用的FFT功能傅立叶变换的一些应用我们将看到以下函数:**cv.dft()** ,**cv.idft()** 等 理论 傅立叶变换用于分析各种滤波器的频率特性。对于图像,使用…...
2024.6.13每日一题
LeetCode 子序列最大优雅度 题目链接:2813. 子序列最大优雅度 - 力扣(LeetCode) 题目描述 给你一个长度为 n 的二维整数数组 items 和一个整数 k 。 items[i] [profiti, categoryi],其中 profiti 和 categoryi 分别表示第 i…...
Linux命令详解(2)
文本处理是Linux命令行的重要应用之一。通过一系列强大的命令,用户可以轻松地对文本文件进行编辑、查询和转换。 cat: 这个命令用于查看文件内容。它可以一次性显示整个文件,或者分页显示。此外,cat 还可以用于合并多个文件的内容…...
iOS ReactiveCocoa MVVM
学习了在MVVM中如何使用RactiveCocoa,简单的写上一个demo。重点在于如何在MVVM各层之间使用RAC的信号来更方便的在各个层之间进行响应式数据交互。 demo需求:一个登录界面(登录界面只有账号和密码都有输入,登录按钮才可以点击操作)࿰…...
图文解析ASN.1中BER编码:结构类型、编码方法、编码实例
本文将详细介绍ASN.1中的BER编码规则,包括其编码机制、数据类型表示、以及如何将复杂的数据结构转换为二进制数据。通过本文的阅读,读者将对ASN.1中的BER编码有一个全面的理解。 目录 一.引言 二.BER编码基本结构 ▐ 1. 类型域(Type&#…...
jQuery如何停止动画队列
在jQuery中,你可以使用.stop()方法来停止动画队列。.stop()方法有几个可选的参数,可以用来控制停止动画的方式。 以下是.stop()方法的基本用法和一些参数选项: 无参数:立即停止当前动画,并跳到最后的状态。后续的动画…...
vue3+electron搭建桌面软件
vue3electron开发桌面软件 最近有个小项目, 客户希望像打开 网易云音乐 那么简单的运行起来系统. 前端用 Vue 会比较快一些, 因此决定使用 electron 结合 Vue3 的方式来完成该项目. 然而, 在实施过程中发现没有完整的博客能够记录从创建到打包的流程, 摸索一番之后, 随即梳理…...
oracle常用经典SQL查询
oracle常用经典SQL查询(转贴) oracle常用经典SQL查询 常用SQL查询: 1、查看表空间的名称及大小 select t.tablespace_name, round(sum(bytes/(1024*1024)),0) ts_size from dba_tablespaces t, dba_data_files d where t.tablespace_name d.tablespace_name grou…...
Android shell 常用 debug 命令
目录 1、查看版本2、am 命令3、pm 命令4、dumpsys 命令5、sed命令6、log定位查看APK进程号7、log定位使用场景 1、查看版本 1.1、Android串口终端执行 getprop ro.build.version.release #获取Android版本 uname -a #查看linux内核版本信息 uname -r #单独查看内核版本 1.2、…...
Unity3D Shader数据传递语法详解
在Unity3D中,Shader是用于渲染图形的一种程序,它定义了物体在屏幕上的外观。Shader通过接收输入数据(如顶点位置、纹理坐标、光照信息等)并计算像素颜色来工作。为了使得Shader能够正确运行并产生期望的视觉效果,我们需…...
计算机组成原理(五)
一、链式查询方式 接口的优先级固定不变 在链式查询的情况下,设备的优先级通常与其在链中的位置有关。具体来说,越靠近查询链的起始位置的设备通常具有较高的优先级,而越靠近链的末尾位置的设备优先级较低。 优点: 简单实现&am…...
后端项目实战--瑞吉外卖项目软件说明书
瑞吉外卖项目软件说明书 一、项目概述 瑞吉外卖项目是一个外卖服务平台,用户可以通过该平台浏览餐厅菜单、下单、支付以及追踪订单状态。产品原型就是一款产品成型之前的一个简单的框架,就是将页面的排版布局展现出来,使产品得初步构思有一…...
哪一个平阳网站建设/怎样进行关键词推广
http://www.tutorialspoint.com/execute_python_online.php转载于:https://www.cnblogs.com/chengJAVA/p/4359595.html...
常州网站快速排名优化/媒体:北京不再公布疫情数据
windows的.lib跟Unix的.a一样,属于中间级。 .o跟Windows的对象一样,是编译后的文件,需要连接起来(LINK),之后才是可执行文件。 clean:一般是make clean ,删除中间的目标文件,.o之类。可以叫别的…...
响应式网站的登录设置/深圳网络推广推荐
全国计算机技术与软件专业技术资格(水平)考试 2015上半年 系统集成项目管理工程师 下午试卷I (考试时间 14:00~16:30 共 150 分钟) 1.在答题纸的指定位置填写你所在的省、自治区、直辖市、计划单列市的名称。 2.在答题纸的指定位置填写准考证号、出生年月日和姓名。 3…...
佛山优化公司推广/朝阳seo推广
2019独角兽企业重金招聘Python工程师标准>>> 前段时间,在现网的error.log日志中出现了很多如下的异常 redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool <bean id"jedisPool"class"…...
那个网站可教做课件好/今日头条十大新闻最新
智遥工作流的表单开发与众不同,他们公司的顾问一个劲的夸赞。多次强调他们的表单可以自动生成,扩展灵活,控件丰富,表单填写方便。今天特意测试了一下,感觉很给力。先说说以往用过的OA系统吧:之前用过的金和…...