mercredi 19 mai 2021

Good design for common success / failure / error handling for multiple APIs using Retrofit Android

I want to design API calls in such a way that it will be easy to handle success and failure responses easily from one place (instead of writing same code of call function for all APIs)

Here are the scenarios which I want to consider.

  • Handle success / failure and error responses like 4xx, 5xx etc of all APIs at one central place.
  • Want to cancel enqueue requests and also stop processing response if request is already sent in case of logout (because response parsing will modify some global data of app)
  • If access token has expired and 401 response received from cloud, it should get new token and then call API again automatically with new token. I know little bit about Retrofit Authenticator. I think for this point it is suitable but it requires synchronous call to get new token using refresh token. If Authenticator is the only option then I can convert asynchronous call to blocking calls.

My current implementation is not satisfying above requirements.

Is there any way to implement API calls which satisfy above requirements using Retrofit ? Please suggest me a good design for this.

Here is my current implementation :

  1. ApiInterface.java - It is an interface which contains different API calls definitions.
  2. ApiClient.java - To get retrofit client object to call APIs.
  3. ApiManager.java - It has methods to call APIs and parse their responses.

ApiInterface.java

public interface ApiInterface {

    // Get Devices
    @GET("https://example-base-url.com" + "/devices")
    Call<ResponseBody> getDevices(@Header("Authorization) String token);

    // Get device details
    @GET("https://example-base-url.com" + "/devices")
    Call<ResponseBody> getDevice(@Header("Authorization) String token, @Query("device_id") String deviceId);

    // Add device
    @PUT("https://example-base-url.com" + "/devices")
    Call<ResponseBody> addDevice(@Header("Authorization) String token, @Body DeviceRequest deviceAddBody);

    // Delete device
    @PUT("https://example-base-url.com" + "/devices")
    Call<ResponseBody> deleteDevice(@Header("Authorization) String token, @Query("device_id") String deviceId);
}

ApiClient.java

public class ApiClient {
    
    private static Retrofit retrofitClient = null;
    
    static Retrofit getClient(Context context) {

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), systemDefaultTrustManager())
                    .connectTimeout(15, TimeUnit.SECONDS)
                    .writeTimeout(15, TimeUnit.SECONDS)
                    .readTimeout(15, TimeUnit.SECONDS)
                    .build();

        retrofitClient = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(okHttpClient)
                .build();
    }
}

ApiManager.java

public class ApiManager {

private static ApiManager apiManager;

    public static ApiManager getInstance(Context context) {
        if (apiManager == null) {
            apiManager = new ApiManager(context);
        }
        return apiManager;
    }

    private ApiManager(Context context) {
        this.context = context;
        apiInterface = ApiClient.getClient(context).create(ApiInterface.class);   
    }

    public void getDevices() {
        // API call and response handling
    }

    public void getDevice(String deviceId) {
        // API call and response handling
    }

    public void addDevice(DeviceRequest deviceAddBody) {
        // API call and response handling
    }

    public void deleteDevice(String deviceId) {
        // API call and response handling
    }
}

Aucun commentaire:

Enregistrer un commentaire