Home > Back-end >  Will collection data from Flow in viewModelScope block UI in Android Studio?
Will collection data from Flow in viewModelScope block UI in Android Studio?

Time:01-28

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

CodePudding user response:

The Code A will not block the UI thread, because the launch method does not block the current thread.

As the documentation says:

Launches a new coroutine without blocking the current thread and returns a reference to the coroutine as a [Job].

If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [Dispatchers.Default] is used.

So in your case, CodeA uses the Dispatches.Default under the hood, while CodeB uses the Dispatchers.IO

More on coroutines here

CodePudding user response:

@qki wrote right. But his answer has inaccuracy. ViewModel have viewModelScope. viewModelScope have context SuperVisorJob() Dispatchers.Main.immediatly. LatestNewsViewModel's init will be execute in main group threads, but this will not block ui thread.

CodePudding user response:

A suspend function does not block unless it breaks the convention that a suspend function must never block. Therefore, it doesn't matter that your coroutine is called from the Main dispatcher. The main thread will not be blocked by calling fetchLatestNews(), unless you have improperly composed the implementation of the function such that it actually blocks.

You usually do not need to do this like in your Code B:

viewModelScope.launch(Dispatchers.IO) {

because you usually aren't calling blocking functions at the top level of your coroutine. If you are, you can wrap those pieces in withContext(Dispatchers.IO) { }. It's generally more convenient to leave the coroutine on the Main dispatcher, since there are so many non-suspend functions in Android that require that you call them from the main thread. If you flip it around you're likely to need withContext(Dispatchers.Main) { } in more places than you would have needed the inverse, and you also incur a one frame delay before the coroutine actually starts. Also, if your coroutines interact with properties in your ViewModel, you avoid potential problems with concurrent access to the properties if you only touch them from the Main dispatcher, since it is single-threaded.

There might be exceptions where you're launching a coroutine that doesn't interact with any such Main-required functions and that directly calls blocking functions, but I think that should be rare, especially if you practice good encapsulation (see here). If you break a chunk of code out of the top level of your coroutine into its own function, you can make that separate function into a suspend function that uses withContext(Dispatchers.IO) if necessary. Then your top level coroutine will look very clean.

  •  Tags:  
  • Related