Compose 中显示数据,要将其存储在变量中,并用 mutableStateOf 包裹,以便自动监听
在 Compose 中显示数据,通常我们要将其存储在变量中,并用 mutableStateOf
包裹,以便实现自动监听与更新。这一步有下列三种写法:
val name = mutableStateOf("Bob") // 1
val name by mutableStateOf("Bob") // 2
val name by remember { mutableStateOf("Bob") } // 3
Compose 过程
在深挖之前,有必要先了解 Compose 从构建到显示的完整过程。
Compose 分为三个步骤:组合 (compose)、布局、绘制。前者是 Compose 独有的,后面两个与传统 View 类似。所谓「组合」,就是根据我们写的代码创建出实际的界面,组合的结果称为 Composition
。
插一句题外话,Compose 函数虽然看起来很像是创建一个对象,例如 Text(text = name.value)
很像创建一个 Text
对象,但实际上不是这样。跟踪源码可以发现,这只不过是一个普通的函数,它没有返回值,因此类似 Text().text = name.value
的写法完全错误 ❌
这与传统 View 中 TextView().text = "xxx"
不是一回事。
—— 所以需要「组合」
如果还是无法理解,可以将「组合」类比传统 View 中 inflat
,解析 xml 文件创建实际的对象。只不过曾经我们可以绕过 xml 手动创建 view,而现在不行了。
MutableState
最基本的用例大概是这样:
val name = mutableStateOf("Bob")
setContent { Column { Text(text = name.value)
Button(onClick = { name.value = "2" })
{
Text(text = "Change Name")
}
}
}
定义了一个 Text 和 Button,点击按钮,显示的文本就会改变。
跟踪源码轻易看出,mutableStateOf()
就是创建一个 ParcelableSnapshotMutableState
对象然后返回它。ParcelableSnapshotMutableState
本身并没有什么有价值的东西,主要是对 parcelable 的实现便于进程通信。真正储存数据的是它的父类 SnapshotMutableStateImpl
:
internal open class SnapshotMutableStateImpl<T>(
value: T,
override val policy: SnapshotMutationPolicy<T>
) : StateObject, SnapshotMutableState<T> {
override var value: T
get() = next.readable(this).value
set(value) = next.withCurrent {
if (!policy.equivalent(it.value, value)) {
next.overwritable(this, it) { this.value = value }
}
}
private var next: StateStateRecord<T> = StateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
// 省略其他的 ...
}
显然 value
就是保存实际数据的东西,可惜它依然不是最终实现。继续探索便遇到一个关键类 StateRecord
(StateStateRecord
是它的子类),要搞清楚它,首先要看看 SnapshotMutableStateImpl
所实现的两个接口。现在我们已经知道 mutableStateOf()
可以返回一个容器对象,它能够被订阅并且在内部值改变时通知订阅者,以此实现 UI 自动刷新。「被订阅」这种特性其实是 StateObject
接口提供的,很反直觉。
再次套娃🪆,StateObject
本身又是一个容器,以链表的形式存储着一串 StateRecord
⬅️ 这个真正保存了数据。为什么是一串?因为它不仅保存了变量的最新值,还保留了曾经的值,以便实现「撤销」等功能。既然是链表,那肯定存在第一个节点,就是 StateObject.firstStateRecord
,后继节点就是 StateRecord.next
。注意这个 next 和 SnapshotMutableStateImpl.next
无关,恰好重名而已。事实上 SnapshotMutableStateImpl
里有一个链表保存着所有值(实现了 StateObject
接口呀),它的首个节点是 SnapshotMutableStateImpl.next
。SnapshotMutableState
小结:StateObject
以链表形式真正储存了数据,StateRecord
是链表的节点,SnapshotMutableStateImpl
是一个实现。
作者:晨鹤
链接:https://juejin.cn/post/7057852733012836388
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。