万万哇 发表于 2025-4-14 19:29:32

241.Redux架构

1. Redux 架构简介

Redux 最初起源于 JavaScript 世界,用于解决前端应用状态管理问题,其焦点头脑是将整个应用的状态存储在一个全局的、只读的“Store”中,通太过发(dispatch)动作(Action)来更新状态,最终通过纯函数(Reducer)计算得出新的状态。Redux 夸大“单一数据源”、“状态不可变”和“利用纯函数更新状态”这三个根本原则,因而具有可猜测、可测试、可调试等显著特点。
在 Android 开发中,尤其利用 Kotlin 语言,我们也可以应用 Redux 架构的头脑。由于 Android 应用通常涉及多组件复杂交互,状态管理和数据流问题不容忽视——此时引入 Redux 风格架构不失为一种有用的方案。常见的 Redux 实现库包罗 ReKotlin(雷同于 ReSwift)以及基于 Kotlin 协程和 Flow 的定制实现。本文将结合纯 Kotlin 的方式,讲解怎样构建一个基于 Redux 架构的 Android 应用。
2. Redux 焦点概念

在 Redux 架构中,重要包罗以下几个焦点组件:

[*] Store(存储器)
Store 是应用状态的容器,它维护了当前所有状态的快照。Store 内部只能通太过发一个 Action(动作)来改变状态,且这种改变必须是不可变的(immutable)。
[*] Action(动作)
Action 是描述状态变更的对象。它通常包罗一个类型(type)和可选的 payload,用来通报所需的数据。Action 必须是纯描述,不应包罗业务逻辑。
[*] Reducer(纯函数)
Reducer 是一个纯函数,吸收当前状态和分发进入的 Action,然后根据 Action 的类型,返回一个新的状态对象。每个 reducer 只负责处置惩罚应用状态的一个切片。
[*] Middleware(中央件)
Middleware 用于拦截和扩展 Action 的分发过程。通常用于处置惩罚异步操作、日记记录、错误捕获等。中央件答应你在 Action 到达 Reducer 前后插入自定义逻辑。
[*] View(视图层)
在 Android 中,View(通常是 Activity 或 Fragment)负责响应用户交互,并订阅 Store 厘革来调解界面。采用 Redux 架构时,界面层通常是无状态的,只依赖 Store 中数据。
3. Redux 在 Android 开发中的优势

将 Redux 头脑引入 Android 应用开发有以下优点:


[*] 状态会合管理
将全局状态同一存储在一个 Store 中,使得数据流向更加清晰,便于调试和测试。
[*] 可猜测的状态改变
由于状态改变只能通过纯函数(Reducer)举行计算,不会有副作用,所以状态变化具有可重复性和可猜测性。
[*] 进步代码可测试性
纯函数 Reducer 和明确的 Action 定义,便于对应用状态管理逻辑举行单位测试。
[*] 易于维护与扩展
除了分散在各个组件中的状态逻辑完全上移到 Store 和 Reducer 层中,代码变更时关注面较小。
[*] 支持日记和调试
借助 Redux DevTools 等工具,可以记录每一次 Action 和随后的 State 快照,使得调试和回滚更容易。
4. Kotlin 实现 Redux 的根本结构

在 Kotlin 中实现 Redux 架构,通常必要计划以下几个部分:
4.1 定义 State

State 将存储应用中所有必要管理的数据。一般会利用数据类来表示状态,而且要求状态不可变(利用 val 声明,大概借助 copy 方法举行局部更新)。
下面是一个简朴的示例,假设我们正在开发一个 Todo 应用,则全局状态可以包罗一个 Todo 列表和输入框内容:
data class Todo(val id: Int, val text: String, val completed: Boolean)

data class AppState(
    val todos: List<Todo> = emptyList(),
    val currentInput: String = ""
) 4.2 定义 Action

Action 用于描述用户交互大概体系事件,在 Redux 中一般定义为 sealed class 大概枚举。这里我们利用 sealed class 来列举所有行为,比方增加 Todo、删除 Todo、修改输入框内容以及切换 Todo 完成状态:
sealed class Action {
    data class AddTodo(val todo: Todo) : Action()
    data class RemoveTodo(val todoId: Int) : Action()
    data class UpdateInput(val input: String) : Action()
    data class ToggleTodo(val todoId: Int) : Action()
}
/*
sealed class 是一种特殊的类,它用于表示受限的类继承结构。 当你在 Redux 中使用 sealed class 时,通常是为了定义 Action 的类型。
sealed class 的概念:
受限的继承: sealed class 允许你定义一个类的继承结构,但限制了子类的数量和位置
所有子类必须在 sealed class 的声明中定义,或者在同一个文件中定义。
类型安全: sealed class 提供了类型安全,因为编译器知道所有可能的子类
这使得在使用 when 表达式时,编译器可以检查是否处理了所有可能的子类,从而避免了遗漏情况。
枚举类的增强版: 可以把 sealed class 看作是枚举类 (enum class) 的增强版
枚举类只能定义有限个实例,而 sealed class 可以定义有限个类。
*/ 4.3 定义 Reducer

Reducer 是一个严酷按照 Action 修改 State 的纯函数。注意:Reducer 不应包罗异步操作和副作用,所有副作用要放到 Middleware 中处置惩罚。下面是一个简朴的 Reducer 示例:
import java.util.UUID

// 定义一个数据类 Todo,表示一个待办事项
data class Todo(
    val id: String = UUID.randomUUID().toString(), // 待办事项的唯一 ID,默认为 UUID
    val text: String, // 待办事项的内容
    val completed: Boolean = false // 待办事项是否已完成,默认为 false
)

// 定义一个数据类 AppState,表示应用程序的状态
data class AppState(
    val todos: List<Todo> = emptyList(), // 待办事项列表,默认为空列表
    val currentInput: String = "" // 当前输入框中的内容,默认为空字符串
)

// 定义一个密封类 Action,表示所有可能的 Action 类型
sealed class Action {
    // 添加待办事项的 Action
    data class AddTodo(val todo: Todo) : Action()
    // 移除待办事项的 Action
    data class RemoveTodo(val todoId: String) : Action()
    // 更新输入框内容的 Action
    data class UpdateInput(val input: String) : Action()
    // 切换待办事项完成状态的 Action
    data class ToggleTodo(val todoId: String) : Action()
}

/**
* 应用的 Reducer 函数,用于根据 Action 更新应用的状态
*
* @param state 先前的应用状态
* @param action 发生的 Action
* @return 新的应用状态
*/
fun appReducer(state: AppState, action: Action): AppState {
    return when (action) {
      // 添加待办事项
      is Action.AddTodo -> {
            // 创建一个新的待办事项列表,将新添加的待办事项添加到列表末尾
            val newTodos = state.todos + action.todo
            // 返回一个新的 AppState 对象,其中 todos 属性更新为新的待办事项列表,currentInput 属性清空
            state.copy(todos = newTodos, currentInput = "")
      }
      // 移除待办事项
      is Action.RemoveTodo -> {
            // 创建一个新的待办事项列表,过滤掉要移除的待办事项
            val newTodos = state.todos.filter { it.id != action.todoId }
            // 返回一个新的 AppState 对象,其中 todos 属性更新为新的待办事项列表
            state.copy(todos = newTodos)
      }
      // 更新输入框内容
      is Action.UpdateInput -> {
            // 返回一个新的 AppState 对象,其中 currentInput 属性更新为新的输入内容
            state.copy(currentInput = action.input)
      }
      // 切换待办事项完成状态
      /*
      map 是一个集合转换方法,在 Kotlin (以及许多其他编程语言) 中
      用于将一个集合(例如 List、Set 等)中的每个元素转换为另一个元素,并返回一个包含转换后元素的新集合。
         */
      is Action.ToggleTodo -> {
            // 创建一个新的待办事项列表,遍历列表中的每个待办事项
            val newTodos = state.todos.map {
                // 如果当前待办事项的 ID 与要切换的待办事项的 ID 相同,则创建一个新的待办事项对象,切换其完成状态
                if (it.id == action.todoId) it.copy(completed = !it.completed)
                // 否则,保持原样
                else it
            }
            // 返回一个新的 AppState 对象,其中 todos 属性更新为新的待办事项列表
            state.copy(todos = newTodos)
      }
    }
} 通过这个 Reducer,每个 Action 都能返回对应的全新 State。由于 State 是不可变对象,因此可以包管每次状态改变都是明确可追踪的。
4.4 实现 Store

Store 负责维护 AppState,同一时刻只存在一份状态。Store 必要提供以下能力:


[*]生存当前 state
[*]提供 dispatch 方法,吸收 Action、调用 Reducer 更新 state
[*]答应组件订阅 state 改变事件
[*]支持 Middleware 机制(可选)
下面给出一个简朴的 Store 实现示例,基于 Kotlin 的高阶函数和观察者模式:

/**
* Redux Store 类,用于管理应用的状态
*
* @param state 初始应用状态
* @param reducer Reducer 函数,用于根据 Action 更新状态
* @param middleware 中间件列表,用于拦截和处理 Action
*/
class Store(
    private var state: AppState, // 存储当前应用状态,使用 var 允许状态更新
    private val reducer: (AppState, Action) -> AppState, // Reducer 函数,接收 state 和 action,返回新的 state
    private val middleware: List<(Store, Action, (Action) -> Unit) -> Unit> = emptyList() // 中间件列表,用于处理 action,默认为空列表
) {
    private val subscribers = mutableListOf<(AppState) -> Unit>() // 存储订阅者(监听器)的列表,当状态变化时通知他们

    /**
   * 获取当前状态
   *
   * @return 当前应用状态
   */
    fun getState(): AppState = state // 返回当前存储的应用状态

    /**
   * 订阅状态变化
   *
   * @param listener 状态变化监听器,接收新的 AppState 作为参数
   */
    fun subscribe(listener: (AppState) -> Unit) {
      subscribers.add(listener) // 将监听器添加到订阅者列表中
    }

    /**
   * 分发 Action,是修改状态的入口
   *
   * @param action 要分发的 Action
   */
    fun dispatch(action: Action) {
      // 中间件链处理
      if (middleware.isNotEmpty()) {
            // 如果存在中间件,则执行中间件链
            executeMiddleware(0, action) // 从第一个中间件开始执行
      } else {
            // 如果没有中间件,则直接调用内部 dispatch 方法更新状态
            internalDispatch(action)
      }
    }

    /**
   * 内部的分发 Action 方法,调用 Reducer 更新状态
   *
   * @param action 要分发的 Action
   */
    private fun internalDispatch(action: Action) {
      state = reducer(state, action) // 调用 Reducer 函数,根据 Action 更新状态
      notifySubscribers() // 通知所有订阅者状态已更新
    }

    /**
   * 递归执行中间件链
   *
   * @param index 当前中间件的索引
   * @param action 要分发的 Action
   */
    private fun executeMiddleware(index: Int, action: Action) {
      if (index < middleware.size) {
            // 如果当前索引小于中间件列表的大小,则执行当前中间件
            val currentMiddleware = middleware // 获取当前中间件
            currentMiddleware(this, action) { nextAction ->
                // 执行中间件,并将 Store、Action 和一个 next 函数传递给它
                // next 函数用于将 Action 传递给下一个中间件或最终的 internalDispatch 方法
                executeMiddleware(index + 1, nextAction) // 递归调用 executeMiddleware,执行下一个中间件
            }
      } else {
            // 如果所有中间件都已执行完毕,则调用内部 dispatch 方法更新状态
            internalDispatch(action)
      }
    }

    /**
   * 通知所有订阅者状态已更新
   */
    private fun notifySubscribers() {
      subscribers.forEach { it(state) } // 遍历订阅者列表,并调用每个订阅者的监听器函数,将新的状态传递给它
    }
} 在这个 Store 实现中,我们可以看到:


[*]通过 dispatch 方法,Action 先经过所有 middleware 处置惩罚后,再通报给 Reducer。
[*]状态厘革后,通过调用 notifySubscribers 告诉所有订阅组件更新 UI。
[*]实现中保持了状态不可变的特性。
4.5 实现 Middleware

Middleware 用于扩展 Redux 的功能,比如日记记录、异步处置惩罚等。下面举例阐明怎样编写一个简朴的日记记录中央件:
val loggerMiddleware: (Store, Action, (Action) -> Unit) -> Unit = { store, action, next ->
    println("Dispatching action: $action")
    next(action)
    println("New state: ${store.getState()}")
} 当在 Store 内部利用中央件时,可以将多个中央件依次注册,如下面的示例:
// 创建 Redux Store 实例
val store = Store(
    state = AppState(), // 初始应用状态,使用 AppState() 创建一个默认的 AppState 对象
    reducer = ::appReducer, // Reducer 函数,使用 ::appReducer 引用 appReducer 函数
    middleware = listOf(loggerMiddleware) // 中间件列表,包含 loggerMiddleware 中间件
)
这样,每一次 dispatch 操作都会先经过 loggerMiddleware 输出日记,然后再计算新的状态。
5. 将 Redux 架构与 Android UI 结合

在 Android 应用中,Activity 或 Fragment 通常充当 View 层,它们负责展示状态和捕获用户交互。结合 Redux 架构的关键在于:


[*]Activity/Fragment 订阅 Store 状态厘革,以便当状态更新时刷新 UI。
[*]用户在界面上举行操作后,调用 Store.dispatch() 发送 Action,触发状态更新。
下面提供一个简朴的示例,展示怎样在 Activity 中结合 Store 更新 UI:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    // 创建全局 Store 对象
    private val store = Store(
      state = AppState(),
      reducer = ::appReducer,
      middleware = listOf(loggerMiddleware)
    )

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      // 假设 activity_main.xml 包含一个 EditText 和一个 RecyclerView 用于展示 Todo 列表
      setContentView(R.layout.activity_main)

      // 订阅 Store 状态变化,更新 UI
      store.subscribe { state ->
            // 更新输入框文本(例如状态 currentInput)
            editTextInput.setText(state.currentInput)
            // 更新 RecyclerView,显示 todos 列表(这部分需配合 Adapter 实现)
            // adapter.submitList(state.todos)
      }

      // 用户输入时发送更新输入内容的 Action
      editTextInput.addTextChangedListener { text ->
            store.dispatch(Action.UpdateInput(text.toString()))
      }

      // 示例:点击按钮添加 Todo 项
      buttonAddTodo.setOnClickListener {
            val currentInput = store.getState().currentInput
            if (currentInput.isNotBlank()) {
                // 使用当前时间戳作为 id 简单示例
                val newTodo = Todo(id = currentInput.hashCode(), text = currentInput, completed = false)
                store.dispatch(Action.AddTodo(newTodo))
            }
      }
    }
} 在上面的示例中,你可以看到 UI 层通过监听 store.subscribe() 得到状态厘革,然后更新输入框、列表等视图;同时,用户在界面操作(比方编辑输入框、点击添加按钮)时,会发送对应的 Action 到 Store,从而触发状态更新。
6. Redux 异步操作与 Side Effects

在实际应用中,大部分操作可能涉及网络请求、数据库访问等异步操作。如果每个异步操作都直接放在 UI 层实现,容易导致代码杂乱。Redux 通过 Middleware 拦截 Action,将异步操作作为副作用处置惩罚,从而保持 Reducer 的纯粹性。
6.1 异步 Middleware 示例

假如我们必要在添加 Todo 项时,从远程服务器获取一些数据(比方数据校验或唯一 ID),这时可以编写一个异步中央件。下面给出一个模拟异步操作的例子:
import kotlinx.coroutines.*

val asyncMiddleware: (Store, Action, (Action) -> Unit) -> Unit = { store, action, next ->
    when (action) {
      is Action.AddTodo -> {
            // 模拟网络请求或异步操作
            GlobalScope.launch {
                delay(1000) // 模拟网络延迟
                // 异步操作结束后,继续分发 AddTodo Action
                next(action)
            }
      }
      else -> next(action)
    }
} 在这个中央件中,当吸收到 AddTodo Action 后,先启动协程举行模拟异步操作(耽误 1 秒),然后再调用 next(action) 将 Action 交给下一个中央件大概直接通报给 Reducer。注意:实际项目中应避免直接利用 GlobalScope,而是利用专门的 CoroutineScope。
6.2 注册多个 Middleware

在实际应用中,我们可以同时注册日记中央件、异步中央件等多个中央件。Store 将按照设置的顺序递归处置惩罚这些中央件,比方:
val store = Store(
    state = AppState(),
    reducer = ::appReducer,
    middleware = listOf(loggerMiddleware, asyncMiddleware)
) 这样,用户界面上每一次对状态的修改请求都将依次流过 loggerMiddleware 记录日记,然后经过 asyncMiddleware 处置惩罚异步操作,末了由 Reducer 计算出新的状态。
7. 实战示例与扩展

7.1 架构扩展

随着应用业务逻辑越来越复杂,Redux 架构也必要做一些扩展,比如:


[*] 拆分 State 与 Reducer:
将全局状态分拆为多个子状态,每个子状态由单独的 reducer 处置惩罚,再通过 combineReducers 归并成全局 Reducer。这样使各个模块解耦,便于维护。
[*] 增强 Middleware:
可以编写中央件处置惩罚错误、重试机制等。比方,当某个网络请求失败时,可以捕获失败 Action,并触发错误报告或重试逻辑。
[*] 状态持久化:
由于 Redux 状态是内存中的一份快照,可以通过序列化状态到本地存储(如 SharedPreferences 或数据库),实现应用重启后的状态恢复。
7.2 利用 Kotlin 协程与 Flow

随着 Kotlin Coroutines 和 Flow 的遍及,很多开发者开始用其来实现响应式的数据流。在 Redux 架构中,可以结合 Flow 来定义状态厘革流,从而使 UI 组件以流式数据订阅方式主动更新。比方:
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

class FlowStore(
    initialState: AppState,
    private val reducer: (AppState, Action) -> AppState
) {
    private val _stateFlow = MutableStateFlow(initialState)
    val stateFlow = _stateFlow.asStateFlow()

    fun dispatch(action: Action) {
      // 简单同步分发,没有中间件,直接生成新状态
      val newState = reducer(_stateFlow.value, action)
      _stateFlow.value = newState
    }
} 在 UI 层,你可以利用 Flow 订阅状态更新,利用 Jetpack 的 lifecycleScope 或 LiveData 转换实现数据绑定:
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

class MainActivityFlow : AppCompatActivity() {
    private val store = FlowStore(initialState = AppState(), reducer = ::appReducer)

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

      lifecycleScope.launch {
            store.stateFlow.collect { state ->
                // 根据新状态更新 UI,比如刷新列表、显示当前输入框内容等
                println("New state received: $state")
            }
      }

      // 例如,点击按钮时分发 Action
      buttonAddTodo.setOnClickListener {
            val currentInput = store.stateFlow.value.currentInput
            if (currentInput.isNotBlank()) {
                val newTodo = Todo(id = currentInput.hashCode(), text = currentInput, completed = false)
                store.dispatch(Action.AddTodo(newTodo))
            }
      }
    }
} 利用 Flow 的优势在于:


[*]代码更加简洁,符合响应式编程头脑;
[*]与 Android 生命周期友爱,主动在合适时机制止网络数据;
[*]极大降低手动管理订阅和通知机制的复杂度。
8. Redux 架构的优缺点

优点



[*]单一状态源: 更容易调试和实现时间旅行调试。
[*]状态不可变: 避免多线程状态写辩论,便于并发处置惩罚与测试。
[*]逻辑清晰: 将业务逻辑放在 Reducer 中,通过 Action 描述所有厘革,便于维护。
[*]中央件扩展性强: 方便添加日记、错误处置惩罚、异步处置惩罚等。
缺点



[*]模板代码较多: 对于简朴应用来说,Redux 的架构可能显得臃肿,增加了学习曲线。
[*]不善于局部状态: 对于只涉及局部 UI 状态(如控件焦点、动画状态等)的管理,Redux 可能显得过于笨重。
[*]调试异步逻辑需额外计划: 异步操作与副作用虽然可通过 Middleware 管理,但复杂场景下仍需谨慎计划和调试。
9. 实际项目中的应用与最佳实践

在实际 Android 项目中,通常会有这样一些需求:


[*]多个界面共享用户信息、购物车数据、设置设定等全局状态。
[*]步伐必要处置惩罚网络请求、用户输入、定时使命等各种异步事件。
[*]必要方便对应用状态举行测试、回溯和调试。
针对这些需求,应用 Redux 架构可以带来以下好处:


[*]状态会合而同一: 所有全局状态存储在 Store 中,而且只能通太过发 Action 修改,便于跟踪问题。
[*]进步单位测试覆盖率: Reducer 是纯函数,可以用单位测试验证各种 Action 的处置惩罚结果。
[*]易于开发调试工具: 比方可以将所有 Action 和状态变更记录下来,做时间旅行调试。
最佳实践建议:

[*]拆分 State 和 Roles: 计划得当的数据结构,将全局状态拆分为多个模块,每个模块拥有独立的 Reducer,末了在 Store 中整合
[*]明确 Action 定义: 定义清晰、可辨识的 Action 类型,避免多种行为混杂在一个 Action 中
[*]利用协程管理异步: 利用 Kotlin 协程和 Flow 结合中央件,实现异步操作和 API 请求,有用隔离副作用
[*]编写单位测试: 对 Reducer、Middleware 举行充实测试,保障业务逻辑的稳固性
[*]关注性能: 尽量淘汰不必要的状态更新和 UI 重绘,必要时利用 Diff 算法来优化 RecyclerView 或其他列表组件
10. 小结

通过本文的详细讲解,我们相识了 Redux 架构的根本概念及其在 Android 中的实现方法。简而言之,Redux 架构包罗 Store、Action、Reducer、Middleware 和 View 五大组件,借助这种架构,可以将应用的全局状态会合管理,并通过纯函数描述状态厘革,使应用更易于调试、测试与维护。同时,结合 Kotlin 协程与 Flow,可进一步进步开发效率与代码简洁度。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 241.Redux架构