Home > OS >  How to await Retrofit call, with Kotlin?
How to await Retrofit call, with Kotlin?

Time:09-16

I was thinking, is there any way to build your code, so you could do Retrofit calls like this:
For e.g in MainActivity:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val data = userRepository.getUsers().await()
    }

CodePudding user response:

The content of the onCreate() function is called on the main thread. You cannot await long-running actions like IO or Retrofit calls on the main thread.

You can push it to a coroutine like this. All code that must be called in sequence after the data is requested must go inside the coroutine.

You must wrap your call in try/catch when using await or a suspend function to retrieve something in Retrofit, so you can handle any IO errors that occur because the retrieve could not be completed.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    lifecycleScope.launch {
        val data = try {
            userRepository.getUsers().await()
        } catch (e: HttpException) {
            // Do something with HTTP failures here.
            return@launch
        } catch (e: Exception) {
            // Do something when connection can't be made here.
            return@launch
        }
        // Do something with data inside the coroutine here.
    }
}

Note that if you're using coroutines, you might as well define your function in your API as a suspend function instead of a function that returns a Call, so change something like

@GET("users")
fun getUsers(@Path("users") users: String): Call<List<User>>

into

@GET("users")
suspend fun getUsers(@Path("users") users: String): List<User>

and you won't need the await() call.

Alternatively, instead of coroutines, you can use a Callback, but it's messier looking code:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    userRepository.getUsers().enqueue(object: Callback<List<User>> {
        override fun onResponse(call: Call<List<User>>, response: Response<List<User>>) {
            if (response.isSuccessful) {
                val data = body!!
                // Do something with response data here.
            } else {
                // Do something with HTTP failures here.
            }
        }

        override fun onFailure(call: Call<List<User>>, t: Throwable) {
            // Do something when connection can't be made here.
        }
    })
}

Regardless of whether you use coroutines or not, you should probably doing this request in a ViewModel instead of directly in your Activity. That way the request doesn't have to be remade anew every time the user rotates the screen.

  • Related