Retrofit is an HTTP client for Android and Java written by Square Inc.
Today I want to expose a way to handle network exceptions for all your service calls and creating your generic error messages in a simple way when using Retrofit.
GenericException class
We will create a GenericException class where we will put all the types which we want to catch and their corresponding error messages we want to display. SocketException, and I have created a custom exception: InternalServerError. Please find below the code for the GenericException class:
public class GenericException extends IOException { Context context; String message; public GenericException(Context context, Exception e) { this.context = context; message = context.getString(R.string.str_internet_connection_error); if (e instanceof SocketException) { message = context.getString(R.string.str_internet_connection_error); } else if (e instanceof InternalServerError) { message = context.getString(R.string.str_internal_server_error); } } @Override public String getMessage() { return message; } } class InternalServerError extends IOException {}
OkHttpClient – Request Interceptor
Retrofit uses OkHttpClient which allows you to add an “interceptor” method. This will allow you to intercept all requests made on your retrofit services. As a result, you can make (common) changes to your requests (e.g add Auth Token) and also inspect the corresponding response and status code.
Below is a code snippet is interceptor method on the Network Module. Notice that we are throwing error using instance of our GenericException class. These exceptions will be propagated to the Activities (or presenter) where the services requests were made.
I checked for 500 status code on the response to catch Internal Server Error and to throw our custom Internal Server Error which will use the custom Exception Message. We also have SocketException to handle timeout exceptions for no internet connection scenarios.
@Singleton @Provides public APIService getService(Context context, OkHttpClient.Builder httpClient, Retrofit.Builder retrofit) { httpClient.addInterceptor((Interceptor.Chain chain) -> { if (!NetworkUtils.isNetworkAvailable(context)) { // Check if there is internet connection throw new GenericException(context, new SocketException()); } Request originalRequest = chain.request(); // set OAuth token Request.Builder newRequest = originalRequest.newBuilder(); String accessToken = SharedPreferencesRepository.getAuthToken(context); newRequest.header("Content-Type", "application/json"); newRequest.header("Accept", "application/json"); newRequest.header("Authorization", "Bearer " + accessToken).method(originalRequest.method(), originalRequest.body()); originalRequest = newRequest.build(); Response response = chain.proceed(originalRequest); //perform request, here original request will be executed int responseCode = response.code(); if (responseCode == 500) { // Check for Internal Server errors throw new GenericException(context, new InternalServerError()); // Internal Server Error } if (responseCode == 401) { . // Expired or invalid Auth Token // Unauthorized. E.g, Token Expired // refreshToken service call . . . } return response; }); retrofit .baseUrl(baseUrl) .client(httpClient.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); return retrofit.build().create(APIService.class); }
Propagating exception message to the UI
If you are using Rx Java, you will have something as below. The method (Throwable e) will get called whenever there is an exception thrown from the apiService. Exception messages which were defined on the GenericException class are thrown to (Throwable e) method. The exception message “e.getMessage()” can now be displayed to the users, e.g, on an AlertDialog.
Subscription s = apiService.loginRequest(userLoginRequest) .compose(RxUtils.applySchedulers()) .subscribe( (UserLoginResponse userLoginResponse) -> { // on Success mView.hideLoading(); if (userLoginResponse.isSuccess()) { mView.onLoginSuccessful(); } else new Throwable(context.getString(R.string.str_login_invalid_credentials)); }, (Throwable e) -> { // on Fail mView.onLoginFailed(e.getMessage()); // Display exception on an AlertDialog mView.hideLoading(); }, () -> { // on Complete mView.hideLoading(); });
Looking forward for next posts. Let me know if you have any queries or if you want me to share additional codes.
Cheers,