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...

相关推荐
王哈哈^_^1 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie1 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic2 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿2 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161773 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test4 小时前
js下载excel示例demo
前端·javascript·excel
Yaml44 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事4 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶4 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json