Md Mominul Islam | Software and Data Enginnering | SQL Server, .NET, Power BI, Azure Blog

while(!(succeed=try()));

LinkedIn Portfolio Banner

Latest

Home Top Ad

Responsive Ads Here

Post Top Ad

Responsive Ads Here

Tuesday, August 26, 2025

Master Android App Development: Chapter 6 - Networking and APIs with Android Studio

 

1. Introduction to Networking in Android
1.1 Why Networking Matters
1.2 Understanding APIs and REST
1.3 Overview of Retrofit and OkHttp

2. Setting Up Your Android Project
2.1 Creating a New Project in Android Studio
2.2 Adding Dependencies (Retrofit, OkHttp, Gson)
2.3 Configuring Internet Permissions

3. Introduction to Retrofit and OkHttp
3.1 What is Retrofit?
3.2 What is OkHttp?
3.3 Why Use Retrofit and OkHttp Together?
3.4 Alternatives to Retrofit (Volley, HttpURLConnection)

4. Making HTTP Requests
4.1 Understanding HTTP Methods (GET, POST)
4.2 Setting Up Retrofit for API Calls
4.3 Making a GET Request
4.4 Making a POST Request

5. Consuming RESTful APIs
5.1 Anatomy of a RESTful API
5.2 Parsing JSON Responses with Gson
5.3 Handling API Keys and Authentication

6. Handling API Responses and Errors
6.1 Processing Successful Responses
6.2 Managing Network Errors
6.3 Implementing Retry Logic
6.4 Displaying User-Friendly Error Messages

7. Asynchronous Programming in Networking
7.1 Threads, Executors, and Handlers
7.2 Introduction to Coroutines in Android
7.3 Using Retrofit with Coroutines
7.4 LiveData and Flow for Observing Data

8. Practical Exercise: Building a Weather App
8.1 Project Overview
8.2 Setting Up the Weather API (OpenWeatherMap)
8.3 Designing the UI
8.4 Fetching Weather Data
8.5 Displaying Data in the UI
8.6 Adding Error Handling
8.7 Enhancing the App with Advanced Features

9. Best Practices for Networking in Android
9.1 Optimizing Network Requests
9.2 Caching Responses
9.3 Security Considerations (HTTPS, API Keys, SSL Pinning)
9.4 Testing Network Code with MockWebServer

10. Pros and Cons of Retrofit and OkHttp
10.1 Advantages
10.2 Limitations
10.3 When to Use Alternatives

11. Alternatives to Retrofit and OkHttp
11.1 Volley
11.2 HttpURLConnection
11.3 Ktor
11.4 Comparison Table

12. Advanced Networking Features (Optional)
12.1 Uploading Files (Multipart)
12.2 Downloading Large Files
12.3 Streaming APIs

13. Conclusion
13.1 Recap of Key Concepts
13.2 Next Steps in Your Android Journey

14. References


1. Introduction to Networking in Android

1.1 Why Networking Matters

Networking is the backbone of modern mobile applications. From fetching real-time weather updates to syncing user data with a server, most apps rely on internet connectivity to deliver dynamic content. In Android, networking enables apps to communicate with external servers, consume APIs, and provide seamless user experiences. For example, a weather app like the one we'll build fetches live data to display current temperature, humidity, and forecasts.

1.2 Understanding APIs and REST

An API (Application Programming Interface) allows your app to interact with external services. A RESTful API (Representational State Transfer) uses standard HTTP methods (GET, POST, PUT, DELETE) to perform operations like fetching or sending data. REST APIs typically return data in JSON or XML format, with JSON being the most common due to its lightweight nature.

Real-Life Example: Imagine you're building a social media app. The app needs to fetch a user's posts from a server (GET request) or upload a new post (POST request). REST APIs make this communication structured and efficient.

1.3 Overview of Retrofit and OkHttp

  • Retrofit: A type-safe HTTP client for Android and Java, developed by Square. It simplifies API calls by turning HTTP requests into Kotlin/Java interfaces, handling JSON parsing, and managing errors.

  • OkHttp: A powerful HTTP client that handles low-level networking tasks like sending requests, managing connections, and caching responses. Retrofit uses OkHttp under the hood for network operations.

Together, they provide a robust, efficient, and developer-friendly way to handle networking in Android apps.


2. Setting Up Your Android Project

2.1 Creating a New Project in Android Studio

  1. Open Android Studio and select New Project.

  2. Choose Empty Activity template.

  3. Configure:

    • Name: WeatherApp

    • Package Name: com.example.weatherapp

    • Language: Kotlin

    • Minimum SDK: API 21 (Lollipop)

  4. Click Finish to create the project.

2.2 Adding Dependencies (Retrofit, OkHttp, Gson)

Add the following dependencies to your app-level build.gradle file (app/build.gradle):

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.11.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}
  • Retrofit: For making API calls.

  • Gson Converter: To parse JSON responses into Kotlin objects.

  • OkHttp: For handling HTTP requests.

  • Gson: For JSON serialization/deserialization.

Sync the project by clicking Sync Project with Gradle Files.

2.3 Configuring Internet Permissions

To allow your app to access the internet, add the following permission to your AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

Place this line above the <application> tag. For Android 9 (API 28) and above, ensure you use HTTPS for API calls, as cleartext (HTTP) traffic is disabled by default. To enable HTTP for testing, add the following to AndroidManifest.xml:

<application
    android:usesCleartextTraffic="true"
    ... >

Best Practice: Use HTTPS in production for security.


3. Introduction to Retrofit and OkHttp

3.1 What is Retrofit?

Retrofit is a high-level library that simplifies HTTP requests by providing a clean, type-safe interface. It abstracts complex networking tasks, allowing you to define API endpoints as Kotlin interfaces. Retrofit handles:

  • URL construction

  • Query parameters

  • JSON parsing

  • Error handling

3.2 What is OkHttp?

OkHttp is a low-level HTTP client that manages connections, caching, and retries. It supports:

  • HTTP/2 and connection pooling

  • GZIP compression

  • Response caching

  • Interceptors for logging and authentication

Retrofit uses OkHttp internally, but you can customize OkHttp for advanced features like logging or retry logic.

3.3 Why Use Retrofit and OkHttp Together?

  • Retrofit: Simplifies API integration with minimal code.

  • OkHttp: Handles low-level networking efficiently.

  • Together: They provide a seamless, scalable solution for networking, reducing boilerplate code and improving performance.

3.4 Alternatives to Retrofit

  • Volley: Google’s lightweight networking library, suitable for small API calls but less powerful for complex APIs.

  • HttpURLConnection: Java’s built-in HTTP client, low-level and verbose, requiring manual handling of requests and responses.

  • Ktor: A Kotlin-first networking library, modern but less commonly used in Android compared to Retrofit.


4. Making HTTP Requests

4.1 Understanding HTTP Methods (GET, POST)

  • GET: Retrieves data from a server (e.g., fetching weather data).

  • POST: Sends data to a server (e.g., submitting a form).

4.2 Setting Up Retrofit for API Calls

Create a Retrofit instance to communicate with the API. Place this code in a singleton or dependency injection module (e.g., using Hilt or Dagger).

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

object RetrofitInstance {
    private const val BASE_URL = "https://api.openweathermap.org/data/2.5/"

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

4.3 Making a GET Request

Define an API interface for the GET request:

import retrofit2.http.GET
import retrofit2.http.Query

interface WeatherApiService {
    @GET("weather")
    suspend fun getWeather(
        @Query("q") city: String,
        @Query("appid") apiKey: String,
        @Query("units") units: String = "metric"
    ): WeatherResponse
}
  • @GET("weather"): Specifies the endpoint (/weather).

  • @Query: Adds query parameters to the URL (e.g., ?q=London&appid=your_api_key&units=metric).

  • suspend: Enables coroutine support for asynchronous calls.

4.4 Making a POST Request

For APIs requiring POST requests (e.g., submitting user data), define a POST method:

import retrofit2.http.Body
import retrofit2.http.POST

interface UserApiService {
    @POST("users")
    suspend fun createUser(@Body user: User): UserResponse
}
  • @POST("users"): Specifies the POST endpoint.

  • @Body: Sends the User object as the request body in JSON format.


5. Consuming RESTful APIs

5.1 Anatomy of a RESTful API

A RESTful API typically includes:

  • Base URL: The root URL (e.g., https://api.openweathermap.org/).

  • Endpoints: Specific paths (e.g., /data/2.5/weather).

  • Query Parameters: Key-value pairs in the URL (e.g., ?q=London).

  • Headers: Metadata like API keys or authentication tokens.

  • Body: Data sent in POST/PUT requests (usually JSON).

Example: OpenWeatherMap API

  • Base URL: https://api.openweathermap.org/

  • Endpoint: /data/2.5/weather

  • Query Parameters: q (city), appid (API key), units (metric/imperial)

5.2 Parsing JSON Responses with Gson

Define a data class to match the API’s JSON response. For OpenWeatherMap:

data class WeatherResponse(
    val main: Main,
    val weather: List<Weather>,
    val name: String
)

data class Main(
    val temp: Float,
    val humidity: Int
)

data class Weather(
    val description: String,
    val icon: String
)

Gson automatically maps the JSON response to these data classes.

5.3 Handling API Keys and Authentication

Store your API key securely (avoid hardcoding). Use BuildConfig or a local properties file:

  1. Add to local.properties:

    WEATHER_API_KEY=your_api_key_here
  2. Read in build.gradle:

    android {
        buildTypes {
            debug {
                buildConfigField "String", "WEATHER_API_KEY", "\"${project.property('WEATHER_API_KEY')}\""
            }
        }
    }
  3. Access in code:

    val apiKey = BuildConfig.WEATHER_API_KEY

6. Handling API Responses and Errors

6.1 Processing Successful Responses

Use coroutines to make asynchronous API calls and handle responses:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

suspend fun fetchWeather(city: String): WeatherResponse? {
    return withContext(Dispatchers.IO) {
        try {
            RetrofitInstance.api.getWeather(city, BuildConfig.WEATHER_API_KEY)
        } catch (e: Exception) {
            null
        }
    }
}

6.2 Managing Network Errors

Handle common network errors:

  • IOException: No internet connection.

  • HttpException: API errors (e.g., 404, 401).

  • UnknownHostException: Invalid URL or server unreachable.

import retrofit2.HttpException
import java.io.IOException
import java.net.UnknownHostException

suspend fun fetchWeather(city: String): Result<WeatherResponse> {
    return try {
        val response = RetrofitInstance.api.getWeather(city, BuildConfig.WEATHER_API_KEY)
        Result.success(response)
    } catch (e: IOException) {
        Result.failure(Exception("No internet connection"))
    } catch (e: HttpException) {
        Result.failure(Exception("API error: ${e.code()}"))
    } catch (e: UnknownHostException) {
        Result.failure(Exception("Server unreachable"))
    } catch (e: Exception) {
        Result.failure(Exception("Unexpected error: ${e.message}"))
    }
}

6.3 Implementing Retry Logic

Use OkHttp’s interceptor to retry failed requests:

import okhttp3.Interceptor
import okhttp3.Response

class RetryInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        var response: Response? = null
        var tryCount = 0
        val maxRetries = 3

        while (response == null && tryCount < maxRetries) {
            try {
                response = chain.proceed(request)
            } catch (e: IOException) {
                tryCount++
                if (tryCount >= maxRetries) throw e
                Thread.sleep(1000L * tryCount) // Exponential backoff
            }
        }
        return response ?: throw IOException("Failed after $maxRetries retries")
    }
}

Add to Retrofit:

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(RetryInterceptor())
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(okHttpClient)
    .addConverterFactory(GsonConverterFactory.create())
    .build()

6.4 Displaying User-Friendly Error Messages

Map exceptions to user-friendly messages:

fun getErrorMessage(exception: Exception): String {
    return when (exception) {
        is IOException -> "Please check your internet connection."
        is HttpException -> when (exception.code()) {
            401 -> "Invalid API key."
            404 -> "City not found."
            else -> "Server error: ${exception.code()}"
        }
        is UnknownHostException -> "Unable to reach the server."
        else -> "An unexpected error occurred."
    }
}

7. Practical Exercise: Building a Weather App

7.1 Project Overview

We’ll build a weather app that:

  • Fetches current weather data for a user-specified city.

  • Displays temperature, humidity, and weather description.

  • Handles errors like invalid city names or no internet.

  • Uses OpenWeatherMap API (free tier).

7.2 Setting Up the Weather API (OpenWeatherMap)

  1. Sign up at OpenWeatherMap and get an API key.

  2. Store the API key in local.properties (see Section 5.3).

7.3 Designing the UI

Create a simple UI with:

  • An EditText for city input.

  • A Button to fetch weather.

  • TextViews to display temperature, humidity, and description.

  • An ImageView for the weather icon.

Layout (res/layout/activity_main.xml):

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <EditText
        android:id="@+id/cityEditText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="Enter city name"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/fetchButton" />

    <Button
        android:id="@+id/fetchButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Weather"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/temperatureText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Temperature: --"
        app:layout_constraintTop_toBottomOf="@id/cityEditText"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="16dp" />

    <TextView
        android:id="@+id/humidityText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Humidity: --"
        app:layout_constraintTop_toBottomOf="@id/temperatureText"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="8dp" />

    <TextView
        android:id="@+id/descriptionText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Description: --"
        app:layout_constraintTop_toBottomOf="@id/humidityText"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="8dp" />

    <ImageView
        android:id="@+id/weatherIcon"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:layout_constraintTop_toBottomOf="@id/descriptionText"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginTop="16dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

7.4 Fetching Weather Data

Create the API service and data classes (see Sections 4.3 and 5.2). In MainActivity.kt, fetch weather data using coroutines:

import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private lateinit var cityEditText: EditText
    private lateinit var fetchButton: Button
    private lateinit var temperatureText: TextView
    private lateinit var humidityText: TextView
    private lateinit var descriptionText: TextView
    private lateinit var weatherIcon: ImageView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        cityEditText = findViewById(R.id.cityEditText)
        fetchButton = findViewById(R.id.fetchButton)
        temperatureText = findViewById(R.id.temperatureText)
        humidityText = findViewById(R.id.humidityText)
        descriptionText = findViewById(R.id.descriptionText)
        weatherIcon = findViewById(R.id.weatherIcon)

        fetchButton.setOnClickListener {
            val city = cityEditText.text.toString().trim()
            if (city.isEmpty()) {
                Toast.makeText(this, "Please enter a city name", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            lifecycleScope.launch {
                val result = fetchWeather(city)
                when {
                    result.isSuccess -> {
                        val weather = result.getOrNull()
                        weather?.let {
                            temperatureText.text = "Temperature: ${it.main.temp}°C"
                            humidityText.text = "Humidity: ${it.main.humidity}%"
                            descriptionText.text = "Description: ${it.weather[0].description}"
                            Glide.with(this@MainActivity)
                                .load("https://openweathermap.org/img/wn/${it.weather[0].icon}.png")
                                .into(weatherIcon)
                        }
                    }
                    result.isFailure -> {
                        Toast.makeText(this@MainActivity, getErrorMessage(result.exceptionOrNull()!!), Toast.LENGTH_LONG).show()
                    }
                }
            }
        }
    }
}

Add Glide for loading weather icons:

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.16.0'
}

7.5 Displaying Data in the UI

The code above updates the UI with temperature, humidity, description, and the weather icon using Glide to load the icon from OpenWeatherMap’s servers.

7.6 Adding Error Handling

The fetchWeather function (Section 6.2) handles errors, and getErrorMessage (Section 6.4) displays user-friendly messages via Toast.

7.7 Enhancing the App with Advanced Features

  • Caching: Use OkHttp’s caching to store responses offline.

  • Retry Logic: Implement the retry interceptor (Section 6.3).

  • Loading Indicator: Add a ProgressBar to show loading state.

  • Background Refresh: Use a WorkManager to periodically fetch weather updates.

Example: Adding a ProgressBar

  1. Add to activity_main.xml:

<ProgressBar
    android:id="@+id/progressBar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone"
    app:layout_constraintTop_toBottomOf="@id/weatherIcon"
    app:layout_constraintStart_toStartOf="parent"
    android:layout_marginTop="16dp" />
  1. Update MainActivity.kt:

private lateinit var progressBar: ProgressBar

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    progressBar = findViewById(R.id.progressBar)
    
    fetchButton.setOnClickListener {
        progressBar.visibility = View.VISIBLE
        lifecycleScope.launch {
            // Fetch weather
            progressBar.visibility = View.GONE
        }
    }
}

8. Best Practices for Networking in Android

8.1 Optimizing Network Requests

  • Minimize Requests: Combine API calls when possible.

  • Use Compression: OkHttp automatically handles GZIP compression.

  • Batch Updates: Fetch data in bulk to reduce network overhead.

8.2 Caching Responses

Enable caching in OkHttp:

val cacheSize = 10 * 1024 * 1024 // 10 MB
val cache = Cache(cacheDir, cacheSize.toLong())

val okHttpClient = OkHttpClient.Builder()
    .cache(cache)
    .build()

8.3 Security Considerations

  • Use HTTPS for all API calls.

  • Store API keys securely (e.g., in BuildConfig).

  • Validate server certificates using OkHttp’s SSLSocketFactory.

8.4 Testing Network Code

  • Unit Tests: Use Mockito to mock API responses.

  • UI Tests: Use Espresso to test UI updates after API calls.

  • Mock Servers: Use tools like MockWebServer for testing.


9. Pros and Cons of Retrofit and OkHttp

9.1 Advantages

  • Retrofit:

    • Simplifies API integration with interfaces.

    • Built-in JSON parsing with Gson.

    • Coroutine support for asynchronous calls.

  • OkHttp:

    • Efficient connection pooling.

    • Supports caching and retries.

    • Handles HTTP/2 and compression.

9.2 Limitations

  • Retrofit:

    • Steep learning curve for beginners.

    • Limited to REST APIs (not ideal for WebSockets).

  • OkHttp:

    • Low-level, requiring more configuration for complex tasks.

    • Manual error handling for advanced scenarios.

9.3 When to Use Alternatives

  • Volley: For small, simple API calls.

  • HttpURLConnection: For legacy apps or minimal dependencies.

  • Ktor: For Kotlin-first projects with coroutine-heavy code.


10. Alternatives to Retrofit and OkHttp

10.1 Volley

Google’s lightweight library for simple networking tasks. Best for small API calls but lacks Retrofit’s type safety and flexibility.

10.2 HttpURLConnection

Java’s built-in HTTP client. Verbose and error-prone, suitable for basic tasks in legacy apps.

10.3 Ktor

A Kotlin-first networking library with coroutine support. Modern but less mature than Retrofit for Android.

10.4 Comparison Table

Feature

Retrofit

OkHttp

Volley

HttpURLConnection

Ktor

Type Safety

Yes

No

No

No

Yes

Coroutine Support

Yes

Partial

No

No

Yes

JSON Parsing

Built-in (Gson)

Manual

Manual

Manual

Built-in

Ease of Use

High

Moderate

High

Low

High

Best For

REST APIs

Low-level

Simple APIs

Legacy Apps

Kotlin Apps


11. Conclusion

11.1 Recap of Key Concepts

In this chapter, we explored Android networking with Retrofit and OkHttp, covering:

  • Setting up a project with internet permissions.

  • Making GET and POST requests.

  • Consuming RESTful APIs and parsing JSON.

  • Handling errors and retries.

  • Building a weather app with real-time data.

11.2 Next Steps in Your Android Journey

  • Explore advanced topics like WebSockets, GraphQL, or Firebase integration.

  • Build more complex apps with multiple API endpoints.

  • Contribute to open-source Android projects to enhance your portfolio.

No comments:

Post a Comment

Thanks for your valuable comment...........
Md. Mominul Islam

Post Bottom Ad

Responsive Ads Here