Intermediate: Android basics · Saving data and calling APIs · topic 32 of 50 · 35 min
Networking with Retrofit
Explanation
Talking to servers
Most apps get their data from a server through a web API: a set of URLs that return data, usually as JSON. For example, GET https://api.example.com/products/42 might return the details of product 42. HTTP is the protocol used: GET reads data, POST creates, PUT/PATCH update, DELETE removes. Retrofit, an open-source library by Square, is the most widely used way to call such APIs on Android.
Step 1: the permission
<uses-permission android:name="android.permission.INTERNET" />Step 2: describe the API as an interface
@Serializable
data class Product(val id: Int, val name: String, val price: Int)
@Serializable
data class NewReview(val productId: Int, val stars: Int, val text: String)
interface ShopApi {
@GET("products")
suspend fun products(@Query("category") category: String): List<Product> // /products?category=...
@GET("products/{id}")
suspend fun product(@Path("id") id: Int): Product // /products/42
@POST("reviews")
suspend fun addReview(@Body review: NewReview): Response<Unit> // sends JSON body
}| Annotation | Meaning |
|---|---|
| @GET("path") / @POST / @PUT / @DELETE | HTTP method and path relative to the base URL |
| @Path("id") | Fills {id} in the path |
| @Query("category") | Adds ?category=value to the URL |
| @Body | Sends the object as the request body (JSON) |
| @Header("Authorization") | Adds a header, for example a login token |
Response<Unit> in addReview gives you the full HTTP response, including the status code, instead of just the body. Use it when the server returns no body or when you need to check the code yourself.
Step 3: build Retrofit
val json = Json { ignoreUnknownKeys = true }
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/") // must end with "/"
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
val api: ShopApi = retrofit.create(ShopApi::class.java)Step 4: call it from a repository
class ProductRepository(private val api: ShopApi) {
suspend fun productsIn(category: String): List<Product> = api.products(category)
}
// In a ViewModel:
// viewModelScope.launch { val list = repository.productsIn("kurtas") }HTTPS and security
Since Android 9 (API 28), apps that target it block plain HTTP (cleartext) traffic by default; use HTTPS URLs. Never put secret API keys in the app: anyone can unpack an APK and read them. Keep secrets on your server and let the app talk to your server.
Seeing what happens: logging
OkHttp, the HTTP engine under Retrofit, has a logging interceptor that prints every request and response in Logcat. Add it only to debug builds, because logs can contain tokens and personal data.
Common mistakes
- Forgetting the INTERNET permission: requests fail with a SecurityException or "permission denied".
- A base URL without the trailing slash: Retrofit throws "baseUrl must end in /".
- Using http:// URLs: blocked as cleartext on modern Android.
- Calling the API from a composable. Go through a repository and ViewModel.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Networking with Retrofit · 1/6
Apps get data from web APIs: URLs that return JSON. Retrofit is the standard Android client.
Prefer a video by a teacher? Find free videos on “retrofit android kotlin tutorial suspend coroutines json” on YouTube ↗
In real life: Pincode lookup in a checkout form
When a user types a 6-digit PIN code in a delivery address form, the app calls the company's own address API (GET /pincodes/{pin}) through Retrofit and fills in the city and state. The call is a suspend function in a repository, started from the ViewModel when the PIN field reaches 6 digits, and the form shows "Checking..." while it waits.
@Serializable
data class PinInfo(val pin: String, val city: String, val state: String)
interface AddressApi {
@GET("pincodes/{pin}")
suspend fun lookup(@Path("pin") pin: String): PinInfo
}Practice (0/3 done)
For @GET("orders") suspend fun orders(@Query("status") s: String), what URL does orders("paid") call with base URL https://api.shop.in/?
Why should you not store a secret API key inside the app?
Using a free public test API (for example one that returns sample posts or users as JSON), create @Serializable models, a Retrofit interface with one GET by id and one GET list with a query, build Retrofit with a JSON converter, and show the results in a LazyColumn via a repository and ViewModel. Add the logging interceptor for debug builds only.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- JSON models and handling errorsSaving data and calling APIs · same module
- Offline-first apps and DataStoreSaving data and calling APIs · same module
- HTTP requests and FutureBuilderFlutter and Dart · Networking, storage and Firebase
- Browser, server, URL and HTTPFrontend Web Development · How the web works
- fetch: talking to serversJavaScript Programming (language in depth) · Asynchronous JavaScript
- HTTP requests, responses and methodsBackend Development · How the web and backends work
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which annotation fills {id} in a Retrofit path?
Answer: @Path
@Path replaces the placeholder.
What must a Retrofit base URL end with?
Answer: /
Retrofit requires a trailing slash.
What does the converter factory do?
Answer: Converts JSON to and from Kotlin objects
For example kotlinx.serialization, Moshi or Gson.
Since Android 9, plain http:// requests are…
Answer: Blocked by default (cleartext not permitted)
Cleartext HTTP is blocked by default; use HTTPS.
What does HTTP status 404 mean?Related
In https://shop.example.in/cart, which part is the domain?Related
Answer: shop.example.in
The domain names the server: shop.example.in. https:// is the protocol and /cart is the path.
What must the body of a JSON POST be?Related
Answer: A string made with JSON.stringify
fetch sends text; stringify the object.
Which part of an HTTP request carries the JSON data you send?Related
Answer: The body
The body comes after the headers and the empty line, and holds the data.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Saving data and calling APIs” before its exam: JSON models and handling errors, Offline-first apps and DataStore.
- Module 8 exam: Saving data and calling APIs · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Permissions, Material 3 and polish. 50 topics left in the course before the final exam.