Understanding Android Networking Library and Best way for Handling Network Error— Retrofit 2

Krupa Desai
The Startup
Published in
4 min readNov 18, 2020

--

Networking is one of the most important parts of Android applications. At the early stage, we wrote our own HTTP classes to handle networking. As time passed development got easier making us dependent on libraries. We use many libraries to get the work done faster but most of us do not analyse its limitations and drawbacks. Before using any library we need to analyse three things What, Why and How. One popular library which we use for Networking is Retrofit. In this post, let’s analyse these things and understand how the request gets processed inside Retrofit.

What?

Retrofit is a type-safe HTTP client for Android and Java.

Why?

Using Retrofit made networking easier in Android apps. As it has many features like,

  1. Easy to connect to web-services by translating the API into Java or Kotlin.
  2. Easy to add Headers and request types.
  3. Easily Customisable, you can customise it and add say any convertors like Gson, JackSon, Moshi, Prtobuf, XML etc. You can also customise it to add different interceptors and cache.
  4. It provides additional functionalities such as custom headers, file uploads, downloads, mocking responses (for testing).

How?

To work with Retrofit we basically need the following three classes:

  • A model class which is used as a JSON model
  • An interface that defines the HTTP operations needs to be performed
  • Retrofit.Builder class: Instance which uses the interface defined above and the Builder API to allow defining the URL endpoint for the HTTP operations. It also takes the converters we provide to format the Response.

Basic OverView

To implement retrofit in our apps we need to follow basic steps

  1. Add required dependencies in app-level build.gradle
implementation ‘com.squareup.retrofit2:retrofit:2.3.0’
implementation ‘com.squareup.retrofit2:converter-gson:2.3.0’

for interceptor implementation

implementation ‘com.squareup.okhttp3:logging-interceptor:3.9.0’

for BuildType : (if you want for various environment) in app level build.gradle

buildTypes { 
debug
{
buildConfigField “String”, “SERVER_URL”, ‘“DEV_URL”’ debuggable true }
release
{
buildConfigField “String”, “SERVER_URL”, ‘“LIVE_URL”’ minifyEnabled false
}
}

If environment is set Define BASE_URL using set environment:

String BASE_URL = BuildConfig.SERVER_URL + “url”;

========================================

Create a class called RestClient For Retrofit Builder :

public class RestClient {private static RestClient instance;
private ApiConstant apiInterface;
private OkHttpClient.Builder okHttpClientBuilder;
public RestClient() {
instance = this;
okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder.connectTimeout(60, TimeUnit.SECONDS);
okHttpClientBuilder.writeTimeout(60, TimeUnit.SECONDS);
okHttpClientBuilder.readTimeout(60, TimeUnit.SECONDS);
//for logs of api response in debug mode if (BuildConfig.DEBUG) {
final HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClientBuilder.addInterceptor(interceptor);
}
}
public static RestClient getInstance() {
return instance;
}
public ApiConstant getApiInterface() { final Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
retrofitBuilder.baseUrl(ApiConstant.BASE_URL);
retrofitBuilder.client(okHttpClientBuilder.build());
retrofitBuilder.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = retrofitBuilder.build();
apiInterface = retrofit.create(ApiConstant.class);
return apiInterface;
}
}

========================================
ApiInterface : (an interface for api calls)

public interface ApiInterface {@POST("Urls Params")
Call<ModelClass> postValidateToken(@Body JSONObject body);}

========================================
Create Generic class called RetrofitCallback which handles Api response and Throws basic errors and show Toast Message to user:

public abstract class RetrofitCallback<T> implements Callback<T> {private ProgressDialog progressDialog;
private Context context;
private boolean validateResponse = true;
public RetrofitCallback(Context c)
{
context = c;
}
public RetrofitCallback(Context c, ProgressDialog dialog) {
progressDialog = dialog;
context = c;
}

public RetrofitCallback(Context context, ProgressDialog progressDialog, boolean validateResponse)
{
this.progressDialog = progressDialog;
this.context = context;
this.validateResponse = validateResponse;
}
public RetrofitCallback(Context context, boolean validateResponse) {
this.context = context;
this.validateResponse = validateResponse;
}
public abstract void onSuccess(T arg0);
@Override
public void onResponse(Call<T> call, Response<T> response) {
if (!(((Activity) context).isFinishing()) && progressDialog != null && progressDialog.isShowing()) {

if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
if (response.isSuccessful() && response.code() == 200) {
onSuccess(response.body());
}
else
{ Toast.makeText(context,context.getString(R.string.something_wrong),
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<T> call, Throwable error) {
if (!validateResponse)
return; String errorMsg;
error.printStackTrace();
if (error instanceof SocketTimeoutException) {
errorMsg = context.getString(R.string.connection_timeout);
} else if (error instanceof UnknownHostException) {
errorMsg = context.getString(R.string.nointernet);
} else if (error instanceof ConnectException) {
errorMsg = context.getString(R.string.server_not_responding);
} else if (error instanceof JSONException || error instanceof JsonSyntaxException) {
errorMsg = context.getString(R.string.parse_error);
} else if (error instanceof IOException) {
errorMsg = error.getMessage();
} else {
errorMsg = context.getString(R.string.something_wrong);
} if (progressDialog != null && progressDialog.isShowing()) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
}
}

========================================
Implementation (How to call in Activity or Fragment):

Call<User> userLoginCall = RestClient.getInstance().getApiInterface().LoginURL(Util.currentLanguage,
Util.getLoginURL(getActivity(), email, LOGIN_GOOGLE, accID));
userLoginCall.enqueue(new RetrofitCallback<User>(getActivity(), DialogUtils.showProgressDialog(getActivity())) {
@Override
public void onSuccess(User user) {
//Your Response
}
});

This guide is aim to simplify understanding for RETROFIT Networking Library.

Workflow of Handling Network Error:

This is a common practice for most of the developers to have a network check before each and every API call. I also did the same and i got bored. Then i started looking for an optimal way. And finally i got the solution by adding an Interceptor with okhttpClientBuilder.

  • Create a class NetworkConnectionInterceptor . And override the method intercept(Chain chian) to check internet connection availability inside it.

Now if we add this NetworkConnectionInterceptor interceptor with Retrofit Service client, it will handle every API call. Before each and every API call intercept(Chain chain) method will be called and it will check for internet connectivity. If no network found then it will interrupts the normal flow of execution and throw NoConnectivityException exception. The exception class looks like below.

  • So add NetworkConnectionInterceptor interceptor with your retrofit Service client.
  • Finally, catch the exception in onFailure(Call<BaseModel> call, Throwable e) or onError(Throwable e) method and show the network error message to user.

That’s it. You have done. !!!!!!!!!!

--

--