Android and Kotlin noob here - I have an app calling a SOAP web service. Right now the calls are made using Thread and communication is working. I would like to move that to either Kotlin coroutines or Android Async tasks, my question is - which is better in this case?
I've tried creating a coroutine call based on this article https://proandroiddev.com/how-to-make-sense-of-kotlin-coroutines-b666c7151b93, basically adapting this pattern:
fun main() = runBlocking {
val deferredResult = async {
delay(1000L)
"World!"
}
println("Hello, ${deferredResult.await()}")
}
When I put the web service call in the coroutine async, then Android Studio highlights the HttpTransportSE call method (http://www.kobjects.org/ksoap2/doc/api/org/ksoap2/transport/HttpTransportSE.html) with the following warning:
Inappropriate blocking method call. Reports thread-blocking method calls found in a code fragment where a thread should not be blocked"
My understanding of this message is that the call made by the HttpTransportSE blocks the thread, therefore we lose the advantage of using coroutines, and I should just stick to the Async task. Is that interpretation correct, or is there a way of wrapping the call with coroutine that would work more properly?
Below is my code (it communicates with the web services, but because of the warning I have a feeling it's not a proper way to do this):
fun callWebService(
...
): String {
val defferedResult: Deferred<String> = GlobalScope.async {
try {
...
val envelope = SoapSerializationEnvelope(SoapEnvelope.VER12)
...
val androidHttpTransport = HttpTransportSE(URL)
androidHttpTransport.debug = true
androidHttpTransport.call("$NAMESPACE/$methodName", envelope) //this is where I get the warning
val resultData = envelope.response
webResponse = "$resultData"
...
}
return@async webResponse
}
return runBlocking { defferedResult.await() }
}
I think I have figured it out, thanks to this article
it comes down to running in the background usingDispatchers.IO
the SOAP call can be regular function, no need for Deffered, etc.
I am using MVVM model with repository, so all the background work is happening on the Repository
level, triggered by button press in the Fragment
launched in the respective ViewModel
, with limited scope
my SOAP call now looks like this
fun callWebService(...): String {
try {
...
val envelope = SoapSerializationEnvelope(SoapEnvelope.VER12)
...
val androidHttpTransport = HttpTransportSE(URL)
androidHttpTransport.debug = true
androidHttpTransport.call("$NAMESPACE/$methodName", envelope)
val resultData = envelope.response
webResponse = "$resultData"
...
}
return webResponse
}
in the Repository
I have following function - note the suspend
and Dispatchers.IO
suspend fun insertAndSend(task: Task) {
withContext(Dispatchers.IO) {
val response = callWebService(processedTask)
processedTask.status = response
taskDao.update(processedTask)
}
}
in the ViewModel
I call the Repository
function within the ViewModelScope
//defined in ViewModel class
fun insertAndSend(task: Task) = viewModelScope.launch { repository.insertAndSend(task) }
which is triggered in the respective Fragment
by pressing a button