原文链接: https://code.cash.app/the-state-of-managing-state-with-compose
作者:JakeWharton
Cash App 从五年前开始将 Android 客户端 UI 的 render 和 presenter 分离到不同类里。过去的几年里我们重度使用 RxJava,对这个过程帮助很大。我曾做过一个分享,使用 RxJava 进行状态管理的现状,在那里我重新定义了 RxJava 的(反)模式以适用于我们想要的架构。
尽管干净的分层改善了测试性,但是管理状态的代码变得不够清晰,难以理解。业务逻辑被淹没在海量的 RxJava 操作符的组合与嵌套之中。我们尝试过几种 redux 类的库,甚至还自己实现了一个,想避免这种情况,但效果都不够理想。
没多久,我离开了 Cash App 去了 Google,开发了 SdkSearch 继续测试类似的架构。在此期间从 RxJava 迁移到 kotlinx.coroutines 的 Channel
解锁了多平台支持。却没有迁移到 Flow
,迁移本身并没有难度,问题不再这,而是生成状态的逻辑应该如何定义。我对我想要的方式有一个画面,但是不能避免陷入大量 API 之中,简单的表达出来。
回到 Cash App 之后,我仍然不认为现有 Flow
和协程的任何形式是一个足够好的解决方案。我开始尝试使用 Compose 去构建命令行 UI
和多平台 UI binding,思考适用于所有基于 Compose 项目的架构应扮演的角色。 Matt Precious 今年早些时候做了一个 Compose Web 项目,我们基于此反复迭代一个典型的 presenter/render 分离在 Compose 中应该是什么样子的。我们搞了点好玩的东西,不过它依赖了 Compose,所以只能用于 Compose UI 和 Compose Web。
或者说,可以?
Enter Molecule
Molecule 基于一个想法,Compose 只用于产生状态,而不是去渲染的 UI。
首先,这个看起来怎么样?这个很重要!
1 |
|
这就是一个普通的 composable 函数,返回一个状态,可以用于绑定到 Compose UI 的 text 属性上。
Molecule 可以让你以 composable 的形式,将其转换成 StateFlow<Int>
在其他地方使用。当第一个值初始化后, Compose 同步 recompose,所有后续的值都会 emit 到 StateFlow。
在 presenter,我们可以在 Molecule 中使用可组合的函数模式:
1 |
|
Compose 为我们开启了一种新的方式,去实现逻辑。编译器插件的使用,以一种原始协程库 API 无法实现的方式解锁了这门语言。关于 Compose 可以看官方文档。
现在,我们可以不必再写一堆 RxJava 或是 Flow 操作符,我就写 if/else
,for
循环,不必再用 publish
/filter
/merge
组合,直接用 when
语句,还能有 exhaustiveness 语法检查。
Compose 提供的工具,比如 remember
,state,derived state,effects 等等我们都可以继续用。Molecule 的示例项目中有稍微复杂的使用场景。
1 |
|
在 Cash App 的实际使用中是基于类的,可以标准化 presenter API,仍然可以享受编译时安全的依赖注入。
1 | class CounterPresenter constructor( |
我们使用 Molecule 已经有 5 个月了。它还没有为正式版做好准备,我们没有 100% 确定应该以怎样的形式使用 Compose API。我们在这周公布了这个库,集成到 Cash App 进行更多的测试。邀请你来加入我们,体验这个库。
这会是我们管理状态的最终形式吗?未必。但这是我们的下一个方案,也可能是你的~