Compose笔记(四十四)--produceState

这一节主要了解一下Compose中的produceState,在Jetpack Compose中,produceState是一个用于将非Compose状态转换为Compose可观测状态的API。它会启动一个协程来执行异步操作,并将结果存储在返回的State对象中,当数据更新时自动触发UI重组。简单总结:

produceState的特点:

1 接收一个初始值和一个协程作用域,在协程中执行异步操作

2 自动管理协程生命周期,当 Composable 退出时会取消协程

3 返回一个 State 对象,可直接在 UI 中使用

栗子:

Kotlin 复制代码
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay

data class User(
    val id: String,
    val name: String,
    val email: String
)


suspend fun fetchUser(userId: String): User {
    // 模拟网络延迟
    delay(2000)
    return User(
        id = userId,
        name = "张三",
        email = "zhangsan@example.com"
    )
}

@Composable
fun UserProfile(userId: String) {
   
    val userState by produceState<User?>(
        initialValue = null, 
        key1 = userId 
    ) {
        
        value = fetchUser(userId)
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        when {
            userState == null -> {
                
                CircularProgressIndicator()
                Text("加载用户信息中...", modifier = Modifier.padding(top = 16.dp))
            }
            else -> {
                
                Text("ID: ${userState?.id}")
                Text("姓名: ${userState?.name}", modifier = Modifier.padding(vertical = 8.dp))
                Text("邮箱: ${userState?.email}")
            }
        }
    }
}


@Composable
fun UserProfileDemo() {
    UserProfile(userId = "123456789")
}
Kotlin 复制代码
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay

@SuppressLint("ProduceStateDoesNotAssignValue")
@Composable
fun CountdownTimer(initialSeconds: Int) {
    var isActive by remember { mutableStateOf(false) }

   
    val countdownState by produceState(
        initialValue = initialSeconds,
        key1 = isActive 
    ) {
        if (isActive) {
            while (value > 0) {
                delay(1000) 
                value--
            }
            isActive = false 
        }
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "剩余时间: $countdownState 秒",
            fontSize = 24.sp,
            modifier = Modifier.padding(bottom = 24.dp)
        )

        Button(
            onClick = { isActive = !isActive },
            enabled = countdownState > 0 || !isActive
        ) {
            Text(if (isActive) "暂停" else "开始")
        }
    }
}


@Composable
fun CountdownDemo() {
    CountdownTimer(initialSeconds = 30)
}

注意:

1.避免阻塞主线程

produceState 内部协程默认在主线程运行,长时间阻塞会导致界面卡顿,使用withContext(Dispatchers.IO)切换到后台线程执行耗时操作。

2.正确处理key参数

当key值变化时,produceState会取消当前协程并重启,若key未正确设置,可能导致协程未重启或重复执行。

3.避免频繁更新状态

短时间内多次更新value可能导致UI频繁重组,使用debounce或throttle限制更新频率。

4.清理资源

需手动释放资源,可通过awaitDispose注册清理逻辑。