Jetpack Compose -> 状态机制的背后秘密

前言


上一章我们讲解了 Jetpack Compose 的无状态、状态提升、单向数据流 本章我们讲解下状态机制的背后秘密

List


前面我们讲过,通过 by mustableStateOf() 就可以被 Compose 自动订阅了;我们前面是通过 String 类型进行的自动订阅,那么换成其他类型是可以的吗?答案是可以的,只要被 mustableStateOf 包裹之后,它就会被一个 MustableState 包裹,这个 MustableState 就是一个代理对象,状态的订阅和更新会被代理到它上面,所以 我们使用其他类型也是可以的,我们可以来看一个例子:

kotlin 复制代码
private var number by mutableStateOf(1)

    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Android_VMTheme {
                Surface {
                    Text(text = "当前数值是: $number", modifier = Modifier.clickable {
                        number ++
                    })
                }
            }
        }
    }

我们执行 Text 的点击事件,让 number++ 来看下 number 的变化是不是可以及时的更新到结果,运行看下:

可以看到,是可以实时的更新的,所以说,换成其他类型,也是可以的;

这个时候,可能会有人有疑问了,那么换成非基本数据类型可以吗?比如换成 List,好,我们来试一下

kotlin 复制代码
private var nums by mutableStateOf(mutableListOf(1, 2, 3))

@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Android_VMTheme {
            Surface {
                Column {
                    for (num in nums) {
                        Text(text = "当前是第 $num 的文字")
                    }
                }
            }
        }
    }
}

我们来运行看下是否是我们想要的效果:

达到了我们期望的效果,但是,接下来,我们对这个 List 进行一下修改看下界面是否还会跟着改变,怎么修改呢?我们可以继续使用点击监听的逻辑;

kotlin 复制代码
private var nums by mutableStateOf(mutableListOf(1, 2, 3))

    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Android_VMTheme {
                Surface {
                    Button(onClick = {
                        nums.add(nums.last() + 1)
                    }) {
                        Text(text = "添加内容")
                    }
                    Column {
                        for (num in nums) {
                            Text(text = "当前是第 $num 的文字")
                        }
                    }
                }
            }
        }
    }

我们额外增加了一个 Button,点击的时候,每点击一次,就给 List 增加一项内容,每次取最后一个值进行加1的操作,我们来运行看下效果:

可以看到,并没有达到我们期望的效果,界面内容并没有随着 List 内容的改变而改变,那么这又是为什么呢?我们来一探究竟;

我们先来想一下,这个被 by mustableStateOf 初始化的对象为什么可以被监听?因为它的get 和 set 函数被加了钩子,它的赋值和取值操作被代理了,所以它能够被监听,也就是 nums 的赋值取值被 mustableStateOf 代理了,所以它能够被监听。这个 nums 读的地方在 for 循环中被读取,那么『写』的地方是在哪里呢?是在 Button 的点击监听中更新了,这种写法看起来是没有问题的呀?那么它到底是哪里不对呢?

其实,就是在 nums 更新的地方不对!

nums 的 set 被加了钩子,是针对的 nums 的 set 方法,而不是 add 方法,所以这个改动是不生效的!也就是说 add 逻辑不会触发 setValue 的调用,所以这个改动不生效,也就不会触发自动更新的操作了;也就是说 如果我们强行增加一个 ReCompose 的过程,它的结果是会更新的;

我们来看一个 ReCompose 的过程:

kotlin 复制代码
private var number by mutableStateOf(1)
private var nums by mutableStateOf(mutableListOf(1, 2, 3))

@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Android_VMTheme {
            Surface {
                Column {
                    Text(text = "当前数值是: $number", modifier = Modifier.clickable {
                        number ++
                    })
                    Button(onClick = {
                        nums.add(nums.last() + 1)
                    }) {
                        Text(text = "添加内容")
                    }
                    Column {
                        for (num in nums) {
                            Text(text = "当前是第 $num 的文字")
                        }
                    }
                }
            }
        }
    }
}

我们在按钮的上面增加了一个 文字,给这个文字增加了点击监听,同时更改这个 number 的值,因为这些整体是被一个 Column 包裹,那么当 number 改变的时候,整个的区域会被 ReCompose,我们运行看下效果:

可以看到,当 number 改变的时候,List 的更新也呈现了出来;

所以,Compose 的监听更新是对 『赋值』操作的监听更新,像这种『nums.add(nums.last() + 1)』修改内部状态的是不会触发更新的,从而不会触发界面的刷新;

重新赋值

问题定位了,那么我们怎么来实现界面的刷新呢?首先大家想到的肯定是重新赋值,怎么赋值呢?

ini 复制代码
Button(onClick = {
    nums = nums.toMutableList().apply {
        add(nums.last() + 1)
    }
}) {
    Text(text = "添加内容")
}

通过 nums.toMutableList() 转换成一个新的 list 之后赋值给 nums,这样就是执行了一个『赋值』操作,我们运行看下效果:

我们通过点击,直接实现了界面的刷新操作~

但是,写到这里的时候,好多人会提出疑问了,这种写法会不会带来性能问题,以及这种写法是不是太笨重了,有没有更优雅的写法呢?答案是,不会带来性能问题,有的~

mustableStateListOf

Compose 针对 List 给我们提供另一个 API,叫作 mustableStateListOf,它内部也会创建一个 MustableStateList,并且它内部的变化也会被 Compose 观测到;

kotlin 复制代码
    private var nums = mutableStateListOf(1, 2, 3)

    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Android_VMTheme {
                Surface {
                    Column {
                        Text(text = "当前数值是: $number", modifier = Modifier.clickable {
                            number ++
                        })
                        Button(onClick = {
                            nums.add(nums.last() + 1)
                        }) {
                            Text(text = "添加内容")
                        }
                        Column {
                            for (num in nums) {
                                Text(text = "当前是第 $num 的文字")
                            }
                        }
                    }
                }
            }
        }
    }

我们直接使用 mutableStateListOf 来监听内部的变化,我们运行看下效果;

完美的实现了状态变化的界面刷新,并且比起前一种写法要好了很多;

mustableStateMapOf

跟 mutableStateListOf 比较相似的是 mustableStateMapOf 它是创建的一个 Map,并且监听这个 Map 的内部变化;

总结


Compose 里面用 mustableStateOf 创造出的 MustableState 是很简单的判断『是否重新赋值』 所以其无法监听普通的 List 和 Map,包括普通的 mustableListOf 和 mustableMapOf, 只能使用 mutableStateListOf 和 mustableStateMapOf 来解决;

好了,Compose 的课程今天就讲到这里吧~~

下一章预告


重组的性能风险和优化

欢迎三连


来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~~

相关推荐
晨春计31 分钟前
【git】
android·linux·git
标标大人1 小时前
c语言中的局部跳转以及全局跳转
android·c语言·开发语言
木鬼与槐2 小时前
MySQL高阶1783-大满贯数量
android·数据库·mysql
iofomo2 小时前
【Abyss】Android 平台应用级系统调用拦截框架
android·开发工具·移动端
AirDroid_cn5 小时前
在家找不到手机?除了语音助手,还可以用远程控制!
android·智能手机·远程控制·手机使用技巧·远程控制手机
Good_tea_h13 小时前
Android中如何处理运行时权限?
android
冬田里的一把火313 小时前
[Android][Reboot/Shutdown] 重启/关机 分析
android·gitee
大海..13 小时前
Android 系统开发人员的权限说明文档
android
技术无疆17 小时前
ButterKnife:Android视图绑定的简化专家
android·java·android studio·android-studio·androidx·butterknife·视图绑定
JohnsonXin17 小时前
【兼容性记录】video标签在 IOS 和 安卓中的问题
android·前端·css·ios·h5·兼容性