前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >安卓软件开发:基于Jetpack Compose实现Redux架构

安卓软件开发:基于Jetpack Compose实现Redux架构

原创
作者头像
Nimyears
发布2025-02-02 22:11:41
发布2025-02-02 22:11:41
710
举报

引言

本文探讨基于Kotlin语言实现Redux架构,结合Jetpack Compose构建可预测的状态管理。


一、Redux架构的核心机制

1.1 状态管理困境

传统Android开发常面临以下痛点:

  • 状态分散在多个ViewModel/Repository中
  • 难追踪状态变更路径
  • 异步操作导致状态条件
  • 跨组件状态同步困难

Redux是单向数据流和单一数据源原则。

1.2 架构核心要素

组件

职责

Kotlin实现形式

Store

全局状态容器

StateFlow管理对象

Action

状态变更意图描述

Sealed Class体系

Reducer

纯函数处理状态转换

高阶函数

二. 理解概念

Redux 有几个核心概念,需要先了解它们在做什么:

1. Store(仓库)

• 整个应用只有一个Store。Store是一个数据仓库,保存着当前应用的所有状态 (State)。

• 你不能直接修改Store,所有想修改状态的地方,都要通过派发(dispatch)一个Action。

2. State(状态) • 你应用中所需维护的所有数据都放在这里,比如登录状态、用户信息、界面上的各种选项状态等。

• State 只是一个普通的 Kotlin 数据类或多个数据类的组合,里面没有任何业务逻辑,只有数据。

3. Action(动作)

  • Action 就是一个普通数据类(或枚举类)实例,用来描述“要做什么”。
  • 比如:LoginAction.Login(username, password)、LoginAction.Logout。
  • 只有给 Store dispatch 相应的 Action,才能触发对 State 的修改。 4. Reducer(处理者/纯函数)

• Reducer 是一个纯函数(输入 + 输出),定义了“当有 Action 派发进来时,如何根据 Action 来生成新的 State”。

• 由于Redux强调“不可变数据”,因此Reducer不会直接修改旧的State,而是创建返回一个新的 State 对象。

这五个核心概念就像是一个流水线:

UI -> dispatch(Action) -> Reducer -> 新 State -> UI 自动更新

三、基础架构实现

展示如何创建 Store、定义 State、Action、 Reducer,模拟一个登录。

2.1 定义 State

代码语言:kotlin
复制
// AppState.kt
data class AppState(
    val loginState: LoginState = LoginState()
)

data class LoginState(
    val isLoading: Boolean = false,
    val isLoggedIn: Boolean = false,
    val username: String = "",
    val errorMsg: String? = null
)

2.2 定义 Action

代码语言:kotlin
复制
// LoginAction.kt
sealed class LoginAction {
    data class Login(val username: String, val password: String) : LoginAction()
    object LoginSuccess : LoginAction()
    data class LoginFailure(val errorMsg: String) : LoginAction()
    object Logout : LoginAction()
}

2.3 定义 Reducer

reducer 最终会输入当前的状态 AppState 和一个 Action,然后返回新的 AppState。

代码语言:kotlin
复制
// AppReducer.kt
fun appReducer(state: AppState, action: Any): AppState {
    return state.copy(
        loginState = loginReducer(state.loginState, action)
    )
}
fun loginReducer(state: LoginState, action: Any): LoginState {
    when (action) {
        is LoginAction.Login -> {
            return state.copy(
                isLoading = true,
                errorMsg = null,
                username = action.username
) }
        is LoginAction.LoginSuccess -> {
            return state.copy(
                isLoading = false,
                isLoggedIn = true
            )
        }
        is LoginAction.LoginFailure -> {
            return state.copy(
                isLoading = false,
                errorMsg = action.errorMsg
) }
        is LoginAction.Logout -> {
            return state.copy(
                isLoggedIn = false,
                username = "",
                errorMsg = null
) }
        else -> return state
    }
}

2.4 定义 Store

代码语言:kotlin
复制

class Store(
    initialState: AppState,
    private val reducer: (AppState, Any) -> AppState
){
private val _state = MutableStateFlow(initialState) val state: StateFlow<AppState> = _state.asStateFlow()
    fun dispatch(action: Any) {
        val currentState = _state.value
val newState = reducer(currentState, action)
        _state.value = newState
    }
}
    initialState = AppState(),
    reducer = ::appReducer
)

2.5 定义 ViewModel

代码语言:kotlin
复制
class LoginViewModel : ViewModel() {

    //  Store 通过依赖注入或直接引用全局单例
    private val store = Store(
        initialState = AppState(),
        reducer = ::appReducer
    )

    // 将状态暴露为 StateFlow 给 UI层
    val loginState: StateFlow<LoginState> = store.state.map { it.loginState }
        .stateIn(viewModelScope, SharingStarted.Eagerly, store.state.value.loginState)

    fun login(username: String, password: String) {
        // 先dispatch:告诉redux正在登陆
        store.dispatch(LoginAction.Login(username, password))

        viewModelScope.launch {
            // 模拟网络延迟
            delay(2000)

            // 简单判断下密码:假设密码是 "123456" 则成功,否则失败
            if (password == "123456") {
                store.dispatch(LoginAction.LoginSuccess)
            } else {
                store.dispatch(LoginAction.LoginFailure("密码错误"))
            }
        }
    }

    fun logout() {
        store.dispatch(LoginAction.Logout)
    }
}
 

五、在 Jetpack Compose(Material 3)中使用

有了LoginViewModel,就能在 Composable 中去订阅 loginState 根据最新状态动态渲染界面描述为

1. 显示两个文本输入框(用户名、密码)

2. 显示一个登录按钮

3. 根据状态显示加载中、错误信息、或登录成功

代码语言:kotlin
复制
@Composable
fun LoginScreen(
    viewModel: LoginViewModel = viewModel() // 通过DI传入
) {
    val loginState by viewModel.loginState.collectAsState()

    // 登录表单本地State(也可以放到Redux管理,但通常只放必要的全局数据到Redux)
    var username by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }

    // 注意:登陆后的 username 也可能和本地输入框不一致
    // 如果希望登录前和登录成功后都绑定同一个变量,可以只用 Redux State 中的 username。

    Scaffold(
        topBar = {
            TopAppBar(title = { Text("Redux + Nim Login") })
        }
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
                .fillMaxSize()
                .padding(16.dp),
            verticalArrangement = Arrangement.Center
        ) {
            // 显示登录状态
            Text(text = "Logged In: ${loginState.isLoggedIn}")

            OutlinedTextField(
                value = username,
                onValueChange = { username = it },
                label = { Text("用户名") },
                modifier = Modifier.fillMaxWidth()
            )

            Spacer(modifier = Modifier.height(8.dp))

            OutlinedTextField(
                value = password,
                onValueChange = { password = it },
                label = { Text("密码") },
                modifier = Modifier.fillMaxWidth(),
                visualTransformation = PasswordVisualTransformation()
            )

            Spacer(modifier = Modifier.height(16.dp))

            // 显示错误
            loginState.errorMsg?.let { error ->
                Text(
                    text = error,
                    color = Color.Red,
                    modifier = Modifier.padding(bottom = 8.dp)
                )
            }

            // 显示加载进度
            if (loginState.isLoading) {
                CircularProgressIndicator()
            } else {
                // 如果没有在加载,就显示登录按钮或退出按钮
                if (!loginState.isLoggedIn) {
                    Button(
                        onClick = {
                            viewModel.login(username, password)
                        },
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        Text("登录")
                    }
                } else {
                    // 已经登录了
                    Button(
                        onClick = {
                            viewModel.logout()
                        },
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        Text("退出登录")
                    }
                }
            }
        }
    }
}

运行流程梳理

1. 用户输入用户名和密码;

2. 点击“登录”后,会调用 viewModel.login(username, password);

3. viewModel.login() 里先 dispatch(LoginAction.Login(username, password)),Redux State 立刻变更:isLoading = true;UI 立即显示“加载中”状态;

4. delay(2秒) 后,根据密码是否为 "123456" 来决定派发 LoginSuccess 或 LoginFailure("密码错误");

5. UI 收到新 State,再次重新组合:

• 成功则显示“已登录”以及“退出登录”按钮;

• 失败则显示错误信息。

四、总结

优点

适用大型项目实施

• 所有状态都在可控范围内,修改 State 的途径统一且可追溯。

• 对复杂场景非常有用,便于日志记录、调试、或者做时间回溯。

缺点

不适用小型app实施

• Redux 会增加代码量和复杂度。

• Action、Reducer、Store 的模板化代码较多。

祝你学习上手 Redux + Compose 顺利,新年快乐)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 一、Redux架构的核心机制
    • 1.1 状态管理困境
    • 1.2 架构核心要素
  • 二. 理解概念
    • 这五个核心概念就像是一个流水线:
  • 三、基础架构实现
    • 2.1 定义 State
    • 2.2 定义 Action
    • 2.3 定义 Reducer
    • 2.4 定义 Store
    • 2.5 定义 ViewModel
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档