Blog purpose for android basic example for android app developer. any query please my contact

Monday, 6 October 2025

api integration with retrofit

api integration with retrofit: Android Guide

In modern Android app development, api integration with retrofit is one of the most reliable and maintainable ways to connect your app to backend services. In this guide, you’ll learn how to set up Retrofit, define your API interface, call endpoints (GET, POST, PUT, DELETE), handle responses & errors, and incorporate best practices.


---

Table of Contents

1. Why choose Retrofit for API integration


2. Setup: dependencies, permissions, model classes


3. Building your Retrofit client & interface


4. Making requests: synchronous, asynchronous, coroutines


5. Error handling, logging, interceptors, authentication


6. Best practices, tips & real examples


7. Conclusion




---

1. Why choose Retrofit for API integration

Retrofit is a type-safe HTTP client for Android and Java, created by Square. 

Advantages of Retrofit

Clean interface-based API definitions using annotations (e.g. @GET, @POST) 

Automatic JSON (or other) serialization/deserialization via converter libraries like Gson, Moshi 

Supports synchronous / asynchronous calls, or Kotlin coroutines / RxJava integration 

Easy to integrate custom headers, interceptors, logging, authentication, error mapping 

Widely documented and maintained (active GitHub)


Because of these benefits, api integration with retrofit helps maintain cleaner architecture and better error handling.


---

2. Setup: Dependencies, Permissions, Model Classes

2.1 Add dependencies

In your module’s build.gradle (app-level), add at least:

implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"

You may also add logging interceptors or other converters (Moshi, Scalars). 

Sync the project after adding dependencies.

2.2 Add Internet permission

In your AndroidManifest.xml:

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

Without this, any network request will fail. 

2.3 Define model (POJO / data class)

You need classes to map the JSON responses. For example, if your API returns:

{
  "userId": 1,
  "id": 5,
  "title": "Hello",
  "body": "World"
}

You might define:

data class Post(
    val userId: Int,
    val id: Int,
    val title: String,
    val body: String
)

You can also use @SerializedName("json_field") if JSON names differ from your Kotlin/Java variable names.
GeeksforGeeks gives a clear step-by-step on this setup. 

> Internal link placeholder: Check out my post on Android JSON parsing techniques for more on building model classes.




---

3. Building Your Retrofit Client & Interface

3.1 Retrofit builder / singleton

To avoid multiple instances, you can build a singleton:

object RetrofitClient {
    private const val BASE_URL = "https://api.example.com/"

    val instance: Retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    val api: ApiService by lazy {
        instance.create(ApiService::class.java)
    }
}

This pattern ensures you always reuse the same Retrofit instance. Many tutorials (e.g. Medium’s guides) show this pattern. 

3.2 Define API interface

Define endpoints using annotated methods:

interface ApiService {
    @GET("posts")
    fun getPosts(): Call<List<Post>>

    @GET("posts/{id}")
    fun getPost(@Path("id") id: Int): Call<Post>

    @POST("posts")
    fun createPost(@Body post: Post): Call<Post>

    @PUT("posts/{id}")
    fun updatePost(@Path("id") id: Int, @Body post: Post): Call<Post>

    @DELETE("posts/{id}")
    fun deletePost(@Path("id") id: Int): Call<Void>
}

You can also use query parameters:

@GET("posts")
fun getPostsPaged(@Query("page") page: Int, @Query("limit") limit: Int): Call<List<Post>>

And dynamic URLs:

@GET
fun getFromUrl(@Url url: String): Call<Any>

This flexibility helps in many use cases. 


---

4. Making Requests: Sync, Async, Coroutines

4.1 Asynchronous calls (enqueue)

This is the common approach in Android:

RetrofitClient.api.getPosts().enqueue(object : Callback<List<Post>> {
    override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) {
        if (response.isSuccessful) {
            val list = response.body()
            // Use the data
        } else {
            // Handle HTTP error codes
        }
    }

    override fun onFailure(call: Call<List<Post>>, t: Throwable) {
        // Handle failure (network error, timeout, etc.)
    }
})

This runs off the main thread automatically. 

4.2 Synchronous calls

Synchronous calls using .execute() are not recommended on the main thread (they block). Example:

val resp = RetrofitClient.api.getPosts().execute()
if (resp.isSuccessful) {
    val list = resp.body()
}

You might use this in a background thread (worker thread). 

4.3 Using coroutines (suspend functions)

In Kotlin projects, you can define:

interface ApiService {
    @GET("posts")
    suspend fun getPostsSuspend(): List<Post>
}

Then call:

CoroutineScope(Dispatchers.IO).launch {
    try {
        val posts = RetrofitClient.api.getPostsSuspend()
        withContext(Dispatchers.Main) {
            // update UI
        }
    } catch (e: Exception) {
        // handle error
    }
}

This approach is cleaner when using Kotlin + coroutines, and many modern tutorials use it. 


---

5. Error Handling, Logging, Interceptors, Authentication

5.1 Logging & OkHttp interceptor

Attach an OkHttp logging interceptor to see request/response bodies (useful during development):

val logging = HttpLoggingInterceptor().apply {
    level = HttpLoggingInterceptor.Level.BODY
}

val client = OkHttpClient.Builder()
    .addInterceptor(logging)
    .build()

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

This helps debug API integration with retrofit.

5.2 Interceptors for headers / auth

You can add an interceptor to inject headers (e.g. token):

val authInterceptor = Interceptor { chain ->
    val newReq = chain.request().newBuilder()
        .addHeader("Authorization", "Bearer $token")
        .build()
    chain.proceed(newReq)
}

val client = OkHttpClient.Builder()
    .addInterceptor(authInterceptor)
    .addInterceptor(logging)
    .build()

5.3 Error mapping & response codes

Use response.code() or response.errorBody() for non-2xx responses

Wrap your API calls in try/catch to capture network exceptions such as IOException

Use a custom ResponseWrapper class to standardize success/failure pattern


5.4 Timeouts, retries, caching

You can configure OkHttp timeouts: connectTimeout, readTimeout, etc.

Use retry interceptors or fallback logic

Use HTTP caching (OkHttp caching with Cache object) for GET requests



---

6. Best Practices, Tips & Real Examples

6.1 Use Repository / MVVM / Clean Architecture

Avoid calling Retrofit directly in Activities or Fragments. Use a repository layer, pass data via ViewModel, LiveData / Flow. This separation helps unit testing and maintenance.

6.2 Don’t leak Retrofit instances

Always reuse your Retrofit client. Don’t build it each time.

6.3 Use consistent response wrapper

Design a data wrapper, e.g.:

sealed class Result<T> {
    data class Success<T>(val data: T): Result<T>()
    data class Error<T>(val message: String): Result<T>()
}

Wrap Retrofit responses into such Result types in repository.

6.4 Retry & exponential backoff

For transient network failures, implement retry with backoff logic in your code or via interceptor.

6.5 Secure credentials

Never hardcode API keys or secrets in code. Use secure storage or remote config.

6.6 Use versioning & base URL strategies

If you have multiple API versions or endpoints, consider dynamic base URL or path segments.


---

7. Example: Full flow

Here’s an example flow summarizing the above:

1. Define data class Post(...)


2. Setup RetrofitClient singleton with logging interceptor


3. Create ApiService interface with suspend GET/POST methods


4. In repository:

suspend fun fetchPosts(): Result<List<Post>> {
    return try {
        val list = api.getPostsSuspend()
        Result.Success(list)
    } catch (e: Exception) {
        Result.Error(e.localizedMessage ?: "Unknown error")
    }
}


5. In ViewModel:

fun loadPosts() = viewModelScope.launch {
    _uiState.value = UiState.Loading
    when (val res = repo.fetchPosts()) {
      is Result.Success -> _uiState.value = UiState.Success(res.data)
      is Result.Error -> _uiState.value = UiState.Error(res.message)
    }
}


6. In UI (Activity/Fragment), observe uiState and update accordingly.



This flow demonstrates clean separation and effective api integration with retrofit.


---

8. Summary & Conclusion

API integration with Retrofit offers a clean, powerful, and scalable way to connect your Android app to remote services. By combining interface-based API definitions, converter libraries, OkHttp customizations, coroutine support, and good architecture practices, you can build robust network layers.

Remember:

Use a singleton Retrofit instance

Define clear model classes

Wrap responses and errors in a consistent structure

Use interceptors for logging, headers, authentication

Organize your network logic via repository / ViewModel


If you like, I can help you generate code samples in Java and Kotlin, or even convert this post into a blog-ready HTML/publishable version for your site.

No comments:

Post a Comment