今天大概会聊到的范围
- Snapshot system
上一篇有提到,State 改变时会触发 recomposition。视这个行为是一种定义。但是为什麽?这怎麽发生的呢?
今天要介绍的是 Compose 中另一个与 State 息息相关的概念 - Snapshot
Snapshot 可以想像是当下所有 State 的存档、快照。就像游戏存档或是电脑备份一样,当下是什麽就是什麽,全部存起来。
提醒:Snapshot 通常不会用在 Compose 的开发中,主要是在用於 Compose 的内部运作。
让我们看看 Snapshot 实际上怎麽使用的:
fun main() {
// 1.
val data = mutableStateOf("")
// 2.
data.value = "Foo"
println("2: ${data.value}")
// 3.
val snap = Snapshot.takeSnapshot()
// 4.
data.value = "Bar"
println("4: ${data.value}")
// 5.
snap.enter {
println("5: ${data.value}")
}
// 6.
println("6: ${data.value}")
// 7.
snap.dispose()
}
Snapshot 和 State 的运作,是不一定要在 composable function 中执行的
Output:
2: Foo
4: Bar
5: Foo
6: Bar
mutableStateOf
建立 State。Snapshot.enter
可以 access 当时拍的 snapshot,在这个 scope 内,我们可以取得当时的 statedispose
释放掉资源Snapshot 的概念就是这麽单纯:保留某一个时刻的所有 State。
在 enter snapshot 後,如果我们还想对 State 进行编辑,将会得到错误。
snap.enter {
data.value = "Buzz" // << --- Error
}
在 snapshot 中,State 是唯读无法编辑的。若希望编辑,我们会需要 MutableSnapshot
fun main() {
val data = mutableStateOf("")
data.value = "Foo"
// 1.
val mutableSnap = Snapshot.takeMutableSnapshot()
// 2.
mutableSnap.enter {
println("2: ${data.value}")
data.value = "Buzz"
println("3: ${data.value}")
}
println("4: ${data.value}")
// 5.
mutableSnap.enter {
println("5: ${data.value}")
}
// 6.
mutableSnap.apply()
println("6: ${data.value}")
mutableSnap.dispose()
}
Output:
2: Foo
3: Buzz
4: Foo
5: Buzz
6: Buzz
takeMutableSnapshot
来建立 MutableSnapshot
MutableSnapshot
中,我们一样可以拿到 takeSnapshot
时的资料 "Foo"MutableSnapshot
中,我们可以对 Sate 进行编辑MutableSnapshot.apply()
我们可以将 snapshot 的值实际赋予到 State 身上takeMutableSnapshot
和 apply
之间,有人直接对 State set value,apply
也不会发生作用fun takeMutableSnapshot(
readObserver: ((Any) -> Unit)? = null,
writeObserver: ((Any) -> Unit)? = null
): MutableSnapshot =
(currentSnapshot() as? MutableSnapshot)?.takeNestedMutableSnapshot(
readObserver,
writeObserver
) ?: error("Cannot create a mutable snapshot of an read-only snapshot")
其实仔细看看 takeMutableSnapshot
的 signature,会发现其实他还有吃两个参数:read/write 的 Observer。
实际上我们可以透过这两个角色去监听到资料被写入、被读取的时机,并且取得即将被异动的 State 与其资料。
fun main() {
val data = mutableStateOf("")
data.value = "Foo"
val readObserver = { readState: Any -> if (readState == data) println("READ")}
val writeObserver = { readState: Any -> if (readState == data) println("WRITE")}
val mutableSnap = Snapshot.takeMutableSnapshot(readObserver, writeObserver)
mutableSnap.enter {
println("1: ${data.value}")
data.value = "Buzz"
println("2: ${data.value}")
}
println("3: ${data.value}")
mutableSnap.apply()
println("4: ${data.value}")
mutableSnap.dispose()
}
READ
1: Foo
WRITE
READ
2: Buzz
3: Foo
4: Buzz
在
println
前,我们需要去读取data.value
并将资料喂给println
function ,因此每次在 snapshot 内的println
之前都会触发 READ
Composable 最後会被丢到 Recomposer
去执行,在 Recomposer
中我们可以发现这段 code
private inline fun <T> composing(
composition: ControlledComposition,
modifiedValues: IdentityArraySet<Any>?,
block: () -> T
): T {
val snapshot = Snapshot.takeMutableSnapshot(
readObserverOf(composition), writeObserverOf(composition, modifiedValues)
)
try {
return snapshot.enter(block)
} finally {
applyAndCheck(snapshot)
}
}
private fun readObserverOf(composition: ControlledComposition): (Any) -> Unit {
return { value -> composition.recordReadOf(value) }
}
//
override fun recordReadOf(value: Any) {
if (!areChildrenComposing) {
composer.currentRecomposeScope?.let { // <---
it.used = true
observations.add(value, it)
...
}
}
}
Recomposer
在 composing 时,利用 snapshot 的 read/write observer 关注在 snapshot 中的 state 变化。并且在 read observer 时建立 recompose scope。
private fun writeObserverOf(
composition: ControlledComposition,
modifiedValues: IdentityArraySet<Any>?
): (Any) -> Unit {
return { value ->
composition.recordWriteOf(value)
modifiedValues?.add(value)
}
}
override fun recordWriteOf(value: Any) = synchronized(lock) {
// ...
derivedStates.forEachScopeOf(value) { // <---
invalidateScopeOfLocked(it)
}
}
在资料变动时,会 iterate 各个 scope 并且将他们 invalidate。
今天聊到的东西非常的底层,因为篇幅的关系,有些逻辑没有说明到。非常推荐大家到下面 Reference 的地方阅读本篇主要参考的文章。看了这些之後感觉对整个 compose 运作流程又再更近一步的认识了!
Reference:
<<: D24 - 走!去浏览器偷听 Capturing & Bubbling
>>: Vue.js指令介绍&基本指令(Directives)(DAY26)
哈罗~今天来聊聊跟@mixin的兄弟 @extend 我们平常在写css时,时常会把class们相同...
上一篇提到了TiDB的特色之一,便是实践了HTAP。那HTAP又是什麽东西? HTAP全名Hybri...
我一开始在学 Vuex 的时候,觉得很难懂,不知道它是在做什麽的。当时的我,就想先追朔单一资料流的始...
前言 前阵子看了bilibili上的一些技术相关影片,码牛学院的公开课程 Android动态加载技术...
延续昨天的内容,在昨天理解完 mount() 後,今天就开始来对资料进行操作吧!! 资料绑定 如果用...