Android Compose UI状态管理之状态提升(二)

上文讲了一下关于compose状态的一些生命周期相关的基础知识,本文就说一下我对官方所说的状态提升的一些理解。 有兴趣的同学可以看下

原文: Android Compose UI状态管理之生命周期(一)

前言:什么是状态提升

您应将界面状态提升到读取和写入状态的所有可组合项之间的最低共同祖先实体 。您应使状态尽可能靠近其使用位置。通过状态所有者,向使用者公开不可变状态和事件,以修改状态。这是官方的说明,后面将通过例子来进行说明。

eg:写一个计数并显示的函数HelloContent1(),我们可能会这样写点击按钮文本加一

-------- demo1

kotlin 复制代码
//-------- demo1

@Composable
fun HelloContent1() {
    Log.d("tgw11", "HelloContent: ")
    var count by rememberSaveable { mutableStateOf(1) } //注释1
    //var count = mainViewModel.count.value  //注释2
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "HelloContent1, $count",
            modifier = Modifier.padding(bottom = 8.dp),
        )
        Button(onClick = { count++ }) {
            Text(
                text = "点击计数增加, $count",
                modifier = Modifier.padding(bottom = 8.dp),
            )
        }

    }
}

上面这样写但就功能来说是没有问题的,但是假设count的值是一个公用的类似上面的注释2处。假设HelloContent()函数代码过多,您需要与其他可组合项共用界面元素状态,如有多个要使用到mainViewModel.count.value数值的函数方法,并在不同位置将界面逻辑应用到状态,则可在界面层次结构中提升状态所在的层次。这样做会使可组合项的可重用性更高,并且更易于测试管理。

kotlin 复制代码
@Composable
fun HelloContent() {
    Log.d("tgw1", "HelloContent: ")
    HelloContent1()
    ···
    省略n多代码
    ···
    HelloContent1()
    HelloContent1()
   
}

如何状态提升:

还是使用之前的例子如下:状态提升后为HelloContent1()的区别

-------- demo2

kotlin 复制代码
//-------- demo2

@Composable
fun HelloContent() {
    var count by rememberSaveable { mutableStateOf(1) }

    Log.d("tgw1", "HelloContent: ")
    //注释1
    HelloContent1(count = count, onCountChange = {
        count = it
    })
    
    //注释2
    Text(
        text = "Hello1",
        modifier = Modifier.padding(bottom = 8.dp),
    )
    
    //注释3
    Text(
        text = "Hello2",
        modifier = Modifier.padding(bottom = 8.dp),
    )
    
     Log.d("tgw1", "HelloContent: 值改变后--注释2---注释三发生重组")
}
kotlin 复制代码
@Composable
fun HelloContent1(count: Int, onCountChange: (Int) -> Unit) {
    Log.d("tgw11", "HelloContent: ")
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "HelloContent1, $count",
            modifier = Modifier.padding(bottom = 8.dp),
        )
        Button(onClick = { onCountChange(count+1) }) {
            Text(
                text = "点击计数增加, $count",
                modifier = Modifier.padding(bottom = 8.dp),
            )
        }

    }
}

上面的HelloContent1()就是提升的状态,这样的话使HelloContent1()变为一个无状态的函数,具备了较强的通用性,也就是编程里面的的函数式方法

状态提升的通用模式就是

我们将状态提升到调用的地方,一般的状态提升是将状态变量替换为两个参数。

  • value: T:要显示的当前值
  • onValueChange: (T) -> Unit:请求更改值的事件,其中 T 是建议的新值

此值表示任何可修改的状态,比如计数器中的count变量,onValueChange只是一个方法名,根据上下文随意命名即可。

状态提升使用不当会造成一些没必要的重组

点击HelloContent1()中的button按钮更改count状态 比如-------- demo2中注释2,注释3,的text文本发生重组如下图:

实际上的话它没必要去重组,大量没必要的重组会消耗性能,解决方案一种是将注释2,注释3的text文本,有两种方案

  • 因为这里无状态可以将注释2,注释3的text文本写到一个compose的方法函数中。
  • 尽可能延迟读取(因为compose重组机制决定,状态读取发生在哪个函数哪个函数就会发生重组)

这里主要说一下什么是 延迟读取: 官方回答--发现性能问题后,延后读取状态会有所帮助。延后读取状态可以确保 Compose 在重组时重新运行尽可能少的代码。例如,如果界面的状态在可组合项树中向上提升,而您在可组合子项中读取状态,则可以将状态封装在 lambda 函数中。这种方式可以确保仅在实际需要时才会执行读取操作。

根据状态读取发生在哪个compose 函数作用域,状态更新的时候,哪个哪个compose 函数作用域就发生重组这一个特点,利用函数调用的特性去进行状态读取。

HelloContent()函数

kotlin 复制代码
    @Composable
    fun HelloContent() {
        var count by rememberSaveable { mutableStateOf(1) }

        Log.d("tgw1", "HelloContent: ")
 //注释1
        HelloContent3(count = { count } , onCountChange = {
            count = it
        })
         //注释2
        Text(
            text = "Hello1",
            modifier = Modifier.padding(bottom = 8.dp),
        )
         //注释3
        Text(
            text = "Hello2",
            modifier = Modifier.padding(bottom = 8.dp),
        )
        Log.d("tgw1", "HelloContent: 值改变后--注释2---注释三发生重组")

    }

HelloContent3()函数

kotlin 复制代码
@Composable
fun HelloContent3(count: () -> Int, onCountChange: (Int) -> Unit) {
    Log.d("tgw31", "HelloContent: ")

    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "HelloContent1, ${count()}",
            modifier = Modifier.padding(bottom = 8.dp),
        )
        Button(onClick = { onCountChange(count() + 1) }) {
            Text(
                text = "点击计数增加, ${count()}",
                modifier = Modifier.padding(bottom = 8.dp),
            )
        }

    }
}

与上面的HelloContent1()函数相比,我们的count参数接受一个带有返回值的方法,将count状态的读取,下降到HelloContent3()方法中

点击HelloContent3()中的button按钮更改count状态,发现整个HelloContent1()未发生重组如下图:

参考: developer.android.google.cn/jetpack/com...

相关推荐
hackeroink1 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者3 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-3 小时前
验证码机制
前端·后端
燃先生._.4 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖5 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235245 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240256 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar6 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人7 小时前
前端知识补充—CSS
前端·css