4
votes

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?.

1
What did you mean by ~ "Before executing it the return value is passed to the caller"IgorGanapolsky

1 Answers

5
votes

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)