其实原理性分析的文章,真的很难讲的通俗易懂,讲的简单浅显没必要写,讲的繁琐难懂往往大家也不乐意看,所以怎么找个好的角度慢慢钻进去尤为重要,比如:Begin simple code,如果确实能帮助到大家完全理解了文章所讲述到的源码理论,那就点个赞吧!
引入话题
正如我上面说的,直接讲原理太枯燥(你也会很懵),我喜欢从简单代码入手,带你一点点进入,现在开始。先看一个最简单的代码示例:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 这种定义变量的方式随处可见了
val name = mutableStateOf("Compose")
setContent {
ComposeBlogTheme {
Text(name.value)
}
}
}
}
我先来简单解读一下这段代码的原理:
- 当我们定义一个变量,用
mutableStateOf
包起来后,它就变成了一个MutableState
类型的对象。 - 同时,我们取值的话就必须要写
name.value
,这样能才能取到 “Compose”,因为name 不再是一个 String,而是MutableState 对象
,也可以叫“状态” 。 - 此时,
name
是一个被订阅的状态,name.value
就是一个被订阅的值,如果它发生变化,Text() 函数就会重新执行一遍,更新到最新的值。
现在我们修改下代码,3s 后改变 name.value 的值:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val name = mutableStateOf("Compose")
setContent {
ComposeBlogTheme {
Text(name.value)
}
LaunchedEffect(true) {
delay(3000) // 3s 延迟
name.value = "Kotlin" // 修改 name.value
}
}
}
}
运行下:
现在关于 mutableStateOf 的用法你已经掌握了,但同时就会产生了一个疑问,name
被自动订阅了,它的值改变了就会让界面重新刷新,这背后的“状态订阅&刷新机制”的原理是什么?
如果你想深入了解,那么接着往下看。

状态订阅&自动刷新
基于 androidx.compose.runtime:runtime:1.4.0 版本
硬核部分走起,查看 mutableStateOf() 源码:
fun <T> mutableStateOf(
value: T,
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)
- mutableStateOf() 返回的是一个 MutableState 对象,这个我们前面说过。
- mutableStateOf() 又调用了另一个函数:createSnapshotMutableState()。
进入 createSnapshotMutableState():
internal actual fun <T> createSnapshotMutableState(
value: T,
policy: SnapshotMutationPolicy<T>
): SnapshotMutableState<T> = ParcelableSnapshotMutableState(value, policy)
又调用了 ParcelableSnapshotMutableState() 函数:
internal class ParcelableSnapshotMutableState<T>(
value: T,
policy: SnapshotMutationPolicy<T>
) : SnapshotMutableStateImpl<T>(value, policy), Parcelable {
// 这里内部的代码全部都是对 Parcelable 接口的实现,我们不用关心,不是核心内容
... ...
}
关键在 SnapshotMutableStateImpl,它里面才是最核心的逻辑:
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
override fun prependStateRecord(value: StateRecord) {
@Suppress("UNCHECKED_CAST")
next = value as StateStateRecord<T>
}
@Suppress("UNCHECKED_CAST")
override fun mergeRecords(
previous: StateRecord,
current: StateRecord,
applied: StateRecord
): StateRecord? {
val previousRecord = previous as StateStateRecord<T>
val currentRecord = current as StateStateRecord<T>
val appliedRecord = applied as StateStateRecord<T>
return if (policy.equivalent(currentRecord.value, appliedRecord.value))
current
else {
val merged = policy.merge(
previousRecord.value,
currentRecord.value,
appliedRecord.value
)
if (merged != null) {
appliedRecord.create().also {
(it as StateStateRecord<T>).value = merged
}
} else {
null
}
}
}
override fun toString(): String = next.withCurrent {
"MutableState(value=${it.value})@${hashCode()}"
}
private class StateStateRecord<T>(myValue: T) : StateRecord() {
override fun assign(value: StateRecord) {
@Suppress("UNCHECKED_CAST")
this.value = (value as StateStateRecord<T>).value
}
override fun create(): StateRecord = StateStateRecord(value)
var value: T = myValue
}
/**
* The componentN() operators allow state objects to be used with the property destructuring
* syntax
*
* ```
* var (foo, setFoo) = remember { mutableStateOf(0) }
* setFoo(123) // set
* foo == 123 // get
* ```
*/
override operator fun component1(): T = value
override operator fun component2(): (T) -> Unit = { value = it }
/**
* A function used by the debugger to display the value of the current value of the mutable
* state object without triggering read observers.
*/
@Suppress("unused")
val debuggerDisplayValue: T
@JvmName("getDebuggerDisplayValue")
get() = next.withCurrent { it }.value
}
好长啊~算了,不看了🙈…

接下来我带你一步步探索其中的奥秘!
先看开头部分:
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
... ...
}
- 一个 value 属性印入眼帘,这就是文章开头代码里面用到的
name.value
。 - value 有 get() 和 set() 函数,并且都有具体的实现。
📓 next 是个啥?
无论是 get()
还是 set()
都有一个 next
。
get() = next.readable(this).value
set(value) = next.withCurrent {...}
它是个啥?我们得先搞明白这个。
private var next: StateStateRecord<T> = StateStateRecord(value)
它的类型是 StateStateRecord
,那 StateStateRecord
有是个啥?
private class StateStateRecord<T>(myValue: T) : StateRecord() {
override fun assign(value: StateRecord) {
@Suppress("UNCHECKED_CAST")
this.value = (value as StateStateRecord<T>).value
}
override fun create(): StateRecord = StateStateRecord(value)
var value: T = myValue
}
StateStateRecord
继承了 StateRecord
,那 StateRecord
又是个啥呢?
abstract class StateRecord {
internal var snapshotId: Int = currentSnapshot().id // 记录快照 id
internal var next: StateRecord? = null // 下一个状态记录的引用,状态记录存储在一个链表中
abstract fun assign(value: StateRecord) // 复制 StateRecord
abstract fun create(): StateRecord // 创建一个新的记录相同的 StateRecord
}
你只要知道 StateRecord
是一个链表的数据结构即可,而 StateStateRecord
实现了它,并且将 value
进行了封装。
下面我们要讲别的了,先记住:StateRecord 是一个链表的数据结构!

接着说,如果你仔细看代码的话,会发现 SnapshotMutableStateImpl 其实继承了两个接口:
internal open class SnapshotMutableStateImpl<T>(
value: T,
override val policy: SnapshotMutationPolicy<T>
) : StateObject, SnapshotMutableState<T> {
... ...
}
一个 StateObjet、一个 SnapshotMutableState。
我们先来看下 SnapshotMutableState:
interface SnapshotMutableState<T> : MutableState<T> {
/**
* A policy to control how changes are handled in a mutable snapshot.
*/
val policy: SnapshotMutationPolicy<T>
}
SnapshotMutableState 继承了 MutableState,正好对应着我们文章开头说的,mutableStateOf() 返回的就是一个 MutableState,我们说是因为它实现了订阅从而可以刷新,但!!!真正的原因并不是因为它!
真正实现状态订阅机制的是另外一个接口:StateObjet!
我们现在来看下 StateObject 这个大佬干了什么事:
interface StateObject {
/**
* The first state record in a linked list of state records.
*/
val firstStateRecord: StateRecord
fun prependStateRecord(value: StateRecord)
fun mergeRecords(
previous: StateRecord,
current: StateRecord,
applied: StateRecord
): StateRecord? = null
}
代码非常简单,但里面有一个核心:
val firstStateRecord: StateRecord
它是干嘛用的?我们去瞅瞅哪里用了它:
private var next: StateStateRecord<T> = StateStateRecord(value)
override val firstStateRecord: StateRecord
get() = next
哦,原来 firstStateRecord 是用来记录 StateRecord 这个链表的 头节点 用的。

📓 get()
上面应该算是把 next 是什么讲清楚了吧?-- 它就是 StateRecord,更准确的说就是 StateRecord 链表的头节点。
下面我们开始扒扒 get() 具体的逻辑了。
get() = next.readable(this).value
进入 readable() :
fun <T : StateRecord> T.readable(state: StateObject): T {
val snapshot = Snapshot.current
// 第一件事
snapshot.readObserver?.invoke(state)
// 第二件事
return readable(this, snapshot.id, snapshot.invalid) ?: sync {
val syncSnapshot = Snapshot.current
@Suppress("UNCHECKED_CAST")
readable(state.firstStateRecord as T, syncSnapshot.id, syncSnapshot.invalid) ?: readError()
}
}
干了两件事,第一件事:
snapshot.readObserver?.invoke(state) // 第一件事
readObserver 是一个读操作的观察者,这个操作是记录 StateObject 中的值被哪里调用了,比如开头的代码示例:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val name = mutableStateOf("Compose")
setContent {
ComposeBlogTheme {
// 这里调用了 name.value,就会执行到:
// get() --> readServer() --> 记录这里调用了 value 值
Text(name.value)
}
LaunchedEffect(true) {
delay(3000)
name.value = "Kotlin"
}
}
}
}
所以,我们可以把这个操作理解为它是一个订阅操作:订阅状态,记录 name.value 在哪里调用了。
第二件事:
return readable(this, snapshot.id, snapshot.invalid) ?: sync {
val syncSnapshot = Snapshot.current
@Suppress("UNCHECKED_CAST")
readable(state.firstStateRecord as T, syncSnapshot.id, syncSnapshot.invalid) ?: readError()
}
调用了三参数的 readable():
private fun <T : StateRecord> readable(r: T, id: Int, invalid: SnapshotIdSet): T? {
// The readable record is the valid record with the highest snapshotId
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) {
@Suppress("UNCHECKED_CAST")
return candidate as T
}
return null
}
前边我们说错:StateRecord 是一个链表,三参数里面的工作就是通过表头对象遍历列表来获取 最新的、有效的 StateRecord。
最终返回一个 最新的、有效的 StateRecord。(什么是最新的、有效的,这个可以不用关心,这跟要结合 Snapshot 快照系统说,你直接过滤即可,不影响我们原理的理解)
所以,整个 readable() 方法就干了两件事:
- 返回最新的、有效的 StateRecord
- 记录在哪里调用了 value
get() = next.readable(this).value
最后还剩一个 value,这就很简单了,前面我们说过 StateStateRecord (StateRecord 的实现类) 会对 value 进行封装,而 StateRecord 它们只是一个链表,我们要获取到值,就要再调用 value 获取内部被包着的值。
到这里 get() 就讲完了!

📓 set()
现在来看 set() 的具体代码:(细节点注意:这边我把代码截图了,而不是纯代码段,你留意一下,下面会回归到这里)
next 是啥不用说了吧,那 withCurrent 是什么?
inline fun <T : StateRecord, R> T.withCurrent(block: (r: T) -> R): R =
block(current(this, Snapshot.current))
它只有一个参数 block,而 block 是一个函数类型的参数,它的工作很直接,直接调用这个函数类型的参数,也就是 withCurrent 后面跟着的 Lambda 表达式。
block 里面传入了一个 current
函数,它做了什么:
internal fun <T : StateRecord> current(r: T, snapshot: Snapshot) =
readable(r, snapshot.id, snapshot.invalid) ?: readError()
发现了什么?它调用了一个三参数的 readable(),你还记得三参数的 readable() 是做什么的吗?
⇒ 获取 最新的、有效的 StateRecord。
这个时候你在看下我开头为啥对代码做了截图:
懂啥意思没?withCurrent 传入的 current 函数的返回值就对应着代码提示器提示的 it 对象(一个 StateStateRecord)。
接着看代码:
set(value) = next.withCurrent {
// 判断新旧值
if (!policy.equivalent(it.value, value)) {
next.overwritable(this, it) { this.value = value }
}
}
if 判断里面会判断取到的 StateRecord 的值和新设置的值是否相同,没变直接结束,变了就进入下一步 overwritable()。
我们看看它做了什么:
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)
}
}
也干了两件事:
- 获取快照;2. 调用了overwritableRecord() 函数;
Snapshot 快照的知识我们可以先忽略,来看 overwritableRecord() 做了什么:
internal fun <T : StateRecord> T.overwritableRecord(
state: StateObject,
snapshot: Snapshot,
candidate: T
): T {
if (snapshot.readOnly) {
snapshot.recordModified(state)
}
val id = snapshot.id
if (candidate.snapshotId == id) return candidate
val newData = newOverwritableRecord(state)
newData.snapshotId = id
snapshot.recordModified(state)
return newData
}
这段代码又涉及到了另外一个知识点:Snapshot(快照)。

我们将代码分为两端来看:
核心代码块 1:如果传进来的 StateRecord 的快照 id 正好对应当前 snapshot 的 id,那么直接返回。
核心代码块 2:否则会创建一个新的或者返回一个弃用的 StateRecord,然后将快照 id 赋予新的 StateRecord。
说白了最终就是要取到一个对应当前 snapshot 的 StateRecord!
🤔 此时就存在一个很大的疑问了?什么是 Snapshot(快照)?Snapshot 怎么和 StateRecord 绑在一起了?
关于 Compose 的 Snapshot 机制,可以看看这篇文章 揭秘 Jetpack Compose
快照系统 ,讲的很好。
但由于 Snapshot 真的不是一两句就能说清楚的,但我仍然告诉你它不影响你对这篇文章原理的理解。

继续回到代码:
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
// 取到一个对应当前 snapshot 的 StateRecord
this.overwritableRecord(state, snapshot, candidate).block()
}.also {
notifyWrite(snapshot, state)
}
}
取到对应当前 snapshot 的 StateRecord 后,紧接着调用了 block(),它是传进来的参数,是外面传进来的,也就是 {this.value = value}
。
set(value) = next.withCurrent {
// 判断新旧值
if (!policy.equivalent(it.value, value)) {
next.overwritable(this, it) { this.value = value }
}
}
又拐回来了,把传入的新值赋值给拿到的 StateRecord 内部的 value,这不就是写新值的操作么?
到这里是不是 set() 流程是不是我们就讲完了? No~,我们再看一下 overwritable() 方法内部:
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) // 但这里还有个 notifyWrite
}
}
还有一行我们遗漏了,notifyWrite() 又做了什么?
internal fun notifyWrite(snapshot: Snapshot, state: StateObject) {
snapshot.writeObserver?.invoke(state)
}
有种似曾相识的感觉,在前面分析 get() 函数的时候,我们看到过单参数的 readable() 里面有一行:
snapshot.readObserver?.invoke(state)
那这里的作用呢?
⇒ 寻找变量在哪里被读了,然后将这部分内容的组合标记为失效,等到下一帧的时候会重组刷新。
所以,整个 set() 方法就干了两件事:
- 将传入的值赋值给 StateRecord 内部的 value
- 写入通知刷新

到这里我们就基本上能够很清晰的明白了状态订阅&自动刷新机制的原理了:
- 当 get() 被调用的时候,不仅返回值,还会记录读值的地方,也就是哪里调用了(相当于订阅)。
- 当 set() 被调用的时候,不仅修改值,还会查找读值的地方,然后进行刷新操作(相当于通知)。
📓 by
在实际开发中,如果每次获取值都要加上 value 会显得很冗余,所以 Compose 给我们提供了一种更方便的写法:by。它是 Kotlin 的一个关键字,表示左边的变量用右边的对象作为代理(委托)。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val name by mutableStateOf("Compose")
setContent {
ComposeBlogTheme {
Text(name) // 可以直接 name,而不需要 name.value
}
}
}
}
如果你这么写,IDLE 会提示报错,因为 name 委托给了 mutableStateOf,如果我们要获取 name 的值,那委托对象需要调用 getValue() 和 setValue() 两个函数,这两个函数需要自己实现。
但实际上并不需要我们实现,你通过提示也可以看出来,我们可以直接导入,因为 Compose 内部已经帮我们实现好了这个方法。
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
自此,关于 Compose 的状态订阅&自动刷新机制的原理算是讲明白了吧…
