Retrofit and Centralized Error Handling

Not sure I understood it correctly, but you could create one Callback and pass it as a parameter to all of your requests.

Instead of:

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

    } 

    @Override 
    public void failure(RetrofitError error) { 

    }
}); 

First define your Callback:

Callback<gitmodel> mCallback = new Callback<gitmodel>() {
    @Override 
    public void success(gitmodel gitmodel, Response response) { 

    } 

    @Override 
    public void failure(RetrofitError error) { 
        // logic to handle error for all requests
    } 
};

Then:

git.getFeed(user, mCallback);

In Retrofit2 you can't set an ErrorHandler with the method .setErrorHandler(), but you can create an interceptor to fork all possible errors centralised in one place of your application.

With this example you have one centralised place for your error handling with Retrofit2 and OkHttpClient. Just reuse the Retrofit object (retrofit).

You can try this standalone example with a custom interceptor for network and server errors. These both will be handled differently in Retrofit2, so you have to check the returned error code from the server over the response code (response.code()) and if the response was not successful (!response.isSuccessful()).

For the case that the user has no connection to the network or the server you have to catch an IOException of the method Response response = chain.proceed(chain.request()); and handle the network error in the catch block.

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    try {
                        Response response = chain.proceed(chain.request());
                        if (!response.isSuccessful()) {
                            Log.e("tag", "Failure central - response code: " + response.code());
                            Log.e("tag", "central server error handling");

                            // Central error handling for error responses here:
                            // e.g. 4XX and 5XX errors
                            switch (response.code()) {
                                case 401:
                                    // do something when 401 Unauthorized happened
                                    // e.g. delete credentials and forward to login screen
                                    // ...

                                    break;
                                case 403:
                                    // do something when 403 Forbidden happened
                                    // e.g. delete credentials and forward to login screen
                                    // ...

                                    break;
                                default:
                                    Log.e("tag", "Log error or do something else with error code:" + response.code());

                                    break;
                            }
                        }

                        return response;
                    } catch (IOException e) {
                        // Central error handling for network errors here:
                        // e.g. no connection to internet / to server

                        Log.e("tag", e.getMessage(), e);
                        Log.e("tag", "central network error handling");

                        throw e;
                    }
                }
            })
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://10.0.2.2:8000/api/v1/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    UserRepository backendRepository = retrofit.create(UserRepository.class);
    backendRepository.getUser("userId123").enqueue(new Callback<UserModel>() {
        @Override
        public void onResponse(Call<UserModel> call, retrofit2.Response<UserModel> response) {
            Log.d("tag", "onResponse");

            if (!response.isSuccessful()) {
                Log.e("tag", "onFailure local server error handling code:" + response.code());
            } else {
                // its all fine with the request


            }
        }

        @Override
        public void onFailure(Call<UserModel> call, Throwable t) {
            Log.e("tag", "onFailure local network error handling");
            Log.e("tag", t.getMessage(), t);

        }
    });

UserRepository example:

public interface UserRepository {
    @GET("users/{userId}/")
    Call<UserModel> getUser(@Path("userId") String userId);

}

UserModel example:

public class UserModel implements Parcelable {
    @SerializedName("id")
    @Expose
    public String id = "";

    @SerializedName("email")
    @Expose
    public String mail = "";

    public UserModel() {

    }

    protected UserModel(Parcel in) {
        id = in.readString();
        mail = in.readString();
    }

    public static final Creator<UserModel> CREATOR = new Creator<UserModel>() {
        @Override
        public UserModel createFromParcel(Parcel in) {
            return new UserModel(in);
        }

        @Override
        public UserModel[] newArray(int size) {
            return new UserModel[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(mail);
    }
}

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...)


In Retrofit you can specify ErrorHandler to all requests.

public class ApiErrorHandler implements ErrorHandler {

    @Override
    public Throwable handleError(RetrofitError cause) {
        //here place your logic for all errors
        return cause;
    }
}

Apply it to RestAdapter

RestAdapter.Builder()
            .setClient(client)
            .setEndpoint(endpoint)
            .setErrorHandler(errorHandler)
            .build();

I think that it is what you asked for.