当我在Android Studio中使用Room搜索记录时,如何显示基于Loading UI的不同复合体



以下代码来自本文。

使用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) 
... 
}

最新更新