Android Networking: Fundamentals

Sep 6 2022 · Kotlin 1.6, Android 12, Android Studio Chipmunk | 2021.2.1 Patch 1

Part 2: Implement Retrofit Basics

14. Challenge: Create Retrofit Calls

Episode complete

Play next episode

Next
About this episode

Leave a rating/review

See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 13. Implement a GET Call Next episode: 15. Add Queries to Calls

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 14. Challenge: Create Retrofit Calls

The student materials have been reviewed and are updated as of July 2022.

Transcript: 14. Challenge: Create Retrofit Calls

To practice retrofitting API calls, you’ll implement two API calls in this challenge using Retrofit. You have to change the Login request to use Retrofit, instead of the manual HttpURLConnection, and you have to implement a new API call, the getUserProfile call.

Once again, you can find the documentation for the requests in the PDF, and you can use Postman to test the API before implementing it.

The UserProfile requires two API calls to form the data, the getTasks and the getProfile. You can do this by first calling getTasks from within the RemoteApi, and then within that callback, create a nested API call.

This is a more complex call structurally, but it’s not hard to implement, however, it is something you will probably encounter in your career, so it’s useful to learn how to do it! :]

That’s it! Now pause the video, and solve the challenge! Then once you’re done, unpause the video, and compare the two solutions! Good luck! :]

Head over to the RemoteApiService.kt and add these following calls:

@POST("/api/login")
fun loginUser(@Body request: RequestBody): Call<ResponseBody>

@GET("/api/user/profile")
fun getMyProfile(@Header("Authorization") token: String): Call<ResponseBody>

These will represent the Login & Profile calls in the app. Nothing new about them, they use request and response bodies, and the profile call requires the Authorization header. Now head back to the RemoteApi.kt, to the login function, and replace the code with the following:

Just like for register, you’re sending the userData as a request body. Now implement the failure and response cases:

val body = RequestBody.create(
    MediaType.parse("application/json"), gson.toJson(userDataRequest)
)

apiService.loginUser(body).enqueue(object : Callback<ResponseBody> {
  override fun onFailure(call: Call<ResponseBody>, error: Throwable) {
    onUserLoggedIn(null, error)
  }

  override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
    val jsonBody = response.body()?.string()
    if (jsonBody == null) {
      onUserLoggedIn(null, NullPointerException("No response body!"))
      return
    }

    val loginResponse = gson.fromJson(jsonBody, LoginResponse::class.java)

    if (loginResponse == null || loginResponse.token.isNullOrEmpty()) {
      onUserLoggedIn(null, NullPointerException("No response body!"))
    } else {
      onUserLoggedIn(loginResponse.token, null)
    }
  }
})

Once again, you have to format the data into a JSON request body, and enqueue a callback for the request.

In the failure block, you simply pass the error back, and in the success block you attempt to parse the data, and send the token back to the user. Now head to the getUserProfile function, and add the following code:

getTasks { tasks, error ->
}

Because the user profile also holds the amount of notes you have, you’re first requesting the tasks or notes, and then within that callback, you can request the profile:

getTasks { tasks, error ->
  if (error != null && error !is NullPointerException) {
    onUserProfileReceived(null, error)
    return@getTasks
  }
}

In case of an error in the first call, which is not a NullPointerException you’ve passed yourself, there’s no use in requesting the rest of the profile, so you can return from this block.

If it’s an NPE, you’ll request the profile, and display it with the number of notes being zero, as there aren’t any notes available.

getTasks { tasks, error ->
  if (error != null && error !is NullPointerException) {
    onUserProfileReceived(null, error)
    return@getTasks
  }

  apiService.getMyProfile(App.getToken()).enqueue(object : Callback<ResponseBody> {
    override fun onFailure(call: Call<ResponseBody>, error: Throwable) {
      onUserProfileReceived(null, error)
    }

    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
      val jsonBody = response.body()?.string()

      if (jsonBody == null) {
        onUserProfileReceived(null, error)
        return
      }

      val userProfileResponse = gson.fromJson(jsonBody, UserProfileResponse::class.java)

      if (userProfileResponse?.email == null || userProfileResponse.name == null) {
        onUserProfileReceived(null, error)
      } else {
        onUserProfileReceived(
            UserProfile(userProfileResponse.email, userProfileResponse.name, tasks.size), null
        )
      }
    }
  })
}

You once again return the error in case of a failure, and attempt to parse the data in the response callback. If the data is invalid, you return the appropriate error back, otherwise, you create a UserProfile object, and pass it to the callback. And as with all requests, remove the runOnUiThread, in respective locations, such as the LoginActivity.

runOnUiThread {
}

That’s it, now run the project, and log in if needed. Then head over to your profile, to check if everything is displayed correctly!