Unit 3 - Practice Quiz
1 What is Retrofit primarily used for in Android development?
2 Which company is the creator of the Retrofit library?
3
What is the main purpose of the Retrofit.Builder class?
4
To integrate Retrofit into a Gradle-based Android project, which section of the build.gradle file do you add its dependency to?
5
In Retrofit, what does the @GET annotation signify?
6 How are API endpoints defined in Retrofit?
AndroidManifest.xml file
7
What is the purpose of the @Path annotation in a Retrofit interface method?
8
If you want to add a URL parameter like ?sort=new, which Retrofit annotation would you use?
@Header
@Query
@Body
@Path
9 Which of the following is a popular JSON parsing library often used with Retrofit as a converter?
10 In the context of using Retrofit with a JSON API, what is the role of a data class (or POJO)?
11
What is the purpose of GsonConverterFactory.create() when building a Retrofit instance?
12 Which Android UI component is most commonly used to display a list of items fetched from an API?
13 To automatically parse a JSON object into a Kotlin data class, the variable names in the data class should typically match what?
14
Which method on a Retrofit Call object is used to execute a network request asynchronously?
execute()
run()
enqueue()
cancel()
15 What is the main reason for making network requests asynchronously in an Android app?
16 When using Kotlin Coroutines with Retrofit, what keyword is added to an interface method to make it a non-blocking function?
await
launch
suspend
async
17 What is the most common and scalable way to add a dynamic authentication token to every request made with Retrofit?
18 Which HTTP header is conventionally used to send an API token for authentication, often prefixed with "Bearer"?
Accept
Content-Type
User-Agent
Authorization
19
In a Retrofit API interface, the @Header annotation is used for what purpose?
20 Which annotation is used to send a complete object as the request body, typically for an HTTP POST or PUT request?
@Body
@Query
@Field
@Path
21
When configuring a Retrofit.Builder, you add multiple Converter.Factory instances, first GsonConverterFactory and then MoshiConverterFactory. Which converter will Retrofit attempt to use first for response parsing?
MoshiConverterFactory).
GsonConverterFactory).
IllegalStateException at build time.
22
You are setting up a Retrofit instance. Consider the following code:
kotlin
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService = retrofit.create(ApiService::class.java)
What essential component is missing for this instance to make a successful network request to https://api.example.com/users?
OkHttpClient.
CallAdapter.Factory.
baseUrl().
Executor.
23
In the context of Retrofit, what is the primary role of defining an interface with annotations like @GET and @POST?
24
Why would a developer explicitly provide a custom OkHttpClient to the Retrofit.Builder using .client(myOkHttpClient)?
OkHttpClient.
25
Given the Retrofit interface method:
kotlin
@GET("group/{id}/users")
fun groupList(@Path("id") groupId: Int, @Query("sort") sort: String): Call<List<User>>
If you call this method with groupList(42, "desc"), what will be the resulting relative URL?
26
When sending data in the body of a POST request, what is the key difference between using the @Body and @Field annotations in a Retrofit interface?
@Body sends a single object serialized as the request body (e.g., as JSON), while @Field is used for sending individual form-urlencoded key-value pairs.
@Body and @Field are interchangeable and achieve the same result.
@Field is for JSON objects, and @Body is for raw text.
@Body can only be used with GET requests, whereas @Field is for POST requests.
27
Your app needs to send a unique, dynamically generated X-Request-ID header with each API call. Which Retrofit annotation is the most appropriate for this purpose when the ID changes for every call?
@Header as a method parameter.
@Headers at the method level.
@HeaderMap as a method parameter.
@Path as a method parameter.
28
You need to make a POST request to an endpoint that expects data in application/x-www-form-urlencoded format. Which combination of annotations is required on your Retrofit interface method?
@PUT, @Multipart, and @Part.
@POST and @QueryMap.
@POST and @Body.
@POST, @FormUrlEncoded, and one or more @Field annotations.
29
If you use the @Url annotation in a Retrofit interface method parameter, what is a key requirement for the endpoint definition in the annotation (e.g., @GET)?
@GET annotation's value must be empty.
@GET annotation must contain a full URL.
@Path annotation.
@GET annotation must define at least one path parameter.
30
You are using GsonConverterFactory and your API returns a JSON object. If a field exists in the JSON response but is not defined in your corresponding Kotlin data class, what is the default behavior of Gson?
NullPointerException when trying to create the object.
onFailure callback will be triggered.
JsonParseException.
31
The API returns a JSON with a field named "first_name". Your Kotlin data class has a property named firstName. How can you correctly map this field using Gson without changing the property name in your data class?
TypeAdapter.
@SerializedName("first_name") annotation on the firstName property.
@Field("first_name") annotation on the firstName property.
32
An API endpoint /users returns a JSON array of user objects, like [ { "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" } ]. What should be the return type of the corresponding method in your Retrofit interface?
Call<User[]>
Call<List<User>>
Call<Map<String, User>>
Call<User>
33
What is the most appropriate use case for creating a custom Gson TypeAdapter and registering it with your Retrofit instance?
34
In Retrofit, which statement correctly compares call.execute() and call.enqueue()?
execute() is synchronous and blocks the current thread, so it must be called from a background thread. enqueue() is asynchronous and safely executes the request on a background thread, delivering results to the main thread via callbacks.
execute() is asynchronous and should be used on the main thread; enqueue() is synchronous and must be used on a background thread.
execute() returns a Response object directly, while enqueue() returns void.
enqueue() allows for cancellation while execute() does not.
35
What is the primary advantage of defining a Retrofit service method as a suspend fun for use with Kotlin Coroutines, compared to the traditional Call<T> with enqueue()?
36
When using call.enqueue(callback), if the server responds with an HTTP status code of 404 (Not Found), which part of the Callback will be executed?
onResponse() method, where response.body() will be null and isSuccessful() will be true.
onResponse() method, where response.isSuccessful() will be false.
onFailure() method, with an HttpException.
onFailure() method, with an IOException.
37
When using a suspend function in a Retrofit interface, how should you handle potential network errors like a timeout or no internet connectivity?
response.isSuccessful() after the call.
null value.
try-catch block and catching IOException or other relevant exceptions.
38
What is the most common and maintainable way to add an Authorization: Bearer <token> header to every request made through a single Retrofit instance?
OkHttp Interceptor that adds the header to each outgoing request.
baseUrl.
@Header("Authorization") parameter to every single method in your API interface.
39 Your application uses an OAuth 2.0 access token that expires. When a request fails with a 401 Unauthorized error, you need to use a refresh token to get a new access token and then retry the original request. Which OkHttp component is best suited for implementing this logic?
Interceptor.
Authenticator.
Converter.Factory.
CallAdapter.Factory.
40
You need to add a static API key as a query parameter (e.g., ?api_key=YOUR_KEY) to all requests made by Retrofit. Which is the most efficient and cleanest approach?
@Url annotation.
@Query("api_key") apiKey: String to every interface method.
OkHttp Interceptor to intercept the request, retrieve its URL, and append the new query parameter.
@GET annotation.
41
You have configured a Retrofit instance with two custom Converter.Factory implementations, FactoryA and FactoryB, added in that order: retrofitBuilder.addConverterFactory(FactoryA).addConverterFactory(FactoryB). FactoryA can handle TypeX but returns null from its responseBodyConverter method for TypeY. FactoryB can handle TypeY. If you make an API call defined as fun getData(): Call<TypeY>, what will be the outcome?
IllegalArgumentException because FactoryA was checked first and couldn't handle TypeY.
FactoryA since it returned null for TypeY and will successfully use FactoryB to convert the response.
TypeY.
NullPointerException when Retrofit attempts to use the null converter returned by FactoryA.
42
In a multithreaded application, multiple concurrent API calls fail with a 401 Unauthorized error, triggering your custom Retrofit Authenticator. A naive implementation of the authenticate() method immediately calls the token refresh endpoint. What is the primary race condition this approach creates, and what is the correct way to solve it?
OkHttpClient for the refresh call.
synchronized block to ensure only one thread refreshes the token while others wait for the result.
43
Consider the following Kotlin Coroutines code within a ViewModel: viewModelScope.launch { val userDeferred = async { api.getUser() }; val postsDeferred = async { api.getPosts() }; try { val user = userDeferred.await(); val posts = postsDeferred.await(); } catch (e: Exception) { // handle error } }. If the api.getPosts() call fails with an IOException but api.getUser() completes successfully before the failure, what is the state of the viewModelScope's Job and what happens to the result of api.getUser()?
Job remains active, the exception is caught, and the user variable will hold the valid user data.
viewModelScope crashes, and the application must be restarted to recover.
async blocks are independent; userDeferred.await() will succeed, and only postsDeferred.await() will throw, allowing partial data processing.
Job is cancelled, and the result of getUser() is lost as the try-catch block catches the exception from the failing await().
44
You need to implement a file upload using a @Multipart request. The API requires a JSON object and a file to be sent in the same request. How should you define the RequestBody for the JSON part to ensure it is correctly formatted with Content-Type: application/json?
@Multipart: @Part("data") data: MyDataClass
@Part("data") data: MyDataClass
RequestBody: @Part("data") data: RequestBody where the body is created with RequestBody.create("application/json; charset=utf-8".toMediaType(), jsonString)
@Part("data") data: String
45
An API endpoint returns a JSON array of 'events'. Each event object has a "type" field ("message" or "image") and other fields that differ based on the type. How would you model this polymorphic data using Moshi and a custom JsonAdapter.Factory for seamless parsing into a sealed class Event?
@JsonClass(generator = "sealed:type") on the sealed class and @Json(name = ...) on the subclasses to automatically delegate parsing based on the "type" field.
"type" field for each element.
@Json(name = ...).
Map<String, Any> to receive the data and then manually cast it based on the "type" field.
46
You have implemented a Retrofit Authenticator to refresh a JWT. The token refresh endpoint itself is protected and requires the expired token to be passed for validation. What critical step must you take when creating the OkHttpClient for the refresh API call inside the authenticate() method to avoid an infinite loop?
.execute() instead of .enqueue().
"X-Is-Refresh-Call: true", which the Authenticator checks to ignore 401s.
OkHttpClient is the cleaner, recommended approach.
OkHttpClient instance for the refresh call that does not have the Authenticator attached.
47
A Retrofit service method is defined with a dynamic URL: suspend fun getFile(@Url fileUrl: String): ResponseBody. The base URL of the Retrofit instance is https://api.example.com/v1/. If you call this method with getFile("images/logo.png"), what will be the final request URL, and why?
images/logo.png - The @Url parameter completely overrides the base URL, leading to an invalid request.
https://api.example.com/images/logo.png - The relative path is resolved against the host of the base URL, ignoring its path segments.
https://api.example.com/v1/images/logo.png - The relative path is resolved against the base URL.
IllegalArgumentException because a full, absolute URL is required for @Url.
48
When defining a Retrofit service interface with suspend functions, what is the fundamental difference in error handling between a function returning Response<T> versus one returning just T when the server responds with an HTTP error code like 404 or 500?
HttpException, but with Response<T> you can inspect the error body before it throws.
T throws an HttpException which must be caught, while Response<T> returns a response object with isSuccessful = false and does not throw.
T will result in a null value for the object, while Response<T> will contain an error code.
try-catch block to handle network failures.
49
An API requires you to send a DELETE request with a JSON body. Which combination of Retrofit annotations would you use to define this service method?
@HTTP(method = "DELETE", path = "items/{id}", hasBody = true) and @Body body: RequestBody
@DELETE does not support a request body; you must use @POST and a custom header like X-HTTP-Method-Override: DELETE.
@DELETE("items/{id}") and @Body body: RequestBody
@FormUrlEncoded and @DELETE("items/{id}") with @Field annotations for the body.
50
An API for a specific field sometimes returns a JSON object {"key":"value"} and other times returns an empty JSON array [] to signify 'no data'. A standard Gson/Moshi parser would crash with a type mismatch. What is the most robust way to handle this using a custom adapter?
JsonAdapter that, in its fromJson method, checks the next token. If it's BEGIN_ARRAY, it consumes the array and returns a null or default object. If it's BEGIN_OBJECT, it proceeds with normal object parsing.
Any in your data class and perform instanceof checks at runtime.
[] with null before it reaches the parser.
51
What is the primary purpose of a Retrofit CallAdapter.Factory, and in which scenario is creating a custom one absolutely necessary?
Call<T> object into another type, such as Single<T> for RxJava or a custom Result<T> wrapper. It's necessary when not using Call or coroutine suspend functions.
52
If your Authenticator's authenticate() method returns null, what is the direct consequence for the original request that received the 401 and for any other requests that were queued by OkHttp while the authenticator was running?
HttpException, which can be caught by the application.
53
Consider a Retrofit method: fun search(@Query("q") query: String?, @Query("sort") sort: String = "asc"). How does Retrofit handle a null value passed for the query parameter, and how does it handle the default value for the sort parameter if it's not provided by the caller?
q= (an empty value) for the null query. The sort parameter will be omitted if not specified.
null or the default, respectively.
q will be omitted from the URL if its value is null. The sort parameter will be included with the value "asc" if not specified.
NullPointerException for the null query. Default values are not supported and must be passed explicitly.
54
When using the Gson converter, you have a data class with a lateinit var property. The corresponding field is missing from the JSON response. What will be the outcome during deserialization?
JsonSyntaxException because a non-nullable property could not be set.
UninitializedPropertyAccessException only when it's accessed.
null to the property, causing an UninitializedPropertyAccessException when it's accessed later.
String).
55
You are using Flow with Retrofit to implement a real-time search feature. A code snippet in your ViewModel is: searchQuery.debounce(300).flatMapLatest { query -> flow { emit(api.search(query)) } }.catch { ... }. If a user types quickly, causing a new query to be emitted while a previous api.search() call is still in flight, what does flatMapLatest ensure?
56
If you add a HttpLoggingInterceptor as a network interceptor (addNetworkInterceptor) versus an application interceptor (addInterceptor), how does the logged Request object differ when a request is redirected by the server (e.g., a 301 response)?
57
What is the key difference between using @QueryMap with a Map<String, String> and using multiple @Query annotations in a Retrofit service method?
@QueryMap is for POST requests while @Query is for GET requests.
@QueryMap allows for a dynamic number of query parameters to be added at runtime, whereas @Query is for a fixed, known set of parameters defined in the method signature.
@QueryMap is just syntactic sugar for multiple @Query annotations.
@Query parameters are URL-encoded, but @QueryMap parameters are not.
58 When securing API keys used by your Retrofit/OkHttp client in an Android app, which method provides the strongest protection against static analysis and reverse engineering of the APK?
strings.xml and decrypting it at runtime.
build.gradle and accessing it via BuildConfig.API_KEY.
59
You make a suspend Retrofit call inside a coroutine launched with supervisorScope. If the network call fails with an IOException, how does this exception affect the supervisorScope and its other child coroutines?
supervisorScope and its other children continue to run unaffected.
supervisorScope and all its children immediately.
supervisorScope.
supervisorScope re-launches the failed coroutine automatically.
60
When building an OkHttpClient, you can set timeouts using connectTimeout, readTimeout, and callTimeout. If you set a callTimeout of 30 seconds and a readTimeout of 15 seconds, and a server takes 20 seconds to send a response, what will happen?
IllegalStateException when the client is built.
SocketTimeoutException because the readTimeout of 15 seconds was exceeded.
callTimeout (30s).
callTimeout because it is the overarching limit.