Compose中的remember和mutableStateOf

在Compose的官方指导和示例代码中常常会看到这样的代码android

var count by remember{mutableStateOf(0)}
 或者
 var count = remember{mutableStateOf(0)}
复制代码

首先须要注意几个问题缓存

  1. 用by的话,count是Int类型【即mutableStateOf参数的类型】
  2. 用“=”, count是MutableState类型,使用时须要经过.value来取值
  3. 使用by的时候可能会报红,错误是

Type 'TypeVariable(T)' has no method 'getValue(Nothing?, KProperty<*>)' and thus it cannot serve as a delegatemarkdown

解决方法很简单,导包便可ide

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
也能够直接:
import androidx.compose.runtime.*
复制代码

这个问题多是Compose的一个bug,若是只是 by mutableStateOf(0),也会报错,可是按alt+enter 会提示须要导包,可是加了remember以后,按alt+enter是不提示导包的,很是奇怪。 4. 注意remember 后面是 {} , 不是()函数

进入正题,来看看mutableStateOf 和 remember 都是干吗的spa

普通参数
class MainActivity : ComponentActivity() {
    @ExperimentalMaterialApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            println("---- clicked onCreated setContent ")
            Surface() {
                var count = 0 // 无状态
                Button(
                    onClick = { count++ }, modifier = Modifier
                        .padding(16.dp)
                        .fillMaxWidth()
                        .height(50.dp)
                ) {
                    Text(
                        text = "I have been clicked $count times",
                        modifier = Modifier.align(Alignment.CenterVertically)
                    )
                    SideEffect(effect = { println("---- text count = $count ") })
                }
                SideEffect(effect = { println("---- out count = $count ") })
            }
        }
    }
}
复制代码

点击按钮,数字不变,控制台只有一次打印日志

2021-05-26 10:21:45.248 32194-32194/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 10:21:45.301 32194-32194/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 10:21:45.301 32194-32194/com.shakespace.compose I/System.out: ---- out count = 0 
复制代码
有状态参数
var count by mutableStateOf(0) // 改为mutableStateOf
复制代码

点击按钮,数字会不断增长,控制台输出code

2021-05-26 10:26:36.949 16988-16988/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 10:26:37.002 16988-16988/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 10:26:37.002 16988-16988/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 10:26:40.511 16988-16988/com.shakespace.compose I/System.out: ---- text count = 1 
2021-05-26 10:26:41.141 16988-16988/com.shakespace.compose I/System.out: ---- text count = 2 
2021-05-26 10:26:41.792 16988-16988/com.shakespace.compose I/System.out: ---- text count = 3 
2021-05-26 10:26:42.828 16988-16988/com.shakespace.compose I/System.out: ---- text count = 4 
2021-05-26 10:26:43.596 16988-16988/com.shakespace.compose I/System.out: ---- text count = 5 
复制代码
  1. 相比普通变量,使用mutableStateOf,使得变量有了被观察的能力,当值发生变化时就会通知使用这个变量的控件进行更新。
  2. 问题是这里没有用到remember,点击时数字仍是会增长,是否是不须要remember呢?
重绘机制

把Button 的padding 改为和count 有关orm

setContent {
            println("---- clicked onCreated setContent ")
            Surface() {
                var count by mutableStateOf(0)
                Button(
                    onClick = { count++ }, modifier = Modifier
                        .padding(count.dp) // 把Button的padding 改为和count 有关
                        .fillMaxWidth()
                        .height(50.dp)
                ) {
                    Text(
                        text = "I have been clicked $count times",
                        modifier = Modifier.align(Alignment.CenterVertically)
                    )
                    SideEffect(effect = { println("---- text count = $count ") })
                }
                SideEffect(effect = { println("---- out count = $count ") })
            }
        }
复制代码

点击按钮,数字不会变化,控制台输出内存

2021-05-26 11:49:28.394 19077-19077/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 11:49:28.450 19077-19077/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 11:49:28.450 19077-19077/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 11:49:33.240 19077-19077/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 11:49:33.240 19077-19077/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 11:49:34.123 19077-19077/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 11:49:34.123 19077-19077/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 11:49:35.776 19077-19077/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 11:49:35.776 19077-19077/com.shakespace.compose I/System.out: ---- out count = 0 
复制代码

这时候数字不会变化,是由于Surface的直接子组件Button依赖于count这个state,那么count变化的时候,Surface接收的这个Composable函数就会重绘[方法从新调用],每次调用count就会是0,因此数字没有变化。

而前面Button不依赖于count的例子,外部不须要重绘(看日志 out 只打印了一次),count也不会重置,因此数字会增长。

remember

在这种状况下,若是还想记住变量值,就要用到remember

setContent {
            println("---- clicked onCreated setContent ")
            Surface() {
                var count by remember{ mutableStateOf(0)} // 使用remember
                Button(
                    onClick = { count++ }, modifier = Modifier
                        .padding(count.dp)
                        .fillMaxWidth()
                        .height(50.dp)
                ) {
                    Text(
                        text = "I have been clicked $count times",
                        modifier = Modifier.align(Alignment.CenterVertically)
                    )
                    SideEffect(effect = { println("---- text count = $count ") })
                }
                SideEffect(effect = { println("---- out count = $count ") })
            }
        }
复制代码

点击按钮,数字会增长,控制台输出:

2021-05-26 11:56:28.675 5449-5449/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 11:56:28.728 5449-5449/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 11:56:28.729 5449-5449/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 11:56:36.311 5449-5449/com.shakespace.compose I/System.out: ---- text count = 1 
2021-05-26 11:56:36.311 5449-5449/com.shakespace.compose I/System.out: ---- out count = 1 
2021-05-26 11:56:36.840 5449-5449/com.shakespace.compose I/System.out: ---- text count = 2 
2021-05-26 11:56:36.840 5449-5449/com.shakespace.compose I/System.out: ---- out count = 2 
2021-05-26 11:56:37.206 5449-5449/com.shakespace.compose I/System.out: ---- text count = 3 
2021-05-26 11:56:37.206 5449-5449/com.shakespace.compose I/System.out: ---- out count = 3 
复制代码

外部仍是会重绘,可是每次值都会内存缓存中读取,这就是remember的做用,当当前Composable重绘的时候,能够暂存变量值。

若是把变量放在外面?
setContent {
            println("---- clicked onCreated setContent ")
            var count by mutableStateOf(0) // 移到外面,不适用remember
            Surface() {
                Button(
                    onClick = { count++ }, modifier = Modifier
                        .padding(count.dp)
                        .fillMaxWidth()
                        .height(50.dp)
                ) {
                    Text(
                        text = "I have been clicked $count times",
                        modifier = Modifier.align(Alignment.CenterVertically)
                    )
                    SideEffect(effect = { println("---- text count = $count ") })
                }
                SideEffect(effect = { println("---- out count = $count ") })
            }
        }
复制代码

数字会增长,控制台输出

2021-05-26 12:03:32.605 9375-9375/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 12:03:32.658 9375-9375/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 12:03:32.658 9375-9375/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 12:03:35.100 9375-9375/com.shakespace.compose I/System.out: ---- text count = 1 
2021-05-26 12:03:35.100 9375-9375/com.shakespace.compose I/System.out: ---- out count = 1 
2021-05-26 12:03:38.646 9375-9375/com.shakespace.compose I/System.out: ---- text count = 2 
2021-05-26 12:03:38.646 9375-9375/com.shakespace.compose I/System.out: ---- out count = 2 
2021-05-26 12:03:39.642 9375-9375/com.shakespace.compose I/System.out: ---- text count = 3 
2021-05-26 12:03:39.642 9375-9375/com.shakespace.compose I/System.out: ---- out count = 3 
复制代码

这和重绘那个例子是相似的,setContent的参数也是个Composable,Surface做为直接子类,并无依赖于count,因此自己不会重绘,count也不会被重置。

可是

若是咱们把上面的Surface换成Column之类的Composable,其余都不变,结果就不同了

2021-05-26 12:07:16.163 9780-9780/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 12:07:16.166 9780-9780/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 12:07:16.166 9780-9780/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 12:07:17.047 9780-9780/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 12:07:17.050 9780-9780/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 12:07:17.050 9780-9780/com.shakespace.compose I/System.out: ---- out count = 0 
2021-05-26 12:07:17.883 9780-9780/com.shakespace.compose I/System.out: ---- clicked onCreated setContent 
2021-05-26 12:07:17.885 9780-9780/com.shakespace.compose I/System.out: ---- text count = 0 
2021-05-26 12:07:17.885 9780-9780/com.shakespace.compose I/System.out: ---- out count = 0 
复制代码

数字不变,并且 “clicked onCreated setContent” 每次都打印,说明setContent里面的Composable每次都会重绘

目前所能知道的是,Column、Row、Box之类的组件,传入的Composable参数是给对应的Scope的扩展函数,而Surface接收的就是一个普通的Composable函数,一个可能不够准确的结论是:扩展函数中的直接组件重绘的话,至关于接收这个扩展函数的组件也要重绘。【即Button须要重绘,Column也会重绘】

相关文章
相关标签/搜索