当我在 Android Studio 中使用 Room 搜索记录时,如何显示 Loading UI based different complex?

How can I display Loading UI based different complex when I search records using Room in Android Studio?

以下代码来自article.

使用wordRepository.allWords()查询记录会花时间,所以作者先设置_isLoading.value = true,然后查询记录,最后在fun load().set _isLoading.value = false.

我觉得作者希望在查询比较复杂的时候,可以长时间显示LoadingUi()

但我认为这些代码存在一些问题。

suspend fun allWords(): Flow<PagingData<Word>>冷流,所以suspend fun allWords()一下子return,_isLoading.value = false就上线了在代码B中很快。我认为LoadingUi()无论有3000条记录还是10条记录都会保持相同的时间。

解决方案有问题吗?我希望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:Return的持续时间:57

第二个运行代码C:E/My:Return的持续时间:1099

一个冷流在开始收集它们之前不会开始产生价值,所以我认为它会在 第一个 运行第二个运行.

suspend fun allWords(): Flow<PagingData<Word>>是冷流,我觉得一下子就return,为什么第一个运行[=69=的时间不一样]和第二个运行?

代码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) 
     }      
  }

我没有发现此解决方案有任何问题。函数 allWords()WordRepository 中定义如下:

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()函数return是 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
}

A cold stream does not start producing values until one starts to collect them.

这是正确的,但是在 Flow 被 returned 之前的 allWords() 函数中,延迟发生了:

suspend fun allWords(): Flow<PagingData<Word>> {
   delay(1000)      // delay before wordStore.ensureIsNotEmpty().all() function returns a Flow
   return wordStore.ensureIsNotEmpty().all()
} 

这就是为什么你有不同的执行时间。

您可能会感到困惑,因为 allWords() 的 return 类型是 Flow,但函数本身不会创建流​​。

so I think it will spend the same time between The first run and The second run.

要做到这一点,allWords() 函数应该使用 flow 构建器自行生成 Flow

fun allWords(): Flow<PagingData<Word>> = flow { 
   delay(1000) 
   ... 
}