仓库位置:
flowvideo/service/lib-component-redux核心文件就两个:
redux.kt和AbsStore.kt,共计不到 450 行代码。非常精简,很多地方设计的确实很巧妙,有很多值得学习的思想。
框架定位
lib-component-redux是一套自研的 Android 端 Redux 实现,为短视频、小视频流场景单独设计。保留了经典 Redux 的架构骨架(单向数据流),同时为适配 Android 生态做了调整。
一句话概括:Action 驱动,Middleware 拦截,Reducer 处理,State 存储,Subscription 通知。
核心思想:单向数据流
整个框架的灵魂就是一条单向的数据流动链路:
用户操作 / 网络回调 / 系统事件
│
▼
dispatch(Action) ← 入口:发出一个事件
│
▼
Middleware 链(责任链模式) ← 拦截/修改/记录/消费
│
▼
Reducer 列表(顺序执行) ← 处理事件,更新 State
│
▼
State 更新 ← 数据发生变化
│
▼
通知 Subscriptions ← 观察者收到通知,刷新界面数据只能沿这条链路单向流动,不能反向。 这是 Redux 最核心的约束,也是它能保持可预测性的关键。
六大角色详解
Action — 事件描述
// redux.kt 第19行
interface ActionAction 是一个空接口,仅作为标记,表示”发生了一件事”。任何事件都实现这个接口:
class LikeAction : Action // 用户点赞
class CommentAction(val text: String) : Action // 用户评论
class NetAction.Success(val data: Any) : Action // 网络请求成功设计要点:Action 只描述”发生了什么”,不包含任何处理逻辑。它是纯粹的数据载体。
两个特殊 Action
// redux.kt 第24行 — 不需要切线程的 Action(性能优化标记)
interface NoPostAction : Action
// redux.kt 第29行 — 事件已被消费(拦截标记)
object Consumer : Action- Consumer:单例对象,Middleware 返回它表示”这个事件我吃掉了,后面的 Reducer 不用看了”。
- NoPostAction:普通 Action 分发时可能通过 Handler.post 切到主线程,标记了 NoPostAction 的则直接执行,减少一次线程切换开销。
State — 状态容器
// redux.kt 第34行
interface State
// redux.kt 第39行
abstract class AbsState : State {
abstract fun getCurrentAction(): Action
open fun <T> put(key: String, data: T) {} // 存数据
open fun <T> put(data: T) {} // 存数据(key 自动用类名)
open fun <T> select(key: String): T? = null // 按 key 取数据
open fun <T> select(clazz: Class<T>): T? = null // 按类型取数据
open fun clear() {} // 清空
}AbsState 提供了put/select的 key-value 读写接口。具体实现是CommonState:
// CommonState.kt
data class CommonState(val data: MutableMap<String, Any?>) : AbsState() {
override fun <T> put(key: String, data: T) { this.data[key] = data }
override fun <T> select(key: String): T? { return cast(data[key]) }
override fun <T> select(clazz: Class<T>): T? { return select(clazz.name) }
}State 本质就是一个 MutableMap,按类名存取各种子状态对象。每个子状态对象内部通常包含MutableLiveData字段,供组件 observe。
Q:与经典 Redux 的差异
A:经典 Redux 要求 State 不可变(immutable),每次 Reducer 都返回一个全新的 State 对象。本框架为配合 Android LiveData,将 State 做成了可变的 Map。
源码注释明确说明了这一点(第48-49行):
“按Redux理念,此个方法只能在Reducer里调用,勿滥用"
"提供这个方法,主要是为适应LiveData需要绑定数据做的妥协”
这意味着数据变更的通知不依赖 Store 的 Subscription 机制,而是依赖 LiveData 自身的 observe 机制。
Reducer — 状态处理器
// redux.kt 第95行
interface Reducer<S : State> {
fun reduce(state: S, action: Action): S
// 签名含义:(旧状态, 事件) → 新状态
}Reducer 是 Redux 中唯一允许修改 State 的角色。每个 Reducer 负责一小块状态,收到 Action 后判断是否与自己相关:
- 相关 → 更新对应的子状态,返回 state
- 不相关 → 直接返回 state,不做任何操作
设计原则:Reducer 应该是纯函数,不做网络请求、不操作 UI、不产生副作用。
Middleware — 中间件(责任链模式)
// redux.kt 第230行
interface Middleware<S : State> {
fun apply(store: Store<S>, action: Action, next: Next<S>): Action
}
// redux.kt 第214行
interface Next<S : State> {
fun next(store: Store<S>, action: Action): Action
}Middleware 位于 dispatch 和 Reducer 之间,是一个可插拔的拦截层。每个 Middleware 可以:
- 放行:调
next.next(store, action)传给下一个 - 修改:调
next.next(store, 新action)替换 action 再传 - 拦截:直接返回
Consumer,后续 Middleware 和 Reducer 都不执行
适合处理:日志记录、埋点上报、网络检查、权限校验等副作用逻辑。
pipeline 责任链的构建
这块设计比较精巧,类似于 Spring Web Filter 链或者 interceptor
// AbsStore.kt 第139-148行
protected fun createNext(index: Int, middlewareList: MutableList<Middleware<S>>): Next<S> {
if (index == middlewareList.size) {
// 链的末端:什么都不做,原样返回 action
return object : Next<S> {
override fun next(store: Store<S>, action: Action): Action = action
}
}
// 递归:当前 middleware + 后续链
return NextMiddleware(middlewareList[index], createNext(index + 1, middlewareList))
}这里的末端不是业务方的末端,业务方的末端不知道后面还是否有其他的业务方 middleware 所以他也会next.next(),但是总要有一个终点,这个重点就是上面的代码。
链的末端相当于递归终点。能轮到终点返回,说明所有业务方都没有要返回的值。那这里就返回默认的 action 或者经过中间件处理过的 newAction。
这是一个递归构建的责任链。假设有 3 个 Middleware [A, B, C]:
createNext(0) = NextMiddleware(A, createNext(1))
= NextMiddleware(A, NextMiddleware(B, createNext(2)))
= NextMiddleware(A, NextMiddleware(B, NextMiddleware(C, 终止节点)))执行时:
A.apply(store, action, nextB)
│ A 调 next → B.apply(store, action, nextC)
│ │ B 调 next → C.apply(store, action, 终止)
│ │ │ C 调 next → 终止:return action
│ │ └─ C 拿到结果,可以后处理
│ └─ B 拿到结果,可以后处理
└─ A 拿到结果,可以后处理NextMiddleware 的桥接作用:
// redux.kt 第250-263行
class NextMiddleware<S : State>(
private val middleware: Middleware<S>,
private val next: Next<S>
) : Next<S> {
override fun next(store: Store<S>, action: Action): Action {
return middleware.apply(store, action, next)
}
}NextMiddleware 实现了 Next 接口,当上一个 Middleware 调next.next()时,它把调用转发给自己持有的 middleware 的apply()方法,同时把”更后面的链”作为 next 参数传入。这样每个 Middleware 都不需要知道链的全貌,只知道”自己”和”下一个”。
这和 OkHttp 的 Interceptor、Servlet 的 Filter 是完全相同的设计模式。
懒加载构建
// AbsStore.kt 第27行
private val middlewareChain by lazy { createNext(0, middlewares) }中间件链使用by lazy延迟构建,只在第一次 dispatch 时才构建,避免无谓的初始化开销。
Store — 中心枢纽
// redux.kt 第135行
interface Store<S : State> {
fun getState(): S // 获取当前状态
fun dispatch(action: Action) // 分发事件
fun subscribe(subscription: Subscription<S>): Unsubscribe // 订阅状态变化
fun <T> subscribe(clazz: Class<T>): T? // 订阅/获取子状态
fun addReducer(reducer: Reducer<S>) // 动态添加 Reducer
fun addMiddleware(middleware: Middleware<S>) // 动态添加 Middleware
}Store 是整个 Redux 的中心,持有并协调所有角色:
// AbsStore.kt 第20-27行
open class AbsStore<S: AbsState>(
protected var states: S, // 状态
protected val reducers: MutableList<Reducer<S>>, // Reducer 列表
protected val middlewares: MutableList<Middleware<S>> // Middleware 列表
) : Store<S> {
protected val subscriptions = arrayListOf<Subscription<S>>() // 订阅者列表
private val middlewareChain by lazy { createNext(0, middlewares) } // 中间件链
}dispatch 完整流程(核心中的核心)
// AbsStore.kt 第44-70行
override fun dispatch(action: Action) {
// Step 0: 给 ScheduleAction 打标记(追踪来源)
// 只给分级调试事件用
attachToken(action)
dispatchInternal(action)
}
private fun dispatchInternal(action: Action) {
// Step 1: 走中间件链
val newAction = applyMiddleware(action)
if (newAction is Consumer) {
// 被中间件拦截,流程结束
return
}
// Step 2: 走所有 Reducer
val newState = applyReducer(states, newAction)
if (newState === states) {
// State 对象没变,不通知(短路优化)
return
}
// Step 3: 更新 State,通知所有订阅者
states = newState
val size = subscriptions.size
for (index in 0 until size) {
subscriptions[index].subscribe(states)
}
}三个出口:
- Middleware 返回 Consumer → 结束(事件被消费)
- Reducer 执行后 State 没变 → 结束(无需通知)
- State 变了 → 通知所有订阅者
subscribe 的两种模式
模式一:订阅整体状态变化(传统 Redux 方式)
// AbsStore.kt 第94-101行
override fun subscribe(subscription: Subscription<S>): Unsubscribe {
subscriptions.add(subscription)
return object : Unsubscribe {
override fun unsubscribe() { subscriptions.remove(subscription) }
}
}注册一个 Subscription,每次 dispatch 导致 State 变化后被通知。返回 Unsubscribe 对象用于取消订阅。
// AbsStore.kt 第111-122行
override fun subscribe(subscription: Subscription<S>, needLastData: Boolean): Unsubscribe {
subscriptions.add(subscription)
//
if (needLastData) {
// handler.post {
subscription.subscribe(states)
// }
}
return object : Unsubscribe {
override fun unsubscribe() {
subscriptions.remove(subscription)
}
}
}订阅以后立即 dispatch 一个 state,避免拿不到最新数据,界面空白的问题。
模式二:获取子状态对象(配合 LiveData,框架特有)
// AbsStore.kt 第161-172行
override fun <T> subscribe(key: String, clazz: Class<T>): T? {
var data = states.select<T>(key)
if (data == null) {
data = createModel(clazz) // 不存在就反射创建
states.put(key, data) // 存入 State
}
return data
}不是”通知”机制,而是”获取或创建”语义。组件拿到子状态对象(内含 LiveData),再通过 LiveData 的 observe 监听数据变化。这是本框架与经典 Redux 最大的区别。
Subscription — 状态变化订阅
// redux.kt 第111行
interface Subscription<S : State> {
fun subscribe(state: S)
}
// redux.kt 第124行
interface Unsubscribe {
fun unsubscribe()
}Subscription 是观察者,Unsubscribe 用于取消订阅。dispatch 导致 State 变化后,Store 遍历所有 Subscription 调用subscribe(newState)。
扩展机制
4.1 ActionTransferInterceptor — 跨层 Action 拦截器
// api/IActionTransferInterceptor.kt
interface IActionTransferInterceptor {
fun accept(action: Action): Boolean // parent 的 action 能否传给 child
fun deliver(action: Action): Boolean // child 的 action 能否传给 parent
}
// ActionTransferInterceptor.kt — 默认实现:全部拦截
open class ActionTransferInterceptor : IActionTransferInterceptor {
override fun accept(action: Action): Boolean = false
override fun deliver(action: Action): Boolean = false
}解决的问题:本框架支持嵌套(一级 Store 管列表,二级 Store 管单个 Item)。默认情况下,每一级的 action 只在本级流转,不会跨层。业务需要跨层通信时,子类重写 accept/deliver 来选择性放行特定 Action。
设计思想:默认隔离,显式放行。遵循最小权限原则,避免 action 意外泄露到其他层级。
4.2 ScheduleAction — 分级加载调度
// ext/ScheduleAction.kt
sealed class ScheduleAction : Action {
var token: Store<*>? = null // 来源标识
class ArchInitReady : ScheduleAction() // 框架初始化完成
class PrimaryTaskReady : ScheduleAction() // 核心组件就绪
class SecondaryTaskReady : ScheduleAction() // 次要组件可加载
class CustomTaskReady(val level: String) : ScheduleAction() // 自定义时机
}解决的问题:一个视频 Item 有几十个子组件,同时创建会导致首屏卡顿。ScheduleAction 把加载过程分成多个阶段:
阶段 1: ArchInitReady → 框架就绪,创建根 View
阶段 2: PrimaryTaskReady → 核心组件(播放器、封面)就绪
阶段 3: SecondaryTaskReady → 加载次要组件(评论、点赞、作者...)配合ScheduleConfig,还支持条件触发和超时兜底:
data class ScheduleConfig(
val conditionAction: Class<out Action>, // 触发条件
val triggerDelayTime: ((Store<*>) -> Long) = { 0L } // 超时自动触发
)设计思想:将”一次性全部加载”拆分为”信号驱动的分级加载”,用 Redux 自身的 dispatch 机制来协调组件的创建时序。
4.3 Unicast — 单播优化(性能优化)
解决的问题:默认 dispatch 时,所有 Middleware 和 Reducer 都会被遍历。当数量达到 60+ 时,每次 dispatch 都有大量无效遍历。
解决方案:通过注解 + APT(编译时注解处理),预先建立 Action → Middleware/Reducer 的映射表。dispatch 时查表,只执行相关的处理器。
广播模式(默认):
dispatch(LikeAction) → 遍历 60 个 Middleware → 遍历 60 个 Reducer
单播模式(优化):
dispatch(LikeAction) → 查表 → 只执行 PraiseMiddleware → 只执行 PraiseReducer核心查询接口:
// UnicastActionMappingManager.kt
fun isMatchActionAndMiddleware(action, middleware): Boolean // Action 与 Middleware 是否匹配
fun isMatchActionAndReducer(action, reducer): Boolean // Action 与 Reducer 是否匹配映射表由 APT 在编译期自动生成(每个 Module 生成一份 IUnicastActionMappingTable),运行时通过 UnicastActionMappingManager 合并为一张大表。
AllUnicastAction 是特殊标记:标注了它的 Middleware/Reducer 会匹配所有单播 Action。
4.4 辅助接口
ReduxView:经典 Redux 的 View 层定义
interface ReduxView<S : State> {
fun render(state: S) // 给你 State,你负责渲染
}因为本框架主要通过 LiveData 驱动 UI 更新,此接口使用较少,保留作为设计完整性。
StoreProvider:Store 获取接口
interface StoreProvider<S : State> {
fun getStore(): Store<S>
}持有 Store 的类实现此接口,方便外部获取 Store 引用。
设计精髓与值得学习之处
极简的核心抽象
整个 Redux 核心只有 6 个接口/类:Action、State、Reducer、Middleware、Store、Subscription。每个接口只有 1-2 个方法。用最少的抽象表达了完整的单向数据流模型。
Middleware pipeline 责任链的递归构建
protected fun createNext(index: Int, middlewareList: MutableList<Middleware<S>>): Next<S> {
if (index == middlewareList.size) {
return object : Next<S> {
override fun next(store: Store<S>, action: Action): Action = action
}
}
return NextMiddleware(middlewareList[index], createNext(index + 1, middlewareList))
}用递归将一个 List 转化为链式结构,每个节点只知道”自己”和”下一个”。这与 OkHttp Interceptor 链、Netty ChannelPipeline 是相同的设计,是后端/客户端通用的经典模式。
核心价值:调用者(Middleware)不需要知道链的全貌,只需要决定”要不要传给下一个”。
Consumer 短路机制
val newAction = applyMiddleware(action)
if (newAction is Consumer) {
return // 被消费就结束,Reducer 不执行
}用一个单例对象作为”终止信号”,让 Middleware 有能力提前结束整个 dispatch 流程。简洁而强大。
三重短路优化
dispatch 流程有三个提前退出的点:
// 短路 1:Middleware 消费了
if (newAction is Consumer) return
// 短路 2:Reducer 处理后 State 对象没变
if (newState === states) return
// 短路 3:没有订阅者(size 为 0,循环不执行)
for (index in 0 until size) { ... }每一步都在尽早退出,避免不必要的后续操作。
务实的 LiveData 融合
经典 Redux 的 State 不可变,每次产生新对象,通过对象引用比较来判断变化。本框架放弃了这一约束,将 State 做成可变 Map,内部用 LiveData 驱动通知。
这个”妥协”带来了三个实际好处:
- 精确通知:组件只 observe 自己关心的 LiveData 字段,不会被无关状态变化打扰
- 生命周期安全:LiveData 天然感知 Android 生命周期,页面销毁时自动取消订阅
- 零额外开销:不用每次 dispatch 都创建新 State 对象
动态可扩展
fun addReducer(reducer: Reducer<S>)
fun addMiddleware(middleware: Middleware<S>)Store 支持运行时动态添加 Reducer 和 Middleware,为组件懒加载场景提供支持——组件在被加载时才注册自己的 Reducer。
单播优化的编译期 + 运行时协作
通过 APT 在编译期生成映射表,运行时查表代替遍历。把计算从运行时搬到了编译时,是 Android 性能优化的经典思路(类似 Dagger、Room 等框架的做法)。
整体架构图
┌─────────────────────────────────────────────────────────┐
│ AbsStore │
│ │
│ dispatch(Action) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Middleware Chain (责任链模式) │ │
│ │ ┌────┐ ┌────┐ ┌────┐ ┌───┐ │ │
│ │ │ M1 │──▶│ M2 │──▶│ M3 │──▶│终止│ │ │
│ │ └────┘ └────┘ └────┘ └───┘ │ │
│ │ 可返回 Consumer 中断链路 │ │
│ └─────────────────────────────────────┘ │
│ │ (如果未被 Consumer 中断) │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Reducer List (顺序执行) │ │
│ │ R1 → R2 → R3 → ... → Rn │ │
│ │ 每个 Reducer 只处理自己关心的 Action │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ State (CommonState = MutableMap) │ │
│ │ ┌────────────────────────────────┐ │ │
│ │ │ "PraiseState" → {count: LiveData}│ │ │
│ │ │ "CommentState" → {list: LiveData}│ │ │
│ │ │ "PlayerState" → {url: LiveData} │ │ │
│ │ └────────────────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ├──▶ Subscription 通知(传统 Redux 方式) │
│ └──▶ LiveData.observe 通知(Android 方式) │
│ │
│ ┌─────────── 扩展能力 ─────────────────┐ │
│ │ ActionTransferInterceptor 跨层隔离 │ │
│ │ ScheduleAction 分级加载 │ │
│ │ Unicast 单播优化 │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘文件清单
lib-component-redux/
│
├── redux.kt 核心定义:Action, State, Reducer,
│ Middleware, Store, Subscription,
│ Next, NextMiddleware, Consumer
│
├── AbsStore.kt Store 实现:dispatch 流程,
│ Middleware 链构建, subscribe 机制
│
├── ActionTransferInterceptor.kt 跨层 Action 拦截默认实现
│
├── api/
│ └── IActionTransferInterceptor.kt 跨层拦截接口定义
│
├── ext/
│ ├── ReduxView.kt View 渲染接口(经典 Redux 保留)
│ ├── ScheduleAction.kt 懒加载调度信号 + 扩展函数
│ └── StoreProvider.kt Store 获取接口
│
└── unicast/
├── AllUnicastAction.kt "匹配所有单播"的标记 Action
├── IUnicastActionMappingTable.kt APT 生成的映射表接口
└── UnicastActionMappingManager.kt 映射表管理器(查表优化 dispatch)总结
| 维度 | 设计选择**** |
|---|---|
| 架构模式 | Redux 单向数据流 |
| 中间件机制 | 责任链模式,递归构建,支持拦截/修改/消费 |
| 状态管理 | 可变 Map + LiveData(务实妥协) |
| 通知机制 | 双通道:Subscription(传统)+ LiveData.observe(Android 原生) |
| 跨层通信 | 默认隔离,ActionTransferInterceptor 显式放行 |
| 性能优化 | 三重短路 + Middleware 懒加载 + 单播映射表 |
| 扩展性 | 运行时动态添加 Reducer/Middleware |
| 代码量 | 核心不到 450 行,小而精 |
Redux 只管”单个 Store 内部”的数据流——Action → Middleware → Reducer → State → Subscription,这条链路是 Redux 的范畴。
至于跨层通信(一级 Store ↔ 二级 Store)是 ComponentArchManager 这一层搭建的,它只是借用了 Redux 的 State 容器来存放 InterceptorState,实际通信靠的是 LiveData。
Redux 本身对”多个 Store 之间怎么通信”完全不知情,也不关心。这部分是上层架构的事。所以到这里,Redux 部分基本讲透了。完结撒花
更新: 2026-03-09 20:31:28
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/nqzsn5invhez8lcz