前言
像我们之前更新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自动进行刷新的功能。
今天就聊到这里,如果有问题欢迎留言和私信。