Ma7moud El-Naggar Ma7moud El-Naggar - 9 days ago 5
Android Question

Retrofit and Centralized Error Handling

Each request to the server may return

error_code
. I want to handle these error in one place
when I was using AsyncTask I had a BaseAsyncTask like that

public abstract class BaseAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {

protected Context context;
private ProgressDialog progressDialog;
private Result result;

protected BaseAsyncTask(Context context, ProgressDialog progressDialog) {
this.context = context;
this.progressDialog = progressDialog;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
HttpResponse<ErrorResponse> response = (HttpResponse<ErrorResponse>) result;
if(response.getData().getErrorCode() != -1) {
handleErrors(response.getData());
}else
onResult(result);

}

private void handleErrors(ErrorResponse errorResponse) {
}
public abstract void onResult(Result result);
}


But, using retrofit each request has its error handling callback:

git.getFeed(user,new Callback<gitmodel>() {
@Override
public void success(gitmodel gitmodel, Response response) {

}

@Override
public void failure(RetrofitError error) {

}
});
}
});


How can I handle all errors in one place?

Answer

If you need to get some 'logic' error, then you need some Java logic since it's not a Retrofit feature so basically:

  1. Create a Your implementation Callback that implements the Retrofit Callback
  2. Create a base object that define the method 'isError'
  3. Modify Retrofit RestAdapter in order to get your Callback instead of the Retrofit One

MyCallback.java

import android.util.Log;
import retrofit.Callback;
import retrofit.client.Response;

public abstract class MyCallback<T extends MyObject> implements Callback<T> {

    @Override
    public final void success(T o, Response response) {
        if (o.isError()) {
            // [..do something with error]
            handleLogicError(o);
        }
        else {
            handleSuccess(o, response);
        }
    }

    abstract void handleSuccess(T o, Response response);

    void handleLogicError(T o) {
        Log.v("TAG", "Error because userId is " + o.id);
    }
}

MyObject.java (the base class for all your objects you get from Retrofit)

public class MyObject {
    public long id;
    public boolean isError() {
        return id == 1;
    }
}

MyRealObject.java - a class that extends the base object

public class MyRealObject extends MyObject {
    public long userId;
    public String title;
    public String body;
}

RetroInterface.java - the interface used by retrofit you should be familiar with

import retrofit.http.GET;
import retrofit.http.Path;

public interface RetroInterface {

    @GET("/posts/{id}")
    void sendGet(@Path("id") int id, MyCallback<MyRealObject> callback);

}

And finally the piece of code where you use all the logic

    RestAdapter adapter = new RestAdapter.Builder()
            .setEndpoint("http://jsonplaceholder.typicode.com")
            .build();

    RetroInterface itf = adapter.create(RetroInterface.class);
    itf.sendGet(2, new MyCallback<MyRealObject>() {
        @Override
        void handleSuccess(MyRealObject o, Response response) {
            Log.v("TAG", "success");
        }

        @Override
        public void failure(RetrofitError error) {
            Log.v("TAG", "failure");
        }
    });

If you copy and paste this code, you'll get an error when you'll execute the itf.sendGet(1, new MyCallback..) and a success for itf.sendGet(2, new MyCallback...)