Jetpack Compose 是引荐用于构建原生 Android 界面的新工具包。后续简称Jetpack Compose为Compose。

在了解State之前需求先对Compose及声明性编程式有个大约的了解,引荐看下文章# 声明式 UI?Android 官方怒推的 Compose 到底是什么#

State初体验

好了,在你有必定了解的基础上,咱们先来运行几个Demo,初步了解为何运用state。这个比如主要是想经过点击按钮,改动案牍的显示。(这个过程称之为重组)

Demo1:

class StateDemoActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        supportActionBar?.hide()
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window, false)
        setContent {
            TestComposeTheme {
                var state = false
                Log.e("StateDemoActivity", "hashcode:${state.hashCode()} value:${state}")
                Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) {
                    Button(onClick = {
                        state = !state
                        Log.e("StateDemoActivity", "onClick hashcode:${state.hashCode()} value:${state}")
                    }, modifier = Modifier.fillMaxWidth()) {
                        Text(
                            text = "Change State:${state}",
                            color = MyColorTheme.textMain,
                            fontSize = 20.sp
                        )
                    }
                }
            }
        }
    }
}

对应输出如下:

StateDemoActivity        E  hashcode:1237 value:false
StateDemoActivity        E  onClick hashcode:1231 value:true
StateDemoActivity        E  onClick hashcode:1237 value:false
StateDemoActivity        E  onClick hashcode:1231 value:true
StateDemoActivity        E  onClick hashcode:1237 value:false

能够发现,点击按钮Text显示的文本没有改动,没有到达预想的目的。

Demo2:这次咱们运用官方的MutableState

TestComposeTheme {
    var state = mutableStateOf(false)
    Log.e("StateDemoActivity", "hashcode:${state.hashCode()} value:${state.value}")
    Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) {
        Button(onClick = {
            state.value = !state.value
            Log.e("StateDemoActivity", "onClick hashcode:${state.hashCode()} value:${state.value}")
        }, modifier = Modifier.fillMaxWidth()) {
            Text(
                text = "Change State:${state.value}",
                color = MyColorTheme.textMain,
                fontSize = 20.sp
            )
        }
    }
}

输出如下,显示的案牍依然没有改动,但比较之前多了Column之前的打印,这标明办法体被从头执行了,state变量的哈希值也在改变。以上体现和普通java办法别无差异,state是办法中的局部变量

StateDemoActivity        E  hashcode:103910553 value:false
StateDemoActivity        E  onClick hashcode:103910553 value:true
StateDemoActivity        E  hashcode:118168247 value:false
StateDemoActivity        E  onClick hashcode:118168247 value:true
StateDemoActivity        E  hashcode:245965755 value:false

Demo3:这次咱们稍作改动,额定运用remember函数,其他不变

var state = remember {
    mutableStateOf(false)
}

输出如下,按钮文字终于如预想的那样发生了改变,此外有个特别的现象是state变量的哈希值并没有发生改变,标明办法每次执行时,state变量并没有从头创立。

StateDemoActivity        E  hashcode:103910553 value:false
StateDemoActivity        E  onClick hashcode:103910553 value:true
StateDemoActivity        E  hashcode:103910553 value:true
StateDemoActivity        E  onClick hashcode:103910553 value:false
StateDemoActivity        E  hashcode:103910553 value:false

remember办法内部必然有全局容器存储变量,源码中能够很明显的看出

@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =
    currentComposer.cache(false, calculation)

综上咱们能够知道,Compose是依赖对State改变的调查来从头执行Compose办法(精确来说Compose基于参数的比较成果来决定是否重组)

重组与安稳类型

接上文一个 Composable 函数在重组中被调用时,如果参数与前次调用时比较没有发生改变,则函数的执行会跳过重组,提高重组功能。需求特别阐明Compose不会因为被调查的对象与前次是同一个就跳过重组。概况参阅 Compose类型安稳性注解:@Stable & @Immutable

Composable的重组规模

在之前的内容中,咱们已经知道了参数的改变会影响重组是否执行,这就带了重组规模的问题。这方面大佬已经有了很好的文章。建议小伙伴先阅览# Jetpack Compose:了解composable的重组规模

当我阅览后用代码验证时却发现了反常,代码如下

class StateDemoActivity : AppCompatActivity() {
    private val TAG = StateDemoActivity::class.java.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
        supportActionBar?.hide()
        super.onCreate(savedInstanceState)
        WindowCompat.setDecorFitsSystemWindows(window, false)
        setContent {
            TestComposeTheme {
                Log.e(TAG, "Scope-1 run ")
                var counter by remember {
                    mutableStateOf(0)
                }
                Column(modifier = Modifier.fillMaxSize().systemBarsPadding()) { 
                    Log.e(TAG, "Scope-2 run ")
                    Button(onClick = run {
                        Log.e(TAG, "Button-onClick")
                        return@run { counter++ }
                    }) {
                        Log.e(TAG, "Scope-3 run ")
                        Text(text = "+")
                    }
                    Text(
                        text = "Counter:${counter}",
                        color = MyColorTheme.textMain,
                        fontSize = 20.sp
                    )
                }
            }
        }
    }
}

输出如下

StateDemoActivity        E  Scope-1 run
StateDemoActivity        E  Scope-2 run 
StateDemoActivity        E  Button-onClick
StateDemoActivity        E  Scope-3 run 

按照文章内的说法Scope-3 run 这行是不应该被打印出来的,即不应该参与重组的,可实际成果却相反。小伙伴能够自己考虑下,稍后再往下翻看原因;

————–完美的分割线^_^————

class StateDemoActivity : AppCompatActivity() {
    companion object {
        private val TAG = StateDemoActivity::class.java.simpleName
    }
    ....
}
StateDemoActivity        E  Scope-1 run
StateDemoActivity        E  Scope-2 run 
StateDemoActivity        E  Button-onClick

日志终于和了解的相同了,现在咱们回头找原因,知道问题就出在打印的变量“TAG”身上。办法中各作用域Scope都读取了变量“TAG”,在第一个代码中该变量是个非安稳类型,故按钮触发重组时,为了保证正确性,所有引用到该变量的Scope都会重组;而第二个代码中“TAG”已经是个静态变量了,故而是个安稳类型,所以重组时不会引发非必要的重组。

最终补充一点:

从Android View转Compose过程中,对LiveData的运用场景需求特别留意。因为本来对LiveData的调查是经过observe办法进行的,每次LiveData内容更新(即便值相同)回调依然会收到;转换为State之后,只要value改变了,才会引发重组;例如

val missOut = MutableLiveData(false)
...
val state = viewModel.missOut.observeAsState(false).value

多次调用missOut.value = true,state只要在第一次设置为true时引发重组。如果期望行为和之前observe相同,应该如下调用:

LaunchedEffect(key1 = Unit) {
    viewModel.missOut.asFlow().collect {
        ...
    }
}

至于compose为何能够调查到状况的改变,请参阅带你了解 Jetpack Compose 快照体系

参阅文章:

Jetpack Compose学习之mutableStateOf与remember是什么
状况和 Jetpack Compose
android compose
揭秘 Compose 原理,了解 Composable 的实质
Compose 的 State 状况到底是个啥?