Retrofit

Type-safe HTTP client generation library for Dart. Dio-based, automatically generates HTTP client implementation from interface definitions using source_gen. Achieves declarative and maintainable API communication through API design inspired by Android Retrofit.

HTTP ClientJavaAndroidType-safeAnnotationsREST

GitHub Overview

square/retrofit

A type-safe HTTP client for Android and the JVM

Stars43,775
Watchers1,547
Forks7,343
Created:September 6, 2010
Language:HTML
License:Apache License 2.0

Topics

androidjava

Star History

square/retrofit Star History
Data as of: 10/22/2025, 09:55 AM

Library

Retrofit

Overview

Retrofit is a "type-safe HTTP client for Android and the JVM" that has established itself as the de facto standard for RESTful API consumption in the Java/Android ecosystem, designed as a high-level HTTP client library emphasizing "annotation-based API definitions." It declaratively defines HTTP APIs using Java interfaces and automatically generates concrete implementations at runtime. Through type safety, compile-time checking, automatic serialization, diverse asynchronous processing models, and rich extensibility, it enables complex Web API integration with intuitive and maintainable code.

Details

Retrofit 2025 edition (v2.11 series) continues its evolutionary journey as a mature HTTP communication solution in the Java ecosystem. Built on a robust OkHttp foundation, five major components collaborate to transform interface method calls into HTTP requests: ServiceMethod, RequestFactory, OkHttpCall, CallAdapter, and Converter. Featuring annotation-based API design (@GET, @POST, @PUT, etc.), diverse Converters (Gson, Moshi, Jackson, Protocol Buffers, etc.), CallAdapters (RxJava, Kotlin Coroutines, CompletableFuture, etc.), comprehensive error handling, and multipart/form data support, it meets the demands of modern Android/Java application development.

Key Features

  • Type-safe API Definition: High reliability and bug prevention through compile-time type checking
  • Annotation-based Design: High readability through declarative and intuitive API definitions
  • Automatic Serialization: Development efficiency improvement through automatic JSON/XML conversion
  • Diverse Asynchronous Models: Support for synchronous, asynchronous, RxJava, and Kotlin Coroutines
  • Rich Extensibility: Customizability through Converter/CallAdapter systems
  • OkHttp Integration: High-performance HTTP processing and optimized connection pooling

Pros and Cons

Pros

  • De facto standard position for REST clients in Java/Android ecosystem
  • High development efficiency and readability through annotation-based declarative API definitions
  • Significant reduction in runtime errors through strong type safety and compile-time validation
  • Support for diverse data formats and asynchronous models through rich Converter/CallAdapter options
  • Comprehensive documentation and abundant community support
  • High-performance network processing and detailed configuration capabilities based on OkHttp

Cons

  • Learning cost and initial setup complexity (annotation understanding required)
  • Runtime overhead due to reflection-based implementation
  • Increased dependencies due to converters and library dependencies
  • May be overly feature-rich for very simple HTTP communications
  • Configuration complexity in ProGuard/R8 environments (keep rules required)
  • Some Java 8 feature limitations due to Android Runtime constraints

Reference Pages

Code Examples

Installation and Basic Setup

// build.gradle (Module: app) - Basic configuration
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.11.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.11.0'  // For JSON conversion
    implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'  // For logging
}

// Multipart/file upload support
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.11.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.11.0'  // For String conversion
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}

// RxJava integration version
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.11.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava3:2.11.0'
    implementation 'io.reactivex.rxjava3:rxjava:3.1.8'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
}
// Basic API interface definition
import retrofit2.Call;
import retrofit2.http.*;

// User data class
public class User {
    public int id;
    public String name;
    public String email;
    public int age;
    public String avatarUrl;
    
    // Constructor, getter/setter omitted
}

public class UserCreateRequest {
    public String name;
    public String email;
    public int age;
    
    public UserCreateRequest(String name, String email, int age) {
        this.name = name;
        this.email = email;
        this.age = age;
    }
}

// API interface definition
public interface UserApiService {
    // GET request (get all users)
    @GET("users")
    Call<List<User>> getUsers();
    
    // GET request (get specific user)
    @GET("users/{id}")
    Call<User> getUser(@Path("id") int userId);
    
    // GET request with query parameters
    @GET("users")
    Call<List<User>> getUsersWithQuery(
        @Query("page") int page,
        @Query("limit") int limit,
        @Query("sort") String sort
    );
    
    // POST request (create user)
    @POST("users")
    Call<User> createUser(@Body UserCreateRequest user);
    
    // PUT request (update user)
    @PUT("users/{id}")
    Call<User> updateUser(
        @Path("id") int userId,
        @Body UserCreateRequest user
    );
    
    // DELETE request
    @DELETE("users/{id}")
    Call<Void> deleteUser(@Path("id") int userId);
    
    // Request with headers
    @GET("users/profile")
    Call<User> getProfile(@Header("Authorization") String token);
}

// Retrofit instance creation
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiClient {
    private static final String BASE_URL = "https://api.example.com/";
    private static Retrofit retrofit;
    
    public static Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        }
        return retrofit;
    }
    
    public static UserApiService getUserApiService() {
        return getRetrofitInstance().create(UserApiService.class);
    }
}

Basic API Calls (Synchronous & Asynchronous)

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import android.util.Log;

public class UserRepository {
    private UserApiService apiService;
    
    public UserRepository() {
        this.apiService = ApiClient.getUserApiService();
    }
    
    // Synchronous execution (must be run on non-main thread)
    public List<User> getUsersSync() {
        try {
            Call<List<User>> call = apiService.getUsers();
            Response<List<User>> response = call.execute();
            
            if (response.isSuccessful()) {
                return response.body();
            } else {
                Log.e("API_ERROR", "Error code: " + response.code());
                return null;
            }
        } catch (IOException e) {
            Log.e("NETWORK_ERROR", "Network error", e);
            return null;
        }
    }
    
    // Asynchronous execution (recommended)
    public void getUsersAsync(ApiCallback<List<User>> callback) {
        Call<List<User>> call = apiService.getUsers();
        call.enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if (response.isSuccessful()) {
                    callback.onSuccess(response.body());
                } else {
                    callback.onError("API Error: " + response.code() + " - " + response.message());
                }
            }
            
            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {
                callback.onError("Network Error: " + t.getMessage());
            }
        });
    }
    
    // Get specific user
    public void getUserById(int userId, ApiCallback<User> callback) {
        Call<User> call = apiService.getUser(userId);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    User user = response.body();
                    if (user != null) {
                        callback.onSuccess(user);
                    } else {
                        callback.onError("User not found");
                    }
                } else {
                    handleErrorResponse(response, callback);
                }
            }
            
            @Override
            public void onFailure(Call<User> call, Throwable t) {
                callback.onError("Network error: " + t.getMessage());
            }
        });
    }
    
    // Create user
    public void createUser(String name, String email, int age, ApiCallback<User> callback) {
        UserCreateRequest request = new UserCreateRequest(name, email, age);
        Call<User> call = apiService.createUser(request);
        
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    callback.onSuccess(response.body());
                } else {
                    handleErrorResponse(response, callback);
                }
            }
            
            @Override
            public void onFailure(Call<User> call, Throwable t) {
                callback.onError("Create user failed: " + t.getMessage());
            }
        });
    }
    
    // Error response handling
    private void handleErrorResponse(Response<?> response, ApiCallback<?> callback) {
        try {
            String errorBody = response.errorBody().string();
            String errorMsg = String.format("HTTP %d: %s - %s", 
                response.code(), response.message(), errorBody);
            callback.onError(errorMsg);
        } catch (IOException e) {
            callback.onError("HTTP " + response.code() + ": " + response.message());
        }
    }
}

// Callback interface
public interface ApiCallback<T> {
    void onSuccess(T result);
    void onError(String error);
}

// Usage example
public class MainActivity extends AppCompatActivity {
    private UserRepository userRepository;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        userRepository = new UserRepository();
        
        // Get user list
        userRepository.getUsersAsync(new ApiCallback<List<User>>() {
            @Override
            public void onSuccess(List<User> users) {
                Log.d("API_SUCCESS", "Retrieved users: " + users.size());
                // Update UI
                updateUserList(users);
            }
            
            @Override
            public void onError(String error) {
                Log.e("API_ERROR", error);
                // Show error
                showError(error);
            }
        });
    }
    
    private void updateUserList(List<User> users) {
        // Update ListView or RecyclerView
    }
    
    private void showError(String error) {
        // Show error dialog or toast
        Toast.makeText(this, error, Toast.LENGTH_LONG).show();
    }
}

Advanced Configuration (Authentication, Interceptors, Timeouts)

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.util.concurrent.TimeUnit;

public class AdvancedApiClient {
    private static final String BASE_URL = "https://api.example.com/";
    private static Retrofit retrofit;
    
    // Advanced Retrofit instance creation
    public static Retrofit getAdvancedRetrofitInstance() {
        if (retrofit == null) {
            // HTTP logging interceptor
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            
            // Authentication interceptor
            Interceptor authInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request originalRequest = chain.request();
                    
                    // Get auth token (from SharedPreferences or SecureStorage)
                    String token = getAuthToken();
                    
                    if (token != null) {
                        Request authenticatedRequest = originalRequest.newBuilder()
                            .header("Authorization", "Bearer " + token)
                            .header("Accept", "application/json")
                            .header("Content-Type", "application/json")
                            .build();
                        return chain.proceed(authenticatedRequest);
                    }
                    
                    return chain.proceed(originalRequest);
                }
            };
            
            // Retry interceptor
            Interceptor retryInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    Response response = null;
                    IOException exception = null;
                    
                    int maxRetries = 3;
                    for (int i = 0; i < maxRetries; i++) {
                        try {
                            response = chain.proceed(request);
                            if (response.isSuccessful()) {
                                return response;
                            }
                            // Retry on server errors
                            if (response.code() >= 500) {
                                response.close();
                                Thread.sleep(1000 * (i + 1)); // Exponential backoff
                                continue;
                            }
                            return response;
                        } catch (IOException e) {
                            exception = e;
                            if (i == maxRetries - 1) break;
                            try {
                                Thread.sleep(1000 * (i + 1));
                            } catch (InterruptedException ie) {
                                Thread.currentThread().interrupt();
                                throw new IOException("Interrupted during retry", ie);
                            }
                        }
                    }
                    
                    if (exception != null) throw exception;
                    return response;
                }
            };
            
            // OkHttpClient configuration
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)      // Connection timeout
                .readTimeout(30, TimeUnit.SECONDS)         // Read timeout
                .writeTimeout(30, TimeUnit.SECONDS)        // Write timeout
                .addInterceptor(authInterceptor)           // Authentication
                .addInterceptor(retryInterceptor)          // Retry
                .addInterceptor(loggingInterceptor)        // Logging (debug only)
                .build();
            
            // Retrofit configuration
            retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create(createCustomGson()))
                .build();
        }
        return retrofit;
    }
    
    // Custom Gson configuration
    private static Gson createCustomGson() {
        return new GsonBuilder()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .setDateFormat("yyyy-MM-dd HH:mm:ss")
            .excludeFieldsWithoutExposeAnnotation()
            .create();
    }
    
    // Get auth token
    private static String getAuthToken() {
        // Get from SharedPreferences or SecureStorage
        // Implementation depends on environment
        return "your-auth-token";
    }
}

// SSL Certificate Pinning
public class SSLPinnedApiClient {
    public static Retrofit createSSLPinnedRetrofit() {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
            .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
            .build();
        
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .certificatePinner(certificatePinner)
            .build();
        
        return new Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    }
}

// Dynamic base URL configuration
public interface DynamicApiService {
    @GET
    Call<User> getUser(@Url String fullUrl);
    
    @GET("users/{id}")
    Call<User> getUserWithDynamicBase(@Path("id") int userId);
}

// Proxy configuration
public class ProxyApiClient {
    public static Retrofit createProxyRetrofit() {
        Proxy proxy = new Proxy(Proxy.Type.HTTP, 
            new InetSocketAddress("proxy.example.com", 8080));
        
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .proxy(proxy)
            .build();
        
        return new Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    }
}

File Upload & Multipart Forms

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import java.io.File;

// File upload API interface
public interface FileUploadService {
    // Single file upload
    @Multipart
    @POST("upload/single")
    Call<UploadResponse> uploadSingleFile(
        @Part("description") RequestBody description,
        @Part MultipartBody.Part file
    );
    
    // Multiple file upload
    @Multipart
    @POST("upload/multiple")
    Call<UploadResponse> uploadMultipleFiles(
        @Part("title") RequestBody title,
        @Part("category") RequestBody category,
        @Part List<MultipartBody.Part> files
    );
    
    // Profile update (image + data)
    @Multipart
    @PUT("users/{id}/profile")
    Call<User> updateProfile(
        @Path("id") int userId,
        @Part("name") RequestBody name,
        @Part("email") RequestBody email,
        @Part MultipartBody.Part avatar
    );
    
    // Form data submission
    @FormUrlEncoded
    @POST("users/register")
    Call<User> registerUser(
        @Field("username") String username,
        @Field("email") String email,
        @Field("password") String password,
        @Field("age") int age
    );
    
    // File download
    @GET("files/{fileId}/download")
    Call<ResponseBody> downloadFile(@Path("fileId") String fileId);
}

public class UploadResponse {
    public String fileId;
    public String fileName;
    public long fileSize;
    public String downloadUrl;
    public String message;
}

public class FileUploadRepository {
    private FileUploadService uploadService;
    
    public FileUploadRepository() {
        this.uploadService = ApiClient.getRetrofitInstance()
            .create(FileUploadService.class);
    }
    
    // Single file upload
    public void uploadSingleFile(File file, String description, 
                                ApiCallback<UploadResponse> callback) {
        // Convert file to RequestBody
        RequestBody requestFile = RequestBody.create(
            MediaType.parse("multipart/form-data"), file);
        
        // Create MultipartBody.Part
        MultipartBody.Part filePart = MultipartBody.Part.createFormData(
            "file", file.getName(), requestFile);
        
        // Convert description to RequestBody
        RequestBody descriptionBody = RequestBody.create(
            MediaType.parse("multipart/form-data"), description);
        
        Call<UploadResponse> call = uploadService.uploadSingleFile(descriptionBody, filePart);
        call.enqueue(new Callback<UploadResponse>() {
            @Override
            public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) {
                if (response.isSuccessful()) {
                    callback.onSuccess(response.body());
                } else {
                    callback.onError("Upload failed: " + response.code());
                }
            }
            
            @Override
            public void onFailure(Call<UploadResponse> call, Throwable t) {
                callback.onError("Upload error: " + t.getMessage());
            }
        });
    }
    
    // Multiple file upload
    public void uploadMultipleFiles(List<File> files, String title, String category,
                                   ApiCallback<UploadResponse> callback) {
        List<MultipartBody.Part> fileParts = new ArrayList<>();
        
        for (File file : files) {
            RequestBody requestFile = RequestBody.create(
                MediaType.parse("multipart/form-data"), file);
            MultipartBody.Part filePart = MultipartBody.Part.createFormData(
                "files", file.getName(), requestFile);
            fileParts.add(filePart);
        }
        
        RequestBody titleBody = RequestBody.create(
            MediaType.parse("multipart/form-data"), title);
        RequestBody categoryBody = RequestBody.create(
            MediaType.parse("multipart/form-data"), category);
        
        Call<UploadResponse> call = uploadService.uploadMultipleFiles(
            titleBody, categoryBody, fileParts);
        
        call.enqueue(new Callback<UploadResponse>() {
            @Override
            public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) {
                if (response.isSuccessful()) {
                    callback.onSuccess(response.body());
                } else {
                    callback.onError("Multiple upload failed: " + response.code());
                }
            }
            
            @Override
            public void onFailure(Call<UploadResponse> call, Throwable t) {
                callback.onError("Multiple upload error: " + t.getMessage());
            }
        });
    }
    
    // Profile avatar upload
    public void updateProfileWithAvatar(int userId, String name, String email, 
                                       File avatarFile, ApiCallback<User> callback) {
        RequestBody nameBody = RequestBody.create(
            MediaType.parse("multipart/form-data"), name);
        RequestBody emailBody = RequestBody.create(
            MediaType.parse("multipart/form-data"), email);
        
        RequestBody avatarRequestBody = RequestBody.create(
            MediaType.parse("image/*"), avatarFile);
        MultipartBody.Part avatarPart = MultipartBody.Part.createFormData(
            "avatar", avatarFile.getName(), avatarRequestBody);
        
        Call<User> call = uploadService.updateProfile(userId, nameBody, emailBody, avatarPart);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    callback.onSuccess(response.body());
                } else {
                    callback.onError("Profile update failed: " + response.code());
                }
            }
            
            @Override
            public void onFailure(Call<User> call, Throwable t) {
                callback.onError("Profile update error: " + t.getMessage());
            }
        });
    }
    
    // File download
    public void downloadFile(String fileId, File destinationFile, 
                            DownloadCallback callback) {
        Call<ResponseBody> call = uploadService.downloadFile(fileId);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if (response.isSuccessful()) {
                    // Save file in background
                    new Thread(() -> {
                        try {
                            saveFileFromResponseBody(response.body(), destinationFile);
                            callback.onDownloadComplete(destinationFile);
                        } catch (IOException e) {
                            callback.onDownloadError("File save error: " + e.getMessage());
                        }
                    }).start();
                } else {
                    callback.onDownloadError("Download failed: " + response.code());
                }
            }
            
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                callback.onDownloadError("Download error: " + t.getMessage());
            }
        });
    }
    
    private void saveFileFromResponseBody(ResponseBody body, File destinationFile) throws IOException {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        
        try {
            byte[] fileReader = new byte[4096];
            long fileSize = body.contentLength();
            long fileSizeDownloaded = 0;
            
            inputStream = body.byteStream();
            outputStream = new FileOutputStream(destinationFile);
            
            while (true) {
                int read = inputStream.read(fileReader);
                if (read == -1) break;
                
                outputStream.write(fileReader, 0, read);
                fileSizeDownloaded += read;
                
                Log.d("DOWNLOAD", "Downloaded: " + fileSizeDownloaded + " / " + fileSize);
            }
            
            outputStream.flush();
        } finally {
            if (inputStream != null) inputStream.close();
            if (outputStream != null) outputStream.close();
        }
    }
}

public interface DownloadCallback {
    void onDownloadComplete(File file);
    void onDownloadError(String error);
}

Error Handling and Detailed Response Processing

import retrofit2.HttpException;
import com.google.gson.JsonSyntaxException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

// Custom error response
public class ApiErrorResponse {
    public int code;
    public String message;
    public String details;
    public List<FieldError> fieldErrors;
    
    public static class FieldError {
        public String field;
        public String message;
    }
}

// Error handling utility
public class ApiErrorHandler {
    
    public static String handleError(Throwable throwable) {
        if (throwable instanceof HttpException) {
            return handleHttpException((HttpException) throwable);
        } else if (throwable instanceof SocketTimeoutException) {
            return "Connection timed out. Please check your network connection.";
        } else if (throwable instanceof UnknownHostException) {
            return "Cannot connect to server. Please check your internet connection.";
        } else if (throwable instanceof JsonSyntaxException) {
            return "Invalid response format from server.";
        } else if (throwable instanceof IOException) {
            return "Network error occurred: " + throwable.getMessage();
        } else {
            return "Unexpected error occurred: " + throwable.getMessage();
        }
    }
    
    private static String handleHttpException(HttpException exception) {
        int code = exception.code();
        
        try {
            String errorBody = exception.response().errorBody().string();
            Gson gson = new Gson();
            ApiErrorResponse errorResponse = gson.fromJson(errorBody, ApiErrorResponse.class);
            
            if (errorResponse != null && errorResponse.message != null) {
                return errorResponse.message;
            }
        } catch (Exception e) {
            // Fallback if error response parsing fails
        }
        
        switch (code) {
            case 400:
                return "Bad request. Please check your input.";
            case 401:
                return "Authentication required. Please login.";
            case 403:
                return "You don't have permission to perform this operation.";
            case 404:
                return "Requested resource not found.";
            case 409:
                return "Data conflict. Please get the latest information.";
            case 422:
                return "Input data has issues. Please check the content.";
            case 429:
                return "Request limit reached. Please wait and try again.";
            case 500:
                return "Internal server error. Please try again later.";
            case 502:
                return "Server temporarily unavailable.";
            case 503:
                return "Service temporarily unavailable. May be under maintenance.";
            default:
                return "HTTP error " + code + " occurred.";
        }
    }
}

// Detailed response processing
public class DetailedApiRepository {
    private UserApiService apiService;
    
    public DetailedApiRepository() {
        this.apiService = ApiClient.getRetrofitInstance().create(UserApiService.class);
    }
    
    // Callback with detailed response information
    public interface DetailedApiCallback<T> {
        void onSuccess(T result, Response<T> response);
        void onError(String error, int httpCode, Response<T> response);
        void onNetworkError(String error);
    }
    
    public void getUserWithDetailedResponse(int userId, DetailedApiCallback<User> callback) {
        Call<User> call = apiService.getUser(userId);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if (response.isSuccessful()) {
                    User user = response.body();
                    
                    // Check response headers
                    String rateLimit = response.headers().get("X-RateLimit-Remaining");
                    String serverTime = response.headers().get("Date");
                    
                    Log.d("API_INFO", "Rate limit remaining: " + rateLimit);
                    Log.d("API_INFO", "Server time: " + serverTime);
                    Log.d("API_INFO", "Response time: " + (System.currentTimeMillis() - call.request().tag()));
                    
                    callback.onSuccess(user, response);
                } else {
                    // Detailed error response processing
                    try {
                        String errorBody = response.errorBody().string();
                        Log.e("API_ERROR", "Error body: " + errorBody);
                        
                        Gson gson = new Gson();
                        ApiErrorResponse errorResponse = gson.fromJson(errorBody, ApiErrorResponse.class);
                        
                        String errorMessage = (errorResponse != null && errorResponse.message != null) 
                            ? errorResponse.message 
                            : ApiErrorHandler.handleError(new HttpException(response));
                        
                        callback.onError(errorMessage, response.code(), response);
                    } catch (IOException e) {
                        callback.onError("Failed to parse error response", response.code(), response);
                    }
                }
            }
            
            @Override
            public void onFailure(Call<User> call, Throwable t) {
                String errorMessage = ApiErrorHandler.handleError(t);
                callback.onNetworkError(errorMessage);
            }
        });
    }
    
    // Cancellable API call
    public Call<List<User>> getUsersWithCancellation(ApiCallback<List<User>> callback) {
        Call<List<User>> call = apiService.getUsers();
        
        call.enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if (!call.isCanceled()) {
                    if (response.isSuccessful()) {
                        callback.onSuccess(response.body());
                    } else {
                        callback.onError("API Error: " + response.code());
                    }
                }
            }
            
            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {
                if (!call.isCanceled()) {
                    callback.onError(ApiErrorHandler.handleError(t));
                }
            }
        });
        
        return call;  // Cancellable by caller
    }
}

// Usage example: API call with cancellation feature
public class CancellableApiActivity extends AppCompatActivity {
    private Call<List<User>> currentCall;
    private DetailedApiRepository repository;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        repository = new DetailedApiRepository();
        
        // Cancellable API call
        currentCall = repository.getUsersWithCancellation(new ApiCallback<List<User>>() {
            @Override
            public void onSuccess(List<User> users) {
                // Update UI
                updateUserList(users);
            }
            
            @Override
            public void onError(String error) {
                // Show error
                showError(error);
            }
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Cancel API call when activity is destroyed
        if (currentCall != null && !currentCall.isCanceled()) {
            currentCall.cancel();
        }
    }
    
    private void updateUserList(List<User> users) {
        // List update processing
    }
    
    private void showError(String error) {
        Toast.makeText(this, error, Toast.LENGTH_LONG).show();
    }
}

RxJava/Kotlin Coroutines Integration

// RxJava integration
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.schedulers.Schedulers;

// RxJava API interface
public interface RxUserApiService {
    @GET("users")
    Observable<List<User>> getUsers();
    
    @GET("users/{id}")
    Single<User> getUser(@Path("id") int userId);
    
    @POST("users")
    Single<User> createUser(@Body UserCreateRequest user);
}

// RxJava Retrofit client
public class RxApiClient {
    private static Retrofit retrofit;
    
    public static Retrofit getRxRetrofitInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                .baseUrl("https://api.example.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .build();
        }
        return retrofit;
    }
    
    public static RxUserApiService getRxUserApiService() {
        return getRxRetrofitInstance().create(RxUserApiService.class);
    }
}

public class RxUserRepository {
    private RxUserApiService apiService;
    
    public RxUserRepository() {
        this.apiService = RxApiClient.getRxUserApiService();
    }
    
    // API call using RxJava
    public void loadUsersWithRx() {
        apiService.getUsers()
            .subscribeOn(Schedulers.io())                    // Execute on background thread
            .observeOn(AndroidSchedulers.mainThread())       // Receive results on main thread
            .subscribe(
                users -> {
                    // Success handling
                    Log.d("RX_SUCCESS", "Retrieved users: " + users.size());
                },
                throwable -> {
                    // Error handling
                    String error = ApiErrorHandler.handleError(throwable);
                    Log.e("RX_ERROR", error);
                }
            );
    }
    
    // Combining multiple API calls
    public void loadUserWithDetails(int userId) {
        Single.zip(
            apiService.getUser(userId),
            apiService.getUserPosts(userId),
            apiService.getUserFollowers(userId),
            (user, posts, followers) -> {
                // Combine three API responses
                UserWithDetails userDetails = new UserWithDetails();
                userDetails.user = user;
                userDetails.posts = posts;
                userDetails.followers = followers;
                return userDetails;
            }
        )
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(
            userDetails -> {
                // Process integrated data
                Log.d("RX_SUCCESS", "User details loaded");
            },
            throwable -> {
                Log.e("RX_ERROR", ApiErrorHandler.handleError(throwable));
            }
        );
    }
}
// Kotlin Coroutines integration
import kotlinx.coroutines.*

// Kotlin Suspend Function API interface
interface CoroutineUserApiService {
    @GET("users")
    suspend fun getUsers(): List<User>
    
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: Int): User
    
    @POST("users")
    suspend fun createUser(@Body user: UserCreateRequest): User
    
    @PUT("users/{id}")
    suspend fun updateUser(@Path("id") userId: Int, @Body user: UserCreateRequest): User
    
    @DELETE("users/{id}")
    suspend fun deleteUser(@Path("id") userId: Int): Response<Void>
}

class CoroutineUserRepository {
    private val apiService = ApiClient.getRetrofitInstance()
        .create(CoroutineUserApiService::class.java)
    
    // API call using Coroutines
    suspend fun getUsers(): Result<List<User>> {
        return try {
            val users = apiService.getUsers()
            Result.success(users)
        } catch (e: Exception) {
            val errorMessage = ApiErrorHandler.handleError(e)
            Result.failure(Exception(errorMessage))
        }
    }
    
    suspend fun getUserById(userId: Int): Result<User> {
        return try {
            val user = apiService.getUser(userId)
            Result.success(user)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
    
    // Parallel execution of multiple API calls
    suspend fun getUserWithDetailsParallel(userId: Int): Result<UserWithDetails> {
        return try {
            val userDeferred = async { apiService.getUser(userId) }
            val postsDeferred = async { apiService.getUserPosts(userId) }
            val followersDeferred = async { apiService.getUserFollowers(userId) }
            
            val user = userDeferred.await()
            val posts = postsDeferred.await()
            val followers = followersDeferred.await()
            
            val userDetails = UserWithDetails(user, posts, followers)
            Result.success(userDetails)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

// Coroutine usage example in Activity/Fragment
class CoroutineActivity : AppCompatActivity() {
    private val repository = CoroutineUserRepository()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Execute Coroutine in lifecycle scope
        lifecycleScope.launch {
            loadUsers()
        }
    }
    
    private suspend fun loadUsers() {
        try {
            // Show loading
            showLoading(true)
            
            // API call
            val result = repository.getUsers()
            
            result.fold(
                onSuccess = { users ->
                    // Success handling
                    updateUserList(users)
                    showLoading(false)
                },
                onFailure = { exception ->
                    // Error handling
                    showError(exception.message ?: "Unknown error")
                    showLoading(false)
                }
            )
        } catch (e: Exception) {
            showError("Unexpected error: ${e.message}")
            showLoading(false)
        }
    }
    
    private fun updateUserList(users: List<User>) {
        // UI update processing
    }
    
    private fun showLoading(show: Boolean) {
        // Show/hide loading
    }
    
    private fun showError(message: String) {
        // Show error
    }
}