Skip to main content

Working with Networking

1. Adding Internet Permission

Permission Description Usage
android.permission.INTERNET Allows applications to open network sockets. Required for all network operations including HTTP requests, WebSocket connections, etc.
android.permission.ACCESS_NETWORK_STATE Allows applications to access information about networks. Useful for checking network connectivity status before making requests.

Example: Declaring Permissions in AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application ... >
        ...
    </application>
</manifest>

2. Making HTTP Requests

Example: Basic GET Request with OkHttp

import okhttp3.*

val client = OkHttpClient()

fun makeGetRequest() {
    val request = Request.Builder()
        .url("https://api.example.com/data")
        .build()

    client.newCall(request).execute().use { response ->
        if (response.isSuccessful) {
            println(response.body?.string())
        } else {
            println("Request failed: ${response.code}")
        }
    }
}
Method Description Notes
OkHttpClient() Creates an instance of the HTTP client Can be customized with interceptors, timeouts, etc.
Request.Builder() Builds a new HTTP request Chainable methods for URL, headers, body
.url() Sets the URL for the request Must be a valid HTTP/HTTPS URL
.build() Finalizes the request object Must be called before executing
client.newCall(request).execute() Executes the HTTP request synchronously Returns a Response object
response.isSuccessful Checks if the HTTP status code is 2xx Useful for handling success vs error cases
response.body?.string() Gets the response body as a string Must be consumed once, and is nullable
Note: For asynchronous requests, use enqueue() instead of execute().
Warning: Never perform network operations on the main thread in Android. Always use background threads or coroutines.

3. Performing GET Requests

Example: Basic GET Request with OkHttp

import okhttp3.OkHttpClient
import okhttp3.Request

val client = OkHttpClient()
val request = Request.Builder()
    .url("https://api.example.com/users")
    .build()

val response = client.newCall(request).execute()
println(response.body?.string())
Property Description Values
url Endpoint to fetch data from "https://api.example.com/users"
method HTTP method to use "GET"
headers Optional headers to send mapOf("Authorization" to "Bearer token")
Note: Ensure network permissions are added to AndroidManifest.xml for HTTP requests.

4. Performing POST Requests

Property Description Example
url Endpoint URL to send the POST request "https://api.example.com/users"
body Request body as a JSON string or object "{\n "name": "John",\n "email": "john@example.com"\n}"
headers Map of HTTP headers to include in the request mapOf("Content-Type" to "application/json")

Example: Sending a POST Request with OkHttp

import okhttp3.*

val client = OkHttpClient()
val json = "{\n  \"name\": \"John\",\n  \"email\": \"john@example.com\"\n}"
val body = RequestBody.create(json, MediaType.get("application/json"))
val request = Request.Builder()
    .url("https://api.example.com/users")
    .post(body)
    .addHeader("Content-Type", "application/json")
    .build()

try {
    val response = client.newCall(request).execute()
    println(response.body?.string())
} catch (e: IOException) {
    e.printStackTrace()
}

Example: Sending a POST Request with Retrofit

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.POST

interface ApiService {
    @POST("users")
    suspend fun createUser(@Body user: User): Response<User>
}

data class User(
    val name: String,
    val email: String
)

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val apiService = retrofit.create(ApiService::class.java)
val user = User("John", "john@example.com")

5. Handling Network Responses

Property Description Example
response.code HTTP status code returned by the server 200, 404
response.body Response body as a string or parsed object "{"message": "Success"}"
response.headers Map of response headers {"Content-Type": "application/json"}

Example: Handling Response Codes

val response = apiClient.fetchData()
when (response.code) {
    200 -> {
        val data = response.body
        // Process successful response
    }
    404 -> {
        // Handle not found error
    }
    else -> {
        // Handle other errors
    }
}

Example: Parsing JSON Response

val response = apiClient.fetchData()
if (response.code == 200) {
    val json = response.body
    val parsed = Gson().fromJson(json, MyDataClass::class.java)
}
Note: Always check the response code before parsing the body to avoid runtime errors.
Warning: Never assume a successful response code. Always validate and handle potential errors.

6. Parsing JSON Responses

Example: Parsing JSON with Gson

import com.google.gson.Gson
import com.google.gson.annotations.SerializedName

data class User(
    @SerializedName("id") val id: Int,
    @SerializedName("name") val name: String,
    @SerializedName("email") val email: String
)

val json = &#34;{&#34;id&#34;: 1, &#34;name&#34;: &#34;John Doe&#34;, &#34;email&#34;: &#34;john@example.com&#34;}&#34;
val gson = Gson()
val user = gson.fromJson(json, User::class.java)
Method Description Notes
fromJson() Parses JSON string into object Requires type adapter or annotations
toJson() Serializes object to JSON Opposite of fromJson()
Note: Gson requires @SerializedName for field mapping when JSON keys differ from Kotlin property names.

Example: Using Moshi for JSON Parsing

import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory

data class Product(
    val id: Int,
    val name: String,
    val price: Double
)

val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

val jsonAdapter = moshi.adapter(Product::class.java)
val productJson = &#34;{&#34;id&#34;: 101, &#34;name&#34;: &#34;Laptop&#34;, &#34;price&#34;: 999.99}&#34;
val product = jsonAdapter.fromJson(productJson)

JSON Parsing Libraries Comparison

  • Gson: Easy to use, built-in with Android Studio
  • Moshi: Faster, more efficient, modern approach
  • Json-Kotlin: Kotlin-first, type-safe, less boilerplate

7. Checking Network Connectivity

Example: Check Network Status

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkInfo
import android.os.Build

fun isNetworkAvailable(context: Context): Boolean {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val network = connectivityManager.activeNetwork
        val capabilities = connectivityManager.getNetworkCapabilities(network)
        capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true ||
        capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true ||
        capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) == true
    } else {
        val networkInfo: NetworkInfo? = connectivityManager.activeNetworkInfo
        networkInfo?.isConnectedOrConnecting == true
    }
}
Method Description Return Type Notes
isNetworkAvailable() Checks if the device has active network connection Boolean Supports both legacy and modern Android versions
Note: For Android 6.0 (API level 23) and above, use ConnectivityManager.getNetworkCapabilities() for better network information.
Warning: Network availability does not guarantee internet access. Additional checks may be required for full connectivity validation.

Network Connectivity Overview

  • ConnectivityManager is the primary class for checking network status
  • Use getNetworkCapabilities() for Android 6.0+ (API level 23)
  • Use activeNetworkInfo for older versions
  • Check for specific network transports like TRANSPORT_WIFI, TRANSPORT_CELLULAR

8. Monitoring Network Changes

Example: Registering for Network Status Updates

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkInfo
import android.os.Build

class NetworkChangeReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val network = connectivityManager.activeNetwork
            val capabilities = connectivityManager.getNetworkCapabilities(network)
            val isConnected = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true ||
                              capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
            if (isConnected) {
                // Handle network connected
            } else {
                // Handle network disconnected
            }
        } else {
            val networkInfo = connectivityManager.activeNetworkInfo
            val isConnected = networkInfo?.isConnected == true
            if (isConnected) {
                // Handle network connected
            } else {
                // Handle network disconnected
            }
        }
    }
}

// Register the receiver in your Activity or Service
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
val receiver = NetworkChangeReceiver()
registerReceiver(receiver, filter)
Property Description Values
ConnectivityManager.CONNECTIVITY_ACTION Intent action broadcast when network connectivity changes android.net.conn.CONNECTIVITY_CHANGE
NetworkCapabilities.TRANSPORT_WIFI Indicates network is using WiFi transport true or false
NetworkCapabilities.TRANSPORT_CELLULAR Indicates network is using cellular transport true or false
Note: For Android 6.0 (API level 23) and above, use ConnectivityManager.registerDefaultNetworkCallback instead of BroadcastReceiver.

Example: Using NetworkCallback (API 23+)

import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build

class NetworkCallback : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
        super.onAvailable(network)
        // Handle network available
    }

    override fun onLost(network: Network) {
        super.onLost(network)
        // Handle network lost
    }

    override fun onCapabilitiesChanged(
        network: Network,
        networkCapabilities: NetworkCapabilities
    ) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        val hasInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        if (hasInternet) {
            // Handle internet available
        } else {
            // Handle no internet
        }
    }
}

// Register the callback in your Activity or Service
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkRequest = NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    .build()

connectivityManager.registerNetworkCallback(networkRequest, NetworkCallback())

Network Change Monitoring Summary

  • BroadcastReceiver: Legacy approach for Android versions below 6.0
  • NetworkCallback: Modern approach for Android 6.0+
  • Permissions: Requires ACCESS_NETWORK_STATE in manifest
  • Network Types: Supports WiFi, cellular, and other transport types

9. Handling Network Exceptions

Exception Type Description Handling Strategy
IOException Thrown when network operations fail due to connectivity issues or timeouts. Implement retry logic with exponential backoff and notify user of connection problems.
SocketTimeoutException Occurs when a socket read or write times out. Use timeout configurations and display a "Request timed out" message to the user.
UnknownHostException Occurs when the host address cannot be resolved (e.g., DNS failure). Inform user that the server could not be reached and suggest checking network settings.
HttpException Indicates an HTTP error response (e.g., 404, 500). Parse the status code and provide user-friendly error messages based on the code.

Example: Handling Network Exceptions in Retrofit

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val apiService = retrofit.create(ApiService::class.java)

try {
    val response = apiService.fetchData()
    if (response.isSuccessful) {
        // Process successful response
    } else {
        // Handle HTTP error
        when (response.code()) {
            404 -> println("Resource not found")
            500 -> println("Server error occurred")
        }
    }
} catch (e: IOException) {
    // Handle network errors
    println("Network error occurred: ${e.message}")
} catch (e: HttpException) {
    // Handle HTTP errors
    println("HTTP error occurred: ${e.code()}")
}
Note: Always handle IOException and HttpException separately to provide appropriate feedback to the user based on the type of error.

10. Using Network Security Configuration

Example: Basic Network Security Config

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="@raw/trusted_ca" />
        </trust-anchors>
    </base-config>
</network-security-config>
Element Description Notes
network-security-config Root element for network security configuration Must be declared in res/xml/
base-config Applies to all network traffic Can set cleartextTrafficPermitted
trust-anchors Defines trusted CA certificates Use src to reference raw resource
Note: Network security config is available from Android 7.0 (API level 24) onwards.

Example: Allowing Cleartext Traffic

<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="@raw/trusted_ca" />
        </trust-anchors>
    </base-config>
</network-security-config>

Example: Using Custom Certificate

<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="@raw/custom_ca" />
        </trust-anchors>
    </base-config>
</network-security-config>

11. Implementing Certificate Pinning

Certificate pinning is a security technique that ensures your app only accepts connections from specific certificates or public keys, mitigating man-in-the-middle (MITM) attacks.

Property Description Values
Certificate Pinning Specifies the certificate or public key to trust for a domain sha256/... or sha1/...

Example: Using OkHttp with Certificate Pinning

import okhttp3.CertificatePinner
import okhttp3.OkHttpClient
import okhttp3.Request

val certificatePinner = CertificatePinner.Builder()
    .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
    .build()

val client = OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build()

val request = Request.Builder()
    .url("https://api.example.com/data")
    .build()

val response = client.newCall(request).execute()
Note: Pinning should be used carefully as it can break connectivity if the server's certificate changes.
Warning: If the pinned certificate does not match, the connection will be rejected.

Was this article helpful?