前言
像我们之前更新View的方式都是通过 setText() 这种形式进行的,随着声明式UI Compose 浪潮的袭来,我们有了其他的选择。声明式UI 通俗的讲 就是数据改变,UI 就会随之刷新,而无需我们拿到变化的数据之后再进行UI 更新。
使用
我们先来看一段很简单的代码:
code-1
val name = mutableStateOf("hello compose")
setContent {
Text(name.value)
}
lifecycleScope.launch {
delay(3000)
name.value = "android"
}

这是一个对 mutableStateOf 最简单的使用。效果很简单,就是延时对文字部分进行了刷新。
可以看到,当name的值进行修改的时候,Text() 也就会随之进行刷新,我们无需关心UI ,这些都是 mutableStateOf 帮我们完成的。
浅聊
刚介绍了mutableStateOf 对的简单使用,我们接下来 简单分析一下它,看看他是如何完成的。
我们先看mutableStateOf 的源码 ,具体的源码追溯 这里就只做简单列举,大家有时间可以自行阅读。
mutableStateOf
->createSnapshotMutableState
->ParcelableSnapshotMutableState
->SnapshotMutableStateImpl
我们重点要分析的就是 SnapshotMutableStateImpl 这个类里面的内容
code-2
internal open class SnapshotMutableStateImpl<T>(
value: T,
override val policy: SnapshotMutationPolicy<T>
) : StateObject, SnapshotMutableState<T> {
@Suppress("UNCHECKED_CAST")
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
............
}
这个类 实现了两个接口,StateObject 和 SnapshotMutableState。
其中 SnapshotMutableState 是MutableState 的子接口, 这里面最重要的就是定义了value,mutableStateOf 的基石就是这个value。
然后我们看下StateObject 接口:
code-3
interface StateObject {
/**
* The first state record in a linked list of state records.
*/
val firstStateRecord: StateRecord
.....
}
接口最重要的一个内容 StateRecord,我们在看看它是做什么的
code-4
abstract class StateRecord {
// 记录 快照id
internal var snapshotId: Int = currentSnapshot().id
internal var next: StateRecord? = null
// 复制StateRecord
abstract fun assign(value: StateRecord)
// 创建一个新的记录相同的StateRecord
abstract fun create(): StateRecord
}
StateRecord 是一个链表的数据结构,snapshotId 是用来记录当前的快照id的。
StateObject 中的 firstStateRecord 就是记录了StateRecord 的头节点。
这两个数据结构了解之后,我们继续看 code-2 SnapshotMutableStateImpl 中的内容
code-5
private var next: StateStateRecord<T> = StateStateRecord(value)
这个是创建了一个StateStateRecord ,这是StateRecord的一个子类,将value值 进行了一次封装。
code-6
override val firstStateRecord: StateRecord
get() = next
这就简单了, 这个就是一个赋值,将next 复制给 firstStateRecord ,记录头节点。
最重要的是value 的get 和 set
code-7
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 }
}
}
get方法
我先来看下 get()方法
next 我们已经知道是 StateRecord 了,我们来看下 readable 做了些什么?
code-8
fun <T : StateRecord> T.readable(state: StateObject): T =
readable(state, currentSnapshot())
currentSnapshot()的主要作用就是获取或创建一个快照。
Snapshot 的主要功能是隔离和感知状态变化。
关于快照系统的详细讲解 可以阅读 AndroidPub 公众号的《揭秘 Jetpack Compose 快照系统》
在这里又调用了两个参数的readable ,
code-9
fun <T : StateRecord> T.readable(state: StateObject, snapshot: Snapshot): T {
// invoke the observer associated with the current snapshot.
snapshot.readObserver?.invoke(state)
return readable(this, snapshot.id, snapshot.invalid) ?: readError()
}
readObserver 是一个读操作的观察者,这个操作是记录 StateObject 中的值 被哪里调用了,其实这个操作更像一个订阅操作。
然后是调用了一个三个参数的 readable
code-10
private fun <T : StateRecord> readable(r: T, id: Int, invalid: SnapshotIdSet): T? {
var current: StateRecord? = r
var candidate: StateRecord? = null
while (current != null) {
if (valid(current, id, invalid)) {
candidate = if (candidate == null) current
else if (candidate.snapshotId < current.snapshotId) current else candidate
}
current = current.next
}
if (candidate != null) {
return candidate as T
}
return null
}
前边我们分析到 StateRecord 是一个链表,这里这个操作就是通过遍历列表 来获取 最新的有效的StateRecord。
经过了三层readable 方法,最终是返回一个最新的可用的StateRecord
整个get() 方法做的事情 有两个:
- 返回最新的可用的值
- 记录在哪里调用了
set方法
我们接着分析set方法
code-11
set(value) = next.wi thCurrent {
if (!policy.equivalent(it.value, value)) {
next.overwritable(this, it) { this.value = value }
}
}
先看下 withCurrent:
code-12
inline fun <T : StateRecord, R> T.withCurrent(block: (r: T) -> R): R =
block(current(this, Snapshot.current))
这里又调用了 current:
code-13
internal fun <T : StateRecord> current(r: T, snapshot: Snapshot) =
readable(r, snapshot.id, snapshot.invalid) ?: readError()
是不是熟悉的味道,这里又是上文分析的 三参数的 readable 方法,用来返回最新的可用的StateRecord的。
那 withCurrent 的作用也就明朗了,是用来返回 StateRecord的。
我们继续分析
code-14
policy.equivalent(it.value, value)
这段代码是用来比较数据的, 如果数据相同的话,那就直接不执行操作了。
code-15
next.overwritable(this, it) { this.value = value }
接下来我们看下 overwritable 做了些什么?
代码块-16
internal inline fun <T : StateRecord, R> T.overwritable(
state: StateObject,
candidate: T,
block: T.() -> R
): R {
var snapshot: Snapshot = snapshotInitializer
return sync {
snapshot = Snapshot.current
this.overwritableRecord(state, snapshot, candidate).block()
}.also {
notifyWrite(snapshot, state)
}
}
这里是先获取到快照,然后调用了 overwritableRecord 方法:
code-17
internal fun <T : StateRecord> T.overwritableRecord(......): T {
.....
val id = snapshot.id
if (candidate.snapshotId == id) return candidate
val newData = newOverwritableRecord(state, snapshot)
newData.snapshotId = id
snapshot.recordModified(state)
return newData
}
这里是如果StateRecord 记录的快照id 正好对应当前 snapshot的id,直接返回。
否则会创建一个新的 或者返回一个弃用的 StateRecord,然后将快照id 赋予新的StateRecord。
一句话: 用overwritableRecord 取一个对应当前snapshot的 StateRecord
我们返回继续看code-16,在调用完overwritableRecord之后,又调用了 block() ,block 是我们传进来的lambda,
那就应该是code-11 中的 这一段
code-18
{ this.value = value }
这里的this 指的就是 code-17中 返回的StateRecord,然后将set 方法中的value参数 赋值给 StateRecord 中的value。
其实简单来说 就是 获取对应snapshot的 StateRecord ,然后把值设置到里面。
这个过程结束之后 我们在看code-16中,还有一句
code-19
notifyWrite(snapshot, state)
code-20
internal fun notifyWrite(snapshot: Snapshot, state: StateObject) {
snapshot.writeObserver?.invoke(state)
}
这里的主要作用是寻找变量在哪里被读了,然后将这部分内容的组合标记为失效,等到下一帧的时候 会重组。
整个set 方法所做的事情也是有两个 :
- 将传入的值赋值给
StateRecord内部的value - 写入通知刷新
总结
现在我们进行一下总结:
当 get 被调用的时候,不仅仅要将值返回给你,还要记录一下 在哪里读了。
当set 被调用的时候,不仅仅要将数据进行改变,还要找下在哪里被读过,然后去进行刷新。
就这样一读一写 配合完成了数据改变 UI自动进行刷新的功能。
今天就聊到这里,如果有问题欢迎留言和私信。