仓库位置:flowvideo/service/lib-component-redux

核心文件就两个:redux.ktAbsStore.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 Action

Action 是一个空接口,仅作为标记,表示”发生了一件事”。任何事件都实现这个接口:

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 可以:

  1. 放行:调next.next(store, action)传给下一个
  2. 修改:调next.next(store, 新action)替换 action 再传
  3. 拦截:直接返回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)
    }
}

三个出口:

  1. Middleware 返回 Consumer → 结束(事件被消费)
  2. Reducer 执行后 State 没变 → 结束(无需通知)
  3. 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 驱动通知。

这个”妥协”带来了三个实际好处

  1. 精确通知:组件只 observe 自己关心的 LiveData 字段,不会被无关状态变化打扰
  2. 生命周期安全:LiveData 天然感知 Android 生命周期,页面销毁时自动取消订阅
  3. 零额外开销:不用每次 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


相关笔记