一、什么是状态广义来说状态就是应用里会变化的数据。一官方给出的Compose状态定义在Compose中界面的自动更新是通过状态的变化来实现的每当状态发生更新应用到此状态的界面就会发生重组从而实现界面的自动更新。在Compose中我们可以通过mutableStateOf()、mutableXxxStateOf()来定义一个可观察的Compose状态如下val isPlaying mutableStateOf(false) val currentProgress mutableFloatStateOf(0f) val currentTime mutableLongStateOf(0L) Composable fun MusicScreen() { IconButton( onClick { //播放/暂停 if (exoPlayer.isPlaying) { exoPlayer.pause() } else { exoPlayer.play() } //修改isPlaying状态的值 isPlaying.value exoPlayer.isPlaying }, modifier Modifier.size(64.dp) ) { Icon( //此处应用了isPlaying状态当isPlaying状态值变化时icon资源会进行改变 painter if (isPlaying.value) painterResource(R.drawable.baseline_pause_128) else painterResource(R.drawable.baseline_play_circle_128), contentDescription ) } }二、什么是非Compose状态也就是不定义在Compose界面代码中的状态比如定义在ViewModle中的LiveData、StateFlow状态定义协程中的状态等。在实际开发中真正使界面更新的数据往往不直接来自Compose状态而来自网络请求的数据、数据库中的数据等等比如文章列表数据、主题明暗模式等。但是只有把这些外部状态转换为Compose状态才能实现Compose的状态界面更新。所以就会涉及到一个问题如何把非Compose状态转换为Compose状态从而实现界面的状态驱动更新。三、非Compose状态转换为Compose状态的方法一通过DisposableEffect()无需用到协程来更新的非Compose状态可以使用此函数来转换为Compose状态。举例如下以下实现了音乐列表循环播放的功能Composable fun MusicScreen() { val context LocalContext.current val songCount songList.size //当前播放歌曲索引 //remember使用在每个可能会进入recompose重组的变量上以避免变量在recompose的过程中再次被初始化造成无法预知的错误简单来讲remember起到一个缓存的作用 //但是在compose中我们无法判断某个区域是否会进入recompose故而最好将所有写在可组合区域也就是compose代码中包括Composable组合函数、setContent中的变量均包上remember var songIndex by remember { mutableIntStateOf(0) } //当前播放歌曲 val curSong songList[songIndex] // 1.播放器 val exoPlayer rememberExoPlayer(context, curSong.rawRes) val isPlaying remember { mutableStateOf(false) } val totalDuration remember { mutableLongStateOf(0L) } val currentProgress remember { mutableFloatStateOf(0f) } val currentTime remember { mutableLongStateOf(0L) } //列表循环 DisposableEffect(exoPlayer, songIndex) { val listener object : Player.Listener { override fun onPlaybackStateChanged(state: Int) { if (state Player.STATE_ENDED) { // 列表循环 songIndex if (songIndex songCount - 1) songIndex 1 else 0 } } } exoPlayer.addListener(listener) //DisposableEffect提供的资源回收、副作用销毁回调 onDispose { exoPlayer.removeListener(listener) } } }上述DisposableEffect中代码的含义首先创建exoPlayer的播放状态监听listener并在其回调里将播放状态非Compose状态转换为Compose状态songIndex。为exoPlayer注册listener监听在onDispose{}中为为exoPlayer移除listener监听二通过LaunchedEffect()此函数为Compose中用于启动协程的函数。需要用到协程来更新的非Compose状态可以使用此函数来转换为Compose状态。举例如下以下实现了一个黑胶唱片旋转的动画效果Composable fun MusicScreen() { val isPlaying remember { mutableStateOf(false) } var diskRotation by remember { mutableFloatStateOf(0f) } val rotationSpeed 0.042f // 每帧递增度数可调速度 // 动画黑胶旋转通过修改变量的值来实现动画Compose的状态驱动 //此处参数传入的意义参数值发生变化会导致LaunchedEffect被取消而后重新启动 LaunchedEffect(isPlaying.value) { while (isPlaying.value) { diskRotation rotationSpeed * 16 // 16是大致每帧毫秒 if (diskRotation 360f) diskRotation - 360f delay(16) } // 这里不处理归零保持在当前角度暂停状态 } Box( modifier Modifier .fillMaxWidth() .fillMaxHeight(0.8f) .aspectRatio(1f)//裁切为正方形 .graphicsLayer(rotationZ diskRotation),//设置旋转动画 contentAlignment Alignment.Center ) { //黑胶 Box( modifier Modifier .clip(CircleShape) .background(Color.Black, CircleShape) .fillMaxSize(0.7f) .align(Alignment.Center) ) { Image( painter painterResource(id R.drawable.ic_disc), contentDescription null, modifier Modifier .fillMaxSize() .clip(CircleShape) ) } // 封面 Box( modifier Modifier .clip(CircleShape) .background(Color.White, CircleShape) .fillMaxSize(0.5f) .align(Alignment.Center) ) { Image( painter painterResource(id curSong.coverRes), contentDescription null, modifier Modifier .fillMaxSize() .clip(CircleShape) ) } } }上述LaunchedEffect中代码的含义进入一个循环当isPlaying.valuetrue时开启循环不断修改rotationSpeed 非Compose状态的值并将其转换赋值给diskRotation Compose状态调用协程函数delay每隔16ms执行一次循环三通过produceState()produceState()本质上是对LaunchedEffect()的一个简化封装方便使用。从它的源码可以得知/** * Return an observable [snapshot][androidx.compose.runtime.snapshots.Snapshot] [State] that * produces values over time without a defined data source. * * [producer] is launched when [produceState] enters the composition and is cancelled when * [produceState] leaves the composition. [producer] should use [ProduceStateScope.value] to set new * values on the returned [State]. * * The returned [State] conflates values; no change will be observable if [ProduceStateScope.value] * is used to set a value that is [equal][Any.equals] to its old value, and observers may only see * the latest value if several values are set in rapid succession. * * [produceState] may be used to observe either suspending or non-suspending sources of external * data, for example: * * sample androidx.compose.runtime.samples.ProduceState * sample androidx.compose.runtime.samples.ProduceStateAwaitDispose */ Composable public fun T produceState( initialValue: T, producer: suspend ProduceStateScopeT.() - Unit, ): StateT { val result remember { mutableStateOf(initialValue) } LaunchedEffect(Unit) { ProduceStateScopeImpl(result, coroutineContext).producer() } return result }1.简便在哪里可以看到produceState函数在内部定义了一个Compose状态且此状态的类型由传入的第一个参数类型决定并将这个Compose状态作为返回值返回。这也就意味着我们可以省去前置定义Compose状态这一步直接通过传入初始值来得到一个转换好的Compose状态。2.举例说明//直接使用LaunchedEffect Composable fun MusicScreen() { val isPlaying remember { mutableStateOf(false) } var diskRotation by remember { mutableFloatStateOf(0f) } val rotationSpeed 0.042f // 每帧递增度数可调速度 // 动画黑胶旋转通过修改变量的值来实现动画Compose的状态驱动 //此处参数传入的意义参数值发生变化会导致LaunchedEffect被取消而后重新启动 LaunchedEffect(isPlaying.value) { while (isPlaying.value) { diskRotation rotationSpeed * 16 // 16是大致每帧毫秒 if (diskRotation 360f) diskRotation - 360f delay(16) } // 这里不处理归零保持在当前角度暂停状态 } }//使用produceState Composable fun MusicScreen() { val isPlaying remember { mutableStateOf(false) } val rotationSpeed 0.042f // 每帧递增度数可调速度 // 动画黑胶旋转通过修改变量的值来实现动画Compose的状态驱动 val diskRotation produceState(rotationSpeed,isPlaying.value) { while (isPlaying.value) { value rotationSpeed * 16 // 16是大致每帧毫秒 if (diskRotation 360f) diskRotation - 360f delay(16) } // 这里不处理归零保持在当前角度暂停状态 } }