欢迎光临散文网 会员登陆 & 注册

Compose 中显示数据,要将其存储在变量中,并用 mutableStateOf 包裹,以便自动监听

2023-04-01 21:14 作者:哆啦a梦的道具师  | 我要投稿

在 Compose 中显示数据,通常我们要将其存储在变量中,并用 mutableStateOf 包裹,以便实现自动监听与更新。这一步有下列三种写法:

val name = mutableStateOf("Bob")               // 1

val name by mutableStateOf("Bob")              // 2

val name by remember { mutableStateOf("Bob") } // 3

Compose 过程

在深挖之前,有必要先了解 Compose 从构建到显示的完整过程。

Compose 分为三个步骤:组合 (compose)、布局、绘制。前者是 Compose 独有的,后面两个与传统 View 类似。所谓「组合」,就是根据我们写的代码创建出实际的界面,组合的结果称为 Composition

插一句题外话,Compose 函数虽然看起来很像是创建一个对象,例如 Text(text = name.value) 很像创建一个 Text 对象,但实际上不是这样。跟踪源码可以发现,这只不过是一个普通的函数,它没有返回值,因此类似 Text().text = name.value 的写法完全错误 ❌ 这与传统 View 中 TextView().text = "xxx" 不是一回事。

—— 所以需要「组合」


如果还是无法理解,可以将「组合」类比传统 View 中 inflat,解析 xml 文件创建实际的对象。只不过曾经我们可以绕过 xml 手动创建 view,而现在不行了。

MutableState

最基本的用例大概是这样:

val name = mutableStateOf("Bob") 

setContent {    Column {        Text(text = name.value)       

Button(onClick = { name.value = "2" }) 

{

           Text(text = "Change Name")        

}

   }

}


定义了一个 Text 和 Button,点击按钮,显示的文本就会改变。

跟踪源码轻易看出,mutableStateOf() 就是创建一个 ParcelableSnapshotMutableState 对象然后返回它。ParcelableSnapshotMutableState 本身并没有什么有价值的东西,主要是对 parcelable 的实现便于进程通信。真正储存数据的是它的父类 SnapshotMutableStateImpl

internal open class SnapshotMutableStateImpl<T>(

    value: T,

    override val policy: SnapshotMutationPolicy<T>

) : StateObject, SnapshotMutableState<T> {

    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 就是保存实际数据的东西,可惜它依然不是最终实现。继续探索便遇到一个关键类 StateRecordStateStateRecord 是它的子类),要搞清楚它,首先要看看 SnapshotMutableStateImpl 所实现的两个接口。现在我们已经知道 mutableStateOf() 可以返回一个容器对象,它能够被订阅并且在内部值改变时通知订阅者,以此实现 UI 自动刷新。「被订阅」这种特性其实是 StateObject 接口提供的,很反直觉。

再次套娃🪆,StateObject 本身又是一个容器,以链表的形式存储着一串 StateRecord ⬅️ 这个真正保存了数据。为什么是一串?因为它不仅保存了变量的最新值,还保留了曾经的值,以便实现「撤销」等功能。既然是链表,那肯定存在第一个节点,就是 StateObject.firstStateRecord ,后继节点就是 StateRecord.next。注意这个 next 和 SnapshotMutableStateImpl.next 无关,恰好重名而已。事实上 SnapshotMutableStateImpl 里有一个链表保存着所有值(实现了 StateObject 接口呀),它的首个节点是 SnapshotMutableStateImpl.nextSnapshotMutableState

小结:StateObject 以链表形式真正储存了数据,StateRecord 是链表的节点,SnapshotMutableStateImpl 是一个实现。

作者:晨鹤
链接:https://juejin.cn/post/7057852733012836388
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


Compose 中显示数据,要将其存储在变量中,并用 mutableStateOf 包裹,以便自动监听的评论 (共 条)

分享到微博请遵守国家法律