CPS251 Android Development by Scott Shaper

Introduction to Retrofit

What is Retrofit?

Think of Retrofit as your app's personal messenger to the internet. Just like how you might ask a friend to get information for you, Retrofit helps your Android app talk to web servers and get data back. It's like having a translator that converts your app's requests into the language that web servers understand.

Retrofit is a popular library that makes it super easy to connect your Android app to the internet. Instead of writing complicated code to handle network connections, Retrofit does the heavy lifting for you. It's like having a smart assistant that knows exactly how to talk to different websites and bring back the information you need.

Quick Reference

Concept Description When to Use
API Interface Defines the contract for network requests Setting up communication with a web service
Retrofit Instance The main object that handles network operations Creating the connection to your web server
HTTP Methods GET, POST, PUT, DELETE operations Different types of data operations
Data Classes Kotlin classes that represent your data Structuring the data you send and receive

Setting Up Retrofit in Your Project

When to Use Retrofit

Common Setup Steps

Step What It Does Why It's Important
Add Dependencies Include Retrofit libraries in your project Gives your app the tools to make network requests
Create API Interface Define the methods for your network calls (by zip code, using OpenWeatherMap) Maps your app's needs to real web server endpoints
Build Retrofit Instance Create the main Retrofit object with the real base URL Establishes the connection to your web server
Add Internet & Network Permissions Tell Android your app needs internet and network state access Required for any network communication and offline checks

Creating a Retrofit Instance

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitClient {
    private const val BASE_URL = "https://api.openweathermap.org/"

    val weatherApiService: WeatherApiService by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(WeatherApiService::class.java)
    }
}

Explanation of RetrofitClient

The RetrofitClient object is a singleton that provides a pre-configured instance of Retrofit, which is then used to create our API service. This ensures that only one Retrofit instance is created, optimizing resource usage.

Creating a Simple API Interface

import retrofit2.http.GET
            import retrofit2.http.Query
            
            interface WeatherApiService {
                @GET("data/2.5/weather")
                suspend fun getCurrentWeather(
                    @Query("zip") zip: String,
                    @Query("appid") appId: String,
                    @Query("units") units: String = "metric"
                ): WeatherResponse
            }            

Explanation of WeatherApiService

The WeatherApiService interface defines the contract for our weather API calls. Retrofit uses annotations to convert these interface methods into actual HTTP requests.

WeatherResponse Data Class

The WeatherResponse data class is used to represent the response from the weather API. It is used to parse the JSON response from the API into a Kotlin object.

JSON Response

Here is a example JSON response from the weather API.

{
    "coord": {
      "lon": -83.9248,
      "lat": 42.6159
    },
    "weather": [
      {
        "id": 804,
        "main": "Clouds",
        "description": "overcast clouds",
        "icon": "04d"
      }
    ],
    "base": "stations",
    "main": {
      "temp": 11.56,
      "feels_like": 10.31,
      "temp_min": 10.21,
      "temp_max": 12.2,
      "pressure": 1006,
      "humidity": 59,
      "sea_level": 1006,
      "grnd_level": 973
    },
    "visibility": 10000,
    "wind": {
      "speed": 4.02,
      "deg": 30,
      "gust": 7.6
    },
    "clouds": {
      "all": 100
    },
    "dt": 1761856253,
    "sys": {
      "type": 1,
      "id": 5267,
      "country": "US",
      "sunrise": 1761826031,
      "sunset": 1761863480
    },
    "timezone": -14400,
    "id": 0,
    "name": "Howell",
    "cod": 200
  }

Here is the data class that matches the JSON response.

data class WeatherResponse(
    val coord: Coord,
    val weather: List,
    val base: String,
    val main: Main,
    val visibility: Int,
    val wind: Wind,
    val clouds: Clouds,
    val dt: Long,
    val sys: Sys,
    val timezone: Int,
    val id: Int,
    val name: String,
    val cod: Int
)

data class Coord(
    val lon: Double,
    val lat: Double
)

data class Weather(
    val id: Int,
    val main: String,
    val description: String,
    val icon: String
)

data class Main(
    val temp: Double,
    val feels_like: Double,
    val temp_min: Double,
    val temp_max: Double,
    val pressure: Int,
    val humidity: Int,
    val sea_level: Int?,
    val grnd_level: Int?
)

data class Wind(
    val speed: Double,
    val deg: Int,
    val gust: Double?
)

data class Clouds(
    val all: Int
)

data class Sys(
    val type: Int?,
    val id: Int?,
    val country: String,
    val sunrise: Long,
    val sunset: Long
)

Learning Aids

Tips for Success

  • Always define your API interface clearly with correct HTTP annotations and query parameters.
  • Use a JSON converter (like Gson) to automatically parse API responses into Kotlin data classes.
  • Ensure your data classes precisely match the structure of the JSON response from the API.
  • Utilize `lazy` initialization for your Retrofit service to create it only when needed.

Common Mistakes to Avoid

  • Forgetting to add internet permissions to your `AndroidManifest.xml` file.
  • Mismatching data class property names with JSON keys, especially when `snake_case` is used in JSON and `camelCase` in Kotlin.
  • Not handling network exceptions and errors gracefully in your API calls.
  • Hardcoding the base URL and API keys in production applications; use more secure methods for configuration.

Best Practices

  • Organize your Retrofit setup (client, service, data classes) into dedicated packages for better modularity.
  • Use Kotlin `suspend` functions in your API interface for easy integration with coroutines for asynchronous operations.
  • Implement a logging interceptor (like OkHttp's `HttpLoggingInterceptor`) for debugging network requests and responses.
  • Design your API interfaces to be as granular as possible, with each method representing a specific API endpoint.