Kotlin Side-effects 之 produceState

372 阅读2分钟

produceState 是一个 Jetpack Compose 库中的函数,它用于将非 Compose 的异步或监听驱动的状态转换成 Compose 可以理解和响应的State。它特别有用于整合像 Flow、LiveData 或者 RxJava 这样的响应式流到 Compose UI 中。

produceState 函数返回一个 State 对象,并启动一个与 Composition 生命周期相关联的协程来更新这个 State 对象的值。

  • 这个协程会在 Composable 进入Composition时启动,
  • 并在 Composable 离开Composition时取消。
  • 返回的 State 是"合并"的,即多次设置相同的值不会触发重组。

使用 produceState 时,可以指定一个初始值和依赖项列表。当依赖项发生变化时,会自动取消当前的协程并启动一个新的协程。

@Composable
fun loadNetworkImage(
    url: String,
    imageRepository: ImageRepository = ImageRepository()
): State<Result<Image>> {

    // Creates a State<T> with Result.Loading as initial value
    // If either `url` or `imageRepository` changes, the running producer
    // will cancel and will be re-launched with the new inputs.
    return produceState<Result<Image>>(initialValue = Result.Loading, url, imageRepository) {

        // In a coroutine, can make suspend calls
        val image = imageRepository.load(url)

        // Update State with either an Error or Success result.
        // This will trigger a recomposition where this State is read
        value = if (image == null) {
            Result.Error
        } else {
            Result.Success(image)
        }
    }
}

class ImageRepository {
    fun load(url: String): Image? { return null }
}

sealed class Result<out T> {
    object Loading : Result<Nothing>()
    object Error : Result<Nothing>()
    class Success<T>(t: T?) : Result<T>()
}

在这个例子中,我们创建了一个带有初始值 Result.LoadingState<Result<Image>>。当 Composable loadNetworkImage 被加入到 Composition 中时,produceState 会启动一个协程,在该协程中我们调用 imageRepository.load(url) 来加载图片。

一旦图片加载完成,我们更新 State 的值。如果图片加载成功,我们设置值为 Result.Success(image);如果加载失败或图片为 null,我们设置值为 Result.Error。这个 State 的更新会触发使用该state的 Composable 重组。

produceState 在底层使用了其他的 Compose effect,它通过 remember { mutableStateOf(initialValue) } 来保持result变量,并在 LaunchedEffect 中触发producer块。当producer块中的 value 被更新时,result状态也会更新为新的值。