我如何使用Kotlin协程在视图模型中获取数据并初始化一个字段而不使用空字段的latenite

u59ebvdq  于 2023-01-21  发布在  Kotlin
关注(0)|答案(2)|浏览(88)

我有一个获取数据的常见情况,我使用Kotlin协程。

1个变体:

class SomeViewModel(
    private val gettingData: GetDataUseCase
) : ViewModel() {

    lateinit var data: List<String>

    init {
        viewModelScope.launch {
            data = gettingData.get()
        }
    }
}

2个变体:

class SomeViewModel(
    private val gettingData: GetDataUseCase
) : ViewModel() {

    val data = MutableStateFlow<List<String>?>(null)

    init {
        viewModelScope.launch {
            data.emit(gettingData.get())
        }
    }
}

我怎样才能用viewModelScope初始化一个数据域不是延迟的,而是立即的,但是没有lateinit或者nullble域?没有LiveData,我的progect使用CoroutineFlow
我无法在.run{}中或通过lazy {}返回viewModelScope作业的结果。我无法从fun返回结果:

val data: List<String> = getData()

fun getData(): List<String> {
    viewModelScope.launch {
        data = gettingData.get()
    }
    return ???
}

此外,我无法使suspend有趣getData(),因为我无法在初始化中创建协程范围'

vdzxcuhz

vdzxcuhz1#

您必须使用SharedFlow和replay 1(存储最后一个值并为新订阅者重放它)来实现它。
我的样品:

interface DataSource {
    suspend fun getData(): Int
}

class DataViewModel(dataSource: DataSource): ViewModel() {
    val dataField =
        flow<Int> {
            dataSource.getData()
        }.shareIn(viewModelScope, SharingStarted.WhileSubscribed(1000), 1)
}
jdzmm42g

jdzmm42g2#

你在描述一种不可能性,假设gettingData.get()被定义为一个suspend函数,这意味着结果不能被立即检索,因为检索需要一段时间,所以你不能有一个immediate值。
这就是为什么应用程序和网站的用户界面中有加载指示器。
如果您使用的是Flow,则可以使用可为空类型的Flow(如上面的选项2),并且在Activity/Fragment中,在收集器中,您可以根据其是否为空来显示加载指示器或数据。
您的代码2可以使用flow构建器和默认值为空的stateIn来简化:

class SomeViewModel(
    private val gettingData: GetDataUseCase
) : ViewModel() {

    val data = flow<List<String>?> { gettingData.get() }
        .stateIn(viewModelScope, SharingStarted.Eagerly, null)

}

在您的Activity或Fragment中:

viewLifecycleOwner.lifecycleScope.launch {
    viewModel.data
        .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
        .collect { list ->
            if(list == null) {
                // Show loading indicator in UI
            } else {
                // Show the data
            }
         }
}

如果你的数据加载速度很快,你可以直接设置默认值emptyList(),而不是让类型为空,这样当列表为空时,你的收集器就什么都不做了。如果数据加载速度足够快,用户不会因为屏幕空白太久而怀疑是否出了问题,这就可以了。

相关问题