聊聊 mutableStateOf

前言

像我们之前更新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"
}

test

这是一个对 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
    ............    
}

这个类 实现了两个接口,StateObjectSnapshotMutableState

其中 SnapshotMutableStateMutableState 的子接口, 这里面最重要的就是定义了valuemutableStateOf 的基石就是这个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 ,记录头节点。

最重要的是valuegetset

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() 方法做的事情 有两个:

  1. 返回最新的可用的值
  2. 记录在哪里调用了

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 取一个对应当前snapshotStateRecord

我们返回继续看code-16,在调用完overwritableRecord之后,又调用了 block()block 是我们传进来的lambda

那就应该是code-11 中的 这一段

code-18
{ this.value = value }

这里的this 指的就是 code-17中 返回的StateRecord,然后将set 方法中的value参数 赋值给 StateRecord 中的value

其实简单来说 就是 获取对应snapshotStateRecord ,然后把值设置到里面。

这个过程结束之后 我们在看code-16中,还有一句

code-19 
notifyWrite(snapshot, state)
code-20
internal fun notifyWrite(snapshot: Snapshot, state: StateObject) {
    snapshot.writeObserver?.invoke(state)
}

这里的主要作用是寻找变量在哪里被读了,然后将这部分内容的组合标记为失效,等到下一帧的时候 会重组。

整个set 方法所做的事情也是有两个 :

  1. 将传入的值赋值给StateRecord内部的value
  2. 写入通知刷新

总结

现在我们进行一下总结:

get 被调用的时候,不仅仅要将值返回给你,还要记录一下 在哪里读了。

set 被调用的时候,不仅仅要将数据进行改变,还要找下在哪里被读过,然后去进行刷新。

就这样一读一写 配合完成了数据改变 UI自动进行刷新的功能。

今天就聊到这里,如果有问题欢迎留言和私信。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不说话的匹诺槽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值