'Using Coroutine for continuous data polling and dumping to UI

My existing Android code uses AsyncTask to continuously poll data from a virtual COM port and dumps data to UI thread by overriding the onProgressUpdate() function. Something like the following:

open class ComActivity(deviceID:Int, listener: OnComRxUpdateUI) : AsyncTask<..., ..., ...>(){
  ...   
  override fun doInBackground(...) { 
  ...   //keep listening on serial port and transfer byte to buffer when available
  }

  override fun onProgressUpdate(...){
  ...  //dump to ui 
  }  
}

The above code works fine (except for some memory leak warnings here and there).

Every now and then, I see Coroutine being promoted for performing concurrent, and asynchronous operations, and I find compelled to try replacing AsyncTask with Coroutine. OTOH, I do not fully grasp the working of Coroutines as a substitute for AsyncTask. So my question is whether Coroutine is a viable replacement for my use case (continuous data polling and dumping on UI). Any good high-level (or pseudo) example out there that demonstrate this use case are welcome.



Solution 1:[1]

It is hard to assume how you are listening on a serial port in AsyncTask, whether you use some kind of listener or an infinite loop, but I guess the same result can be achieved using Flow and MVVM approach. It will look something like the following:

In Repository class:

fun listenPort(): Flow<String> = flow {
    // keep listening on serial port, use emit() function to emit data
    while (true) {
        val data = listen()
        emit(data)
    }
}

suspend fun listen() = withContext(Dispatchers.IO) {
    // listening on serial port
}

In ViewModel:

private val _state = MutableStateFlow<State>(Loading)
val state = _state

init {
    repository.listenPort()
        .onEach { data ->
            _state.value = Dump(data)
        }.launchIn(viewModelScope)
}

States:

sealed interface State
object Loading : State
data class Dump(val data: ...) : State

In Ui(Activity or Fragment):

vm.state
        .flowWithLifecycle(lifecycle)
        .onEach { state ->
            // handle all states
            if (state is Dump) {
                val data = state.data
                // use data
            }
    }.launchIn(lifecycleScope)

If you are not a fan of the approach of using MVVM architecture, you can do all of it in UI class(Activity or Fragment), which I don't recommend:

listenPort().flowWithLifecycle(lifecycle)
        .onEach { data ->
            // use data
        }.launchIn(lifecycleScope)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1