Jetpact Compose状态管理remember简单理解

简介: Jetpact Compose状态管理remember简单理解

概览


所谓的状态可以简单的理解为应用中的某个值的变化,比如可以是一个布尔值、数组

放在业务的场景中,可以是 TextField 中的文字、动画执行的状态、用户收藏的商品都是状态

我们知道 compose 是声明式的 ui,每次我们重组页面的时候都会把组件重组,此时就需要引入状态进行管理,例如:

我们在商品的 item 里面点击按钮收藏了商品,此时商品的收藏状态发生了改变,我们需要重组 ui 将商品变为已收藏状态,这个时候就需要用 remember 扩展方法保存重组状态,如果使用 boolean 这个基本类型保存那么就无法在重组 ui 后正常的设置组件的状态。

代码举例(抄官方代码):

@Composable
fun HelloContent() {
   Column(modifier = Modifier.padding(16.dp)) {
       OutlinedTextField(
           value = "输入的值",
           onValueChange = { },
           label = { Text("Name") }
       )
   }
}
复制代码


运行上面的代码,我们会发现无论我们如何在 TextField 中输入内容,TextFile 的内容都不会变,这就是因为无法保存状态导致的,以下代码示例可以正常的改变 TextField 中的内容

@Composable
fun textFieldStateHasTextShow(){
    var value by remember {//这里就是对TextField中展示的文字进行状态保存的操作
        mutableStateOf("")
    }
    Box(modifier = Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) {
        OutlinedTextField(
            value = value,
            onValueChange = {
                  value=it//每次输入内容的时候,都回调这个更新状态,从而刷新重组ui
            },
            label = { Text("Name") }
        )
    }
}
复制代码


状态管理的常用方法


remember 重组中保存状态

组合函数可以通过remember记住单个对象,系统会在初始化期间将remember初始的值存储在组合中。重组的时候可以返回对象值,remember既可以用来存储可变对象又可以存储不可变的对象

当可组合项被移除后,会忘记 remember 存储的对象。


mutableStateOf

mutableStateOf 会创建可观察的 MutableState<T>,例如如下代码: data 就是一个MutableState对象

每当data.value值发生改变的时候,系统就会重组ui。

var data = remember {
        mutableStateOf("")
}
复制代码

注:mutableStateOf 必须使用 remember 嵌套才能在数据更改的时候重组界面


rememberSaveable 保存配置

remember可以帮助我们在界面重组的时候保存状态,而rememberSaveable可以帮助我们存储配置更改(重新创建activity或进程)时的状态。


Livedata、Flow、RxJava 转换为状态

这三个框架是安卓常用的三个响应式开发框架,都支持转化为State对象,以 Flow 举例,如下代码可以转化为一个 State:

val favorites = MutableStateFlow<Set<String>>(setOf())
    val state = favorites.collectAsState()
复制代码


状态管理


有状态和无状态

使用 remember、rememberSaveState 方法保存状态的组合项是有状态组合

反之是无状态组合


状态提升

如下代码是官方关于状态提升的代码:

本例代码中 HelloContent 是无状态的,它的状态被提升到了 HelloScreen 中,HelloContent 有nameonNameChange两个参数,name 是状态,通过 HelloScreen 组合项传给 HelloContent

而 HelloContent 中发生的更改它也不能自己进行处理,必须将更改传给HelloScreen进行处理并重组界面。

以上的逻辑叫做:状态下降,事件上升

@Composable
fun HelloScreen() {
    var name by rememberSaveable { mutableStateOf("") }
    HelloContent(name = name, onNameChange = { name = it })
}
@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = onNameChange,
            label = { Text("Name") }
        )
    }
}
复制代码


存储状态的方式


前面的介绍中我们知道使用rememberSaveable方法我们可以通过 Bundle 的方式保存状态,那么如果我们要保存的状态不方便用 Bundle 的情况下该何如处理呢?

以下三种方式,可以实现对非 Bundle 的数据的保存(配置更改后的保存)


Parcelize

代码示例:

@Parcelize
data class City(val name: String, val country: String) : Parcelable
@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}
复制代码


MapSaver

data class City(val name: String, val country: String)
val CitySaver = run {
    val nameKey = "Name"
    val countryKey = "Country"
    mapSaver(
        save = { mapOf(nameKey to it.name, countryKey to it.country) },
        restore = { City(it[nameKey] as String, it[countryKey] as String) }
    )
}
@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid", "Spain"))
    }
}
复制代码


ListSaver

data class City(val name: String, val country: String)
val CitySaver = listSaver<City, Any>(
    save = { listOf(it.name, it.country) },//数组中保存的值和City中的属性是顺序对应的
    restore = { City(it[0] as String, it[1] as String) }
)
@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid", "Spain"))
    }
}
复制代码


状态管理源码分析


remember

初次阅读 remember 的源码,可能有理解不对的地方(但总得有人先去看不是),多多见谅,欢迎指正

  • remember 方法调用的主流程

remember方法返回的是一个MutableState对象,MutableState可以在数据更新的时候通知系统重组ui

image.png

rememberedValue 就是数据转换的逻辑

  • rememberedValue 方法解析

image.png

inserting:如果我们正在将新的节点插入到视图数中,那么 inserting=true

reusing:意为正在重用,我的理解是当前正在重新使用这个状态,所以避免多次获取

  • reader.next 方法 晒一段源码
fun next(): Any? {
        if (emptyCount > 0 || currentSlot >= currentSlotEnd) return Composer.Empty
        return slots[currentSlot++]
    }
复制代码

slots是一个数组,currentSlot表示我们要获取到的状态在数组中的索引,compose 构建页面是单线程的,索引每次我们调用remember方法的时候如果状态已经存在就从slots中获取数据,然后把currentSlot索引加 1,这样当我们调用了最后一个remember方法的时候currentSlot索引刚好等于slots数组.length-1



相关文章
|
7月前
|
存储 缓存 编译器
探索 Jetpack Compose 内核:深入 SlotTable 系统
探索 Jetpack Compose 内核:深入 SlotTable 系统
194 1
|
存储 算法 Android开发
Jetpack-Compose 学习笔记(三)—— Compose 的自定义“View”(下)
Jetpack-Compose 学习笔记(三)—— Compose 的自定义“View”(下)
132 0
|
6月前
深入了解 Jetpack Compose 中的 Modifier
深入了解 Jetpack Compose 中的 Modifier
107 0
|
算法 Android开发 开发者
Jetpack-Compose 学习笔记(三)—— Compose 的自定义“View”(上)
Jetpack-Compose 学习笔记(三)—— Compose 的自定义“View”(上)
196 0
|
7月前
|
安全 API 开发工具
一文看懂 Jetpack Compose 快照系统
一文看懂 Jetpack Compose 快照系统
137 0
|
存储 XML 前端开发
Jetpack-Compose 学习笔记(五)—— State 状态是个啥?又是新概念?(上)
Jetpack-Compose 学习笔记(五)—— State 状态是个啥?又是新概念?(上)
121 0
|
存储 前端开发 Java
Jetpack-Compose 学习笔记(五)—— State 状态是个啥?又是新概念?(下)
Jetpack-Compose 学习笔记(五)—— State 状态是个啥?又是新概念?(下)
86 0
|
缓存 Kotlin
开源 | 如何写一个好用的 JetPack Compose 状态页组件
世界很大,也很小,组件很多,也很少。
237 0
|
设计模式 存储 安全
Jetpack 系列(12)—— OnBackPressedDispatcher:Jetpack 处理回退事件的新姿势
Jetpack 系列(12)—— OnBackPressedDispatcher:Jetpack 处理回退事件的新姿势
481 0
Jetpack 系列(12)—— OnBackPressedDispatcher:Jetpack 处理回退事件的新姿势
|
存储 安全
Jetpack之DataStore解析
前言在正式讲解DataStore之前,我们先回顾下大家熟知的SharedPreferences(以下简称SP),众所周知SP有一些缺点,如调用getXXX()获取数据,可能会阻塞主线程;无法保证类型安全;加载的数据会一直留在内存中,浪费内存;apply方法无法获取到操作成功或失败的结果。SharedPreferences回顾getXXX()可能会阻塞主线程请看以下SP获取value值的使用代码: 
296 0
Jetpack之DataStore解析