The Code A is from official article about Flow
viewModelScope.launch{} run in UI thread by default, I think suspend fun fetchLatestNews() will run in UI thread by default too, so I think Code A maybe cause UI blocked when fetchLatestNews() is long time operation, right?
I think Code B can fix the problem, right?
Code A
class LatestNewsViewModel(
private val newsRepository: NewsRepository
) : ViewModel() {
init {
viewModelScope.launch {
// Trigger the flow and consume its elements using collect
newsRepository.favoriteLatestNews.collect { favoriteNews ->
// Update View with the latest favorite news
}
}
}
}
class NewsRemoteDataSource(
private val newsApi: NewsApi,
private val refreshIntervalMs: Long = 5000
) {
val latestNews: Flow<List<ArticleHeadline>> = flow {
while(true) {
val latestNews = newsApi.fetchLatestNews()
emit(latestNews) // Emits the result of the request to the flow
delay(refreshIntervalMs) // Suspends the coroutine for some time
}
}
}
// Interface that provides a way to make network requests with suspend functions
interface NewsApi {
suspend fun fetchLatestNews(): List<ArticleHeadline>
}
Code B
class LatestNewsViewModel(
private val newsRepository: NewsRepository
) : ViewModel() {
init {
viewModelScope.launch(Dispatchers.IO) {
//The same
}
}
}
//The same
Added content:
To Tenfour04: Thanks!
I think a suspend function maybe block UI, so I think to define dispatchers is the most important in order not to block UI! Is it right?
Code 1 work well when I click "Start" button to display the information if I use Dispatchers.IO.
Code 2 is freezed and no information updated when I click "Start" button if I use Dispatchers.Main.
Code 3 can work just like Code 1 if I use Dispatchers.Main, the reason is only because I set delay(100).
BTW, Flow is suspend. So if there is a long time operation even if it's wrapped with coroutine instead of soundDbFlow().collect { myInfo.value = it.toString() }, I think I can get the same test result just like Code 1, Code 2 and Code 3.
And more, Code 4 is OK after I added flowOn(Dispatchers.IO) for the Flow even if it is launched in viewModelScope.launch(Dispatchers.Main){} !
Code 1 OK
class HandleMeter: ViewModel() {
var myInfo = mutableStateOf("World")
private var myJob: Job?=null
private var k=0
private fun soundDbFlow() = flow {
while (true) {
emit(k++)
delay(0)
}
}
fun calCurrentAsynNew() {
myJob?.cancel()
myJob = viewModelScope.launch(Dispatchers.IO){
soundDbFlow().collect { myInfo.value = it.toString() }
}
}
fun cancelJob(){
myJob?.cancel()
}
}
@Composable
fun Greeting(handleMeter: HandleMeter) {
var info = handleMeter.myInfo
Column(
modifier = Modifier.fillMaxSize()
) {
Text(text = "Hello ${info.value}")
Button(
onClick = { handleMeter.calCurrentAsynNew() }
) {
Text("Start")
}
Button(
onClick = { handleMeter.cancelJob() }
) {
Text("Stop")
}
}
}
Code 2 Freezed
class HandleMeter: ViewModel() {
private fun soundDbFlow() = flow {
while (true) {
emit(k++)
delay(0)
}
}
fun calCurrentAsynNew() {
myJob?.cancel()
myJob = viewModelScope.launch(Dispatchers.Main){
soundDbFlow().collect { myInfo.value = it.toString() }
}
}
...
//The same
}
...
//The same
Code 3 OK
class HandleMeter: ViewModel() {
private fun soundDbFlow() = flow {
while (true) {
emit(k++)
delay(100) //It's 100
}
}
fun calCurrentAsynNew() {
myJob?.cancel()
myJob = viewModelScope.launch(Dispatchers.Main){
soundDbFlow().collect { myInfo.value = it.toString() }
}
}
...
//The same
}
...
//The same
Code 4 Ok
class HandleMeter: ViewModel() {
private fun soundDbFlow() = flow {
while (true) {
emit(k++)
delay(0)
}
}.flowOn(Dispatchers.IO)// I added
fun calCurrentAsynNew() {
myJob?.cancel()
myJob = viewModelScope.launch(Dispatchers.Main){
soundDbFlow().collect { myInfo.value = it.toString() }
}
}
...
//The same
}
...
//The same