Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin Coroutines how to achieve to call api in right way

Hey I want to call api from object class. I am new in Coroutines. I tried some code, but i am not sure is it correct way of doing it or not.

  1. Inside LoginHelper there is function called logout have more that one function. I want to excute api call first. then i want to excute other function inside logout.

  2. In Mainactivity I am calling LoginHelper.logout it will finish then i need to excute other line. But i don't want to make suspend function because it's using other place as well.

Also i got a errorProcess:

com.dimen.app, PID: 12496
    android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1605)

Session.kt

interface Session{
    @DELETE("/session/delete")
    fun deleteSession(): Call<Void>
}

SessionRepository.kt

suspend fun deleteSession(): RequestResult<Void> {
        return apiCall(api.deleteSession())
}

RequestResult is a Sealed Class

sealed class RequestResult<out T : Any> {
    data class Success<out T : Any>(): RequestResult<T>
    data class Error(): RequestResult<Nothing>()
    fun result(success: (data: T?) -> Unit),error: (error: Error) -> Unit)
}

MainActivity.kt

private fun setupLogout() {
    logoutButton.setOnClickListener {
        LoginHelper.logout() // need to wait untill this finish
        // more logic here....
    }
}

LoginHelper.kt

object LoginHelper {

    fun logout() {
        logD("logout")
        deleteSession() // need to wait untill this finish and then excute more function....
    } 

    private fun deleteSession() {
       runBlocking{
        apiCall.deleteSession().execute()
       }
    }
}
like image 698
Vivek Modi Avatar asked Nov 23 '25 19:11

Vivek Modi


1 Answers

Never use runBlocking in an Android app unless you know exactly what you're doing. It's the wrong choice 99% of the time because it defeats the purpose of using coroutines. Blocking means the current thread waits for the coroutine to run its asynchronous code. But you cannot block the main thread because that freezes the UI.

Since your LoginHelper is an object or singleton, it needs its own CoroutineScope if it's going to launch coroutines.

You can make deleteSession() a suspend function so it can call the api.deleteSession() suspend function.

You can make logout() launch a coroutine to sequentially delete the session and subsequently perform other tasks. And you can make it return the launched Job so other classes can choose whether or not to simply start the logout, or to start and wait for the logout in a coroutine.

object LoginHelper {
    private val scope = CoroutineScope(SupervisorJob() + CoroutineName("LoginHelper"))

    fun logout(): Job = scope.launch {
        logD("logout")
        deleteSession()
        // .... more functions that happen after deleteSession() is complete
    }

    private suspend fun deleteSession() {
        Tokenclass.getToken()?.let {
            logE("token ::-> $it")
            apiCall.deleteSession(it).execute()
        }
    }
}

If you want the outside class to be able to wait for the logout to complete, it can call join() on the returned Job in its own coroutine, for example:

logoutButton.setOnClickListener {
    lifecycleScope.launch {
        LoginHelper.logout().join()
        // more logic here....
    }
}

If you don't need to wait for it in the activity, you don't need to start a coroutine, and you don't need to call join().

like image 159
Tenfour04 Avatar answered Nov 25 '25 10:11

Tenfour04