Points to understand :
If you are learning Android development, Retrofit code can look confusing at first:
private fun getPost() {
RetrofitClient.apiService
.getPost()
.enqueue(object : Callback<Post> {
override fun onResponse(
call: Call<Post>,
response: Response<Post>
) {
if (response.isSuccessful) {
val post = response.body()
tvTitle.text = post?.title
tvBody.text = post?.body
} else {
Toast.makeText(
this@day2,
"Something went wrong",
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(
call: Call<Post>,
t: Throwable
) {
Toast.makeText(
this@day2,
"Error: ${t.message}",
Toast.LENGTH_LONG
).show()
}
})
}
There are functions, callbacks, objects, Retrofit, Response, Call, Throwable, null safety and UI updates—all inside a few lines of code.
Instead of memorizing this code, let’s understand what Android is actually doing.
1. First Understand the Goal
Our Android application wants data from a server.
Imagine the server contains this JSON:
{
"userId": 1,
"id": 1,
"title": "Hello Android",
"body": "I am learning Retrofit"
}
We want to download this data and display:
Title → Hello Android
Body → I am learning Retrofit
The basic flow is:
Android App
↓
Send API Request
↓
Internet
↓
Server
↓
JSON Response
↓
Android App
↓
Convert JSON into Kotlin Object
↓
Display Data on Screen
Retrofit helps us perform this communication.
2. What is Retrofit?
Retrofit is a popular HTTP client library for Android.
In simple terms:
Retrofit helps our Android application communicate with a web server or REST API.
Without Retrofit, we would have to manually handle many details related to HTTP requests, URLs, response handling and data conversion.
With Retrofit, we can describe an API like this:
interface ApiService {
@GET("posts/1")
fun getPost(): Call<Post>
}
Now Retrofit knows that getPost() should make a GET request to:
posts/1
3. Understanding the Post Data Class
Suppose the server returns:
{
"userId": 1,
"id": 1,
"title": "Hello Android",
"body": "I am learning Retrofit"
}
We create a Kotlin data class:
data class Post(
val userId: Int,
val id: Int,
val title: String,
val body: String
)
Retrofit’s converter can transform the JSON response into this Kotlin object.
Conceptually:
SERVER JSON
{
"userId": 1,
"id": 1,
"title": "Hello Android",
"body": "I am learning Retrofit"
}
↓
JSON Converter
↓
KOTLIN OBJECT
Post(
userId = 1,
id = 1,
title = "Hello Android",
body = "I am learning Retrofit"
)
This concept is extremely important.
The server sends JSON.
Our Android code works with a Kotlin object.
4. Understanding getPost()
Now let’s examine our function.
private fun getPost() {
}
This is simply a Kotlin function.
Its responsibility is:
Get a Post from the server and display it on the screen.
You can think about functions based on their responsibility:
fun loginUser()
fun registerUser()
fun getProducts()
fun getStudents()
fun getPost()
In our example:
getPost()
means:
Start the process of getting a post.
5. Understanding RetrofitClient.apiService
Next we have:
RetrofitClient.apiService
Break this into two parts:
RetrofitClient
↓
apiService
RetrofitClient normally contains our Retrofit configuration.
For example:
object RetrofitClient {
private const val BASE_URL =
"https://jsonplaceholder.typicode.com/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService: ApiService =
retrofit.create(ApiService::class.java)
}
Here:
BASE_URL
tells Retrofit where the server is.
And:
apiService
gives us access to functions declared inside:
ApiService
6. Understanding .getPost()
Now look at:
RetrofitClient.apiService.getPost()
Our ApiService contains:
@GET("posts/1")
fun getPost(): Call<Post>
So calling:
.getPost()
creates a request for that API.
But there is an important concept here.
getPost() does not immediately return the Post data.
It returns:
Call<Post>
Think about Call<Post> as:
A request capable of fetching a Post
It is similar to placing an order.
getPost()
does NOT mean:
"Here is your Post."
Instead it means:
"Here is the request for getting your Post."
7. Understanding enqueue()
Next comes:
.enqueue(...)
This is one of the most important parts.
RetrofitClient.apiService
.getPost()
.enqueue(...)
enqueue() tells Retrofit to execute the network request asynchronously.
In beginner-friendly language:
Send the request without freezing the Android application, and inform me when the result arrives.
Think about ordering food.
You place an order
↓
Restaurant starts preparing food
↓
You don't stand frozen at the counter
↓
You can do other things
↓
Restaurant informs you when order is ready
Network requests work similarly.
The server may take:
100 ms
500 ms
2 seconds
5 seconds
to respond.
Android should not freeze the UI while waiting.
8. Why Do We Need a Callback?
Here comes the scary-looking part:
object : Callback<Post>
Don’t worry about the syntax yet.
First understand the requirement.
Retrofit needs to know:
What should I do when the network request finishes?
There are two important possibilities:
Request
|
├──── HTTP response received
|
└──── Network call failed
Retrofit gives us two callback functions:
onResponse()
and:
onFailure()
Conceptually:
Retrofit:
"If I receive an HTTP response,
I will call onResponse()."
"If the network request itself fails,
I will call onFailure()."
9. Understanding object : Callback
Now we can understand:
object : Callback<Post>
We are creating an anonymous object that implements Retrofit’s:
Callback<Post>
The callback requires us to provide implementations for:
onResponse()
and:
onFailure()
So our structure becomes:
.enqueue(object : Callback<Post> {
override fun onResponse(...) {
}
override fun onFailure(...) {
}
})
For beginners, remember it as:
enqueue
↓
Wait for Retrofit
↓
┌─────────────────┐
│ │
onResponse onFailure
10. Understanding onResponse()
Now look at:
override fun onResponse(
call: Call<Post>,
response: Response<Post>
) {
}
Retrofit calls this function when an HTTP response is received.
The most important thing here is:
response
The response contains information returned from the server.
Conceptually:
Android
|
| Request
↓
Server
|
| HTTP Response
↓
response
11. Understanding response.isSuccessful
Inside onResponse() we have:
if (response.isSuccessful) {
}
Why do we need this?
Because receiving an HTTP response doesn’t necessarily mean the request succeeded.
For example:
200 OK
201 Created
400 Bad Request
401 Unauthorized
404 Not Found
500 Internal Server Error
Retrofit can receive all of these as HTTP responses.
Therefore:
response.isSuccessful
checks whether the HTTP status code represents a successful response.
Generally:
2xx → Successful
4xx → Client error
5xx → Server error
So:
if (response.isSuccessful)
essentially means:
Did the server successfully process our request?
12. Understanding response.body()
If the response is successful:
val post = response.body()
body() contains the actual data returned by the server after conversion.
For example, the server returns:
{
"userId": 1,
"id": 1,
"title": "Hello Android",
"body": "Learning Retrofit"
}
Retrofit converts it into:
Post(
userId = 1,
id = 1,
title = "Hello Android",
body = "Learning Retrofit"
)
Therefore:
val post = response.body()
means:
Take the converted Post object from the server response and store it in
post.
Now we can access:
post?.id
post?.userId
post?.title
post?.body
13. Why Do We Use ?.
You will notice:
post?.title
instead of:
post.title
This is Kotlin’s null-safety system.
response.body() can potentially return null.
Therefore:
val post = response.body()
may contain either:
Post object
or:
null
The safe-call operator:
?.
means:
If
postis not null, access its property. Otherwise return null safely.
So:
post?.title
means:
If post exists
↓
get title
If post is null
↓
don't access title
14. Displaying the Data
Now we have:
tvTitle.text = post?.title
tvBody.text = post?.body
Suppose:
post?.title
contains:
Hello Android
Then:
tvTitle.text = post?.title
displays:
Hello Android
inside the TextView.
The complete data flow is:
Server
↓
JSON
↓
Retrofit
↓
Post Object
↓
post.title
↓
tvTitle
↓
Android Screen
15. What Happens in the else Block?
We have:
if (response.isSuccessful) {
// display data
} else {
Toast.makeText(
this@day2,
"Something went wrong",
Toast.LENGTH_SHORT
).show()
}
The else block means:
The server sent us an HTTP response, but the HTTP status indicates that the request wasn’t successful.
Examples could include:
400 Bad Request
401 Unauthorized
404 Not Found
500 Internal Server Error
We therefore display:
Something went wrong
16. Understanding onFailure()
Now look at:
override fun onFailure(
call: Call<Post>,
t: Throwable
) {
}
onFailure() is different from an unsuccessful HTTP response.
It generally means the network call itself failed before Retrofit received a usable HTTP response.
Possible causes include:
No internet connection
Connection timeout
DNS problem
Server unreachable
Connection interrupted
t represents the failure.
We can access:
t.message
to obtain information about what happened.
Therefore:
Toast.makeText(
this@day2,
"Error: ${t.message}",
Toast.LENGTH_LONG
).show()
displays the error to the user.
17. onResponse vs onFailure — Very Important
A common beginner mistake is thinking:
onResponse = Success
onFailure = Error
That isn’t completely correct.
The better mental model is:
API REQUEST
|
┌───────────┴───────────┐
↓ ↓
HTTP response Network call
received failed
| |
onResponse() onFailure()
|
┌─────┴─────┐
↓ ↓
2xx 4xx/5xx
| |
Success HTTP Error
For example:
200 → onResponse()
404 → onResponse()
500 → onResponse()
No Internet → onFailure()
Timeout → onFailure()
Inside onResponse() we therefore check:
response.isSuccessful
18. Complete Flow of Our Code
Now let’s connect everything.
getPost()
↓
RetrofitClient
↓
apiService
↓
getPost()
↓
Call<Post>
↓
enqueue()
↓
Network Request
↓
SERVER
↓
What happened?
|
├───────────────┐
↓ ↓
HTTP Response Network Failure
↓ ↓
onResponse() onFailure()
↓
isSuccessful?
|
┌──┴──┐
↓ ↓
YES NO
↓ ↓
body() Show Error
↓
Post Object
↓
title + body
↓
TextViews
↓
SCREEN
If you understand this diagram, you understand the basic Retrofit callback flow.
19. Read the Complete Code Like English
Now let’s translate the original Kotlin code into normal English.
private fun getPost()
Create a function whose responsibility is getting a post.
RetrofitClient.apiService
Get access to our configured API service.
.getPost()
Create the API request for getting the post.
.enqueue()
Execute the request asynchronously.
object : Callback<Post>
Provide instructions for what Retrofit should do when the network operation finishes.
onResponse()
An HTTP response was received.
response.isSuccessful
Check whether the HTTP response represents success.
response.body()
Extract the converted Post object.
post?.title
Get the title safely.
post?.body
Get the body safely.
tvTitle.text
Display the title on the screen.
tvBody.text
Display the body on the screen.
else
The server responded, but the HTTP response wasn’t successful.
onFailure()
The network request itself failed.
20. Don’t Learn Retrofit by Memorizing Code
Beginners often try to memorize this:
RetrofitClient.apiService
.getPost()
.enqueue(object : Callback<Post> {
That is the wrong approach.
Instead, remember the architecture:
ANDROID
↓
API REQUEST
↓
RETROFIT
↓
INTERNET
↓
SERVER
↓
JSON
↓
RETROFIT
↓
KOTLIN OBJECT
↓
UI
Once this flow is clear, the Kotlin code starts making sense.
21. Beginner Exercise
Before moving to another API, try modifying the current example.
Add another TextView:
tvId.text = post?.id.toString()
Now display:
Post ID
Title
Body
Then try changing:
@GET("posts/1")
to:
@GET("posts/2")
Run the application again and observe how the server returns a different post.
This is a simple way to understand that Android isn’t creating this data—the data is coming from the server.
22. Final Mental Model
Whenever you see Retrofit callback code, think:
1. Which API am I calling?
2. What type of data am I expecting?
3. Send the request.
4. Wait without blocking the UI.
5. Did I receive an HTTP response?
6. Was that response successful?
7. Extract the response body.
8. Convert/use the Kotlin object.
9. Update the UI.
10. Handle failures.
The most important concept is:
Android sends a request → server sends JSON → Retrofit converts it into a Kotlin object → our application uses that object to update the UI.
Once this flow is clear, Retrofit stops looking like complicated syntax and starts looking like a predictable sequence of operations.
What’s Next?
After understanding this callback-based example, the next topics to learn are:
Retrofit + Coroutines → Repository → ViewModel → StateFlow/LiveData → UI
That is the point where we move from a simple beginner Retrofit example toward production-style Android architecture.
SoftSence Academy — Android Development Learning Series
