androidkotlinasync-awaitandroid-ksoap2

Kotlin - running ksoap2 as a coroutine vs async - which is better?


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() }
}


Solution

  • 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