Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning value from normal function which called suspend function using Kotlin Coroutine

Hi I am using Kotlin coroutine library in a project.

The below method calls a suspend function which return a boolean value.

fun isNetworkAvailable(context: Context?): Boolean {
            //return checkNetworkReachability(context)
            var isNetworkAvailable = false
            GlobalScope.launch(Dispatchers.Default) {
                isNetworkAvailable = GlobalScope.async<Boolean> {
                    checkNetwork()

                }.await()
            }
            return isNetworkAvailable

        }

Here checkNetwork is the suspend function. Before executing it the return value is passed to the caller (View/Activity). How could I achieve without making the "isNetworkAvailable" as suspend?.

Inside the checkNetwork method, to check reach-ability calling network call as like below.

private suspend fun checkNetwork() : Boolean {
            val value = GlobalScope.async<Boolean> {
                val isEastReachable = async { checkEastReachable() }
                if (!isEastReachable.await()) {
                    checkWestReachable()
                } else {
                    true
                }
            }

            return value.await()
        }

And the sub-methods are

private suspend fun checkEastReachable(): Boolean = coroutineScope {

                withContext(Dispatchers.Default) {
                    repository.networkManager.callReachableEast()
                }
            }



private suspend fun checkWestReachable(): Boolean = coroutineScope {

                withContext(Dispatchers.Default) {
                    repository.networkManager.callReachableWest()
                }
            }

The sub-suspend methods are calling a web service using retrofit. As it would return a boolean, I made it as an synchronous .execute() call.

fun callReachableEast(): Boolean {
        return try {
            val requestCall =
                ApiService.create("eastApi").getReachabilityEast()
            requestCall.execute().isSuccessful
        } catch (exception: Exception) {
            false
        }
    }

    fun callReachableWest(): Boolean {
        return try {
            val requestCall =
                ApiService.create("westApi").getReachabilityWest()
            return requestCall.execute().isSuccessful
        } catch (exception: Exception) {
            false
        }
    }

I have gone through the below links

https://kotlinlang.org/docs/reference/coroutines/composing-suspending-functions.html

https://proandroiddev.com/async-operations-with-kotlin-coroutines-part-1-c51cc581ad33

and some more.

Repeating my question, How could I achieve without making the "isNetworkAvailable" as suspend?.

like image 684
Karthikeyan Ve Avatar asked May 23 '19 13:05

Karthikeyan Ve


People also ask

Can you run a suspend function outside of a coroutine or another suspending function?

The syntax of a suspending function is similar to that of a regular function except for the addition of the suspend keyword. It can take a parameter and have a return type. However, suspending functions can only be invoked by another suspending function or within a coroutine.

What is suspend function in Kotlin coroutines?

If you notice the functions closely, they can be used to resume the coroutine with a return value or with an exception if an error had occurred while the function was suspended. This way, a function could be started, paused, and resume with the help of Continuation. We just have to use the suspend keyword.

What is the difference between suspending and blocking in Kotlin?

BLOCKING: Function A has to be completed before Function B continues. The thread is locked for Function A to complete its execution. SUSPENDING: Function A, while has started, could be suspended, and let Function B execute, then only resume later. The thread is not locked by Function A.

How do you wait for Suspend to finish Kotlin?

To wait for a coroutine to finish, you can call Job. join . join is a suspending function, meaning that the coroutine calling it will be suspended until it is told to resume. At the point of suspension, the executing thread is released to any other available coroutines (that are sharing that thread or thread pool).


1 Answers

If you can't make isNetworkAvailable a suspend function, then it will be a blocking function. This means, any code calling isNetworkAvailable will block as well, or you'd need to change this function's signature to have a callback instead.

First, let's look at the blocking version. There is a special coroutine-builder that is suited for bridging from the suspendable world into the regular/blocking world. It is called runBlocking:

fun isNetworkAvailable(context: Context?): Boolean = runBlocking {
    checkNetworkReachability(context)
}

...

val isAvailable = isNetworkAvailable(activity)
if (isAvailable) { ... }
...

If you'd like to change its signature and have a callback instead of a return-value:

fun CoroutineScope.isNetworkAvailable(context: Context?, callback: (Boolean) -> Unit) { 
    launch {
        callback(checkNetworkReachability(context))
    }
}

...
scope.isNetworkAvailable(activity) { isAvailable ->
    if (isAvailable) { ... }
}

(code may have typos)

like image 181
Streets Of Boston Avatar answered Oct 18 '22 16:10

Streets Of Boston