Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Divide Retrofit service declaration into multiple interfaces

I am creating an app linking to an API with about 265 methods to it. I would very much like to break apart the declaration of these APIs into multiple files to keep them organized and accessible. However Retrofit explicitly disallows combining interfaces through extension.

java.lang.IllegalArgumentException: Interface definitions must not extend other interfaces.

I had been trying to declare it as follows.

public interface ApiService extends ProfileService, AccountService {
    // Empty interface, methods divided into other services
}

public interface ProfileService {
    @GET("/api/v1/protected/profile")
    public void getProfile(Callback<Profile> callback);

    ...
}

public interface AccountService {
    @GET("/api/v1/protected/account")
    public void getAccount(Callback<Account> callback);


    ...
}

There is a discussion about this issue on a pull request. The library authors have decided that extending interfaces like this is not in the spirit of the library. https://github.com/square/retrofit/pull/676

Jake Wharton (the author) says that "Retrofit favors composition." And in response to "Do you really have a single adapter with a ton of proxies?", "Yes. They are generated from a service declaration in protos. One interface per service."

I've been reading about composition vs inheritance and fail to grasp how to achieve the goal of breaking up the declaration.

How can I divide the interface declaration up? Is there a best practice that I'm missing?

Thank you.

like image 911
Derek Avatar asked Apr 23 '15 16:04

Derek


2 Answers

Just create separate interfaces.

public interface ProfileService {

  /* ... */
}

public interface AccountService {

  /* ... */
}

ProfileService profileService = mRestAdapter.create(ProfileService.class);
AccountService accountService = mRestAdapter.create(AccountService.class);
like image 72
nhaarman Avatar answered Nov 17 '22 15:11

nhaarman


I am still experimenting on if this is the best way to go about using this but here is what I have so far. It may not be the cleanest approach yet but I am liking it versus one service with 100 api calls. Splits it up a little and makes it easier to read.

This is the main class to access the data. I have seen a lot separate out the two static methods I have into a separate class but i just included it as one.

public class RetrofitApi {
   public enum ApiTypes {
        USER_API(UserApi.class);

        private final Class<? extends RetrofitApi> apiClass;
        ApiTypes(Class<? extends RetrofitApi> apiClass){
            this.apiClass = apiClass;
        }
        Class<? extends RetrofitApi> getApiType() {return this.apiClass;}
    }
    public static  <T> T getApi(RetrofitApi.ApiTypes type) {
        try {
            return (T) type.getApiType().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
    public static RestAdapter getRestAdapter() {
        RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(BASE_URL)
            .setLogLevel(retrofit.RestAdapter.LogLevel.HEADERS)
            .build();
        return restAdapter;
    }
}

Each service has its own api. This does mean more classes. I split them into api, service, model. API is the high level that you will use and expose. Service is more or less just a list of calls. And Model is the model (data object).

public class UserApi extends RetrofitApi {

    private UserService service;

    public UserApi() {
        RestAdapter restAdapter =
                RetrofitApi.getRestAdapter();
        service = restAdapter.create(UserService.class);
    }

    public void login(String email,
                      String password,
                      Callback<User> callback) {
        service.login(email, password, callback);
    }
}

Service is the interface. Its more or less just a list of api calls that get exposed.

public interface UserService {

    @GET("/api/users/login")
    void login(@Query("email") String email,
                   @Query("password") String password,
                   Callback<User> callback);
}

Then to use it.

 UserApi api = RetrofitApi.getApi(RetrofitApi.ApiTypes.USER_API);
 api.login(email,password,callback);

And here is the project structure. To me, it seems clean at the moment. I am sure it will end up getting big when I have 20+ of them. But it might be a little cleaner when those 20 have multiple calls. enter image description here

like image 5
Kalel Wade Avatar answered Nov 17 '22 16:11

Kalel Wade