以下代码来自本文。
使用wordRepository.allWords()
查询记录会花费时间,因此作者在fun load()
中先设置_isLoading.value = true
,然后查询记录,最后设置set _isLoading.value = false
。
我认为作者希望在查询更复杂的情况下继续长时间显示LoadingUi()
。
但我认为这些代码存在一些问题。
suspend fun allWords(): Flow<PagingData<Word>>
是冷流,所以suspend fun allWords()
会立即返回,_isLoading.value = false
会在代码B中快速启动。我认为无论有3000条记录还是10条记录,LoadingUi()
都会保持显示相同的时间。
解决方案有问题吗?我希望LoadingUi()
在查询需要处理3000条记录时保持长时间显示,LoadingUi()
在查询需要管理10条记录时显示。
代码A
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.load()
setContent {
...
val isLoading by viewModel.isLoading.collectAsState(false)
WordsTheme {
when {
isLoading -> LoadingUi()
else -> WordListUi(...)
}
}
}
}
}
class MainViewModel(application: Application) : AndroidViewModel(application) {
private val _isLoading = MutableStateFlow(true)
val isLoading: StateFlow<Boolean> = _isLoading
fun load() = effect {
_isLoading.value = true
allWords.value = wordRepository.allWords()
_isLoading.value = false
}
private fun effect(block: suspend () -> Unit) {
viewModelScope.launch(Dispatchers.IO) { block() }
}
}
class WordRepository(...) {
suspend fun allWords(): Flow<PagingData<Word>> = wordStore.ensureIsNotEmpty().all()
suspend fun allWords(term: String): Flow<PagingData<Word>> = wordStore.ensureIsNotEmpty().all(term)
...
}
代码B
fun load() = effect {
_isLoading.value = true
//suspend fun allWords(): Flow<PagingData<Word>> is cold Flow,
//wordRepository.allWords() will return at once no matter there is 3000 records or 10 records
allWords.value = wordRepository.allWords()
_isLoading.value = false
}
添加的内容:
第一次运行代码C:E/My:返回持续时间:57
第二次运行代码C:E/My:退货持续时间:1099
冷流在开始收集值之前不会开始产生值,所以我认为它将在第一次运行和第二次运行之间花费相同的时间。
suspend fun allWords(): Flow<PagingData<Word>>
是冷流,我认为它会立即返回,为什么它在第一次运行和第二次运行之间花费不同的时间?
代码C(我修改过(
fun load() = effect {
_isLoading.value = true
val a = Calendar.getInstance().timeInMillis //I add
allWords.value = wordRepository.allWords()
val b = Calendar.getInstance().timeInMillis //I add
Log.e("My","Duration for Return: "+(b-a)) //I add
_isLoading.value = false
}
//I modify
suspend fun allWords(): Flow<PagingData<Word>> {
//delay(10) //The first run
delay(1000) //The second run
return wordStore.ensureIsNotEmpty().all()
}
//I modify
suspend fun allWords(term: String): Flow<PagingData<Word>> {
//delay(10) //The first run
delay(1000) //The second run
return wordStore.ensureIsNotEmpty().all(term)
}
private suspend fun WordStore.ensureIsNotEmpty() = apply {
if (isEmpty()) {
val words = wordSource.load()
save(words)
}
}
我认为这个解决方案没有任何问题。WordRepository
中定义的函数allWords()
如下:
suspend fun allWords(): Flow<PagingData<Word>> = wordStore.ensureIsNotEmpty().all()
private suspend fun WordStore.ensureIsNotEmpty() = apply {
if (isEmpty()) {
val words = wordSource.load()
save(words)
}
}
这里,当调用allWords()
时,首先执行函数ensureIsNotEmpty()
(由于网络调用wordSource.load()
,可能需要一些时间(,然后.all()
函数返回Flow
。
功能wordSource.load()
使用Dispatchers.IO
上下文发出网络请求,并在WordSource
中定义如下:
suspend fun load(): List<Word> = withContext(Dispatchers.IO) {
client.getRemoteWords()
.lineSequence()
.map { Word(it) }
.toList()
}
因此wordRepository.allWords()
发出网络请求,可能需要一些时间才能执行,因此MainViewModel
中的函数load()
似乎是正确的:
fun load() = effect {
_isLoading.value = true
allWords.value = wordRepository.allWords()
_isLoading.value = false
}
在开始收集值之前,冷流不会开始产生值。
这是正确的,但在返回Flow
之前的allWords()
函数中,会发生延迟:
suspend fun allWords(): Flow<PagingData<Word>> {
delay(1000) // delay before wordStore.ensureIsNotEmpty().all() function returns a Flow
return wordStore.ensureIsNotEmpty().all()
}
这就是为什么你有不同的执行时间。
您可能会感到困惑,因为allWords()
的返回类型是Flow
,但函数本身并不创建流。
所以我认为它将在第一次运行和第二次运行之间花费相同的时间。
要做到这一点,allWords()
函数应该使用flow
生成器自己生成Flow
fun allWords(): Flow<PagingData<Word>> = flow {
delay(1000)
...
}