将Retrofit服务声明分成多个接口

Divide Retrofit service declaration into multiple interfaces

我正在创建一个链接到 API 的应用程序,其中包含大约 265 种方法。我非常想将这些 API 的声明分解成多个文件,以保持它们的组织性和可访问性。但是 Retrofit 明确禁止通过扩展组合接口。

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

我一直在尝试声明如下。

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


    ...
}

在拉取请求中有关于此问题的讨论。库作者已经决定像这样扩展接口不符合库的精神。 https://github.com/square/retrofit/pull/676

Jake Wharton(作者)说 "Retrofit favors composition." 并回应 "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."

我一直在阅读有关组合与继承的内容,但未能掌握如何实现分解声明的目标。

如何划分接口声明?是否有我遗漏的最佳实践?

谢谢。

只需创建单独的接口。

public interface ProfileService {

  /* ... */
}

public interface AccountService {

  /* ... */
}

ProfileService profileService = mRestAdapter.create(ProfileService.class);
AccountService accountService = mRestAdapter.create(AccountService.class);

我仍在试验这是否是使用它的最佳方式,但这是我目前所拥有的。它可能不是最干净的方法,但我喜欢它而不是一个有 100 api 个调用的服务。稍微拆分一下,使其更易于阅读。

这是访问数据的主要class。我已经看到很多将我拥有的两个静态方法分开成一个单独的 class 但我只是将它作为一个包含在内。

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;
    }
}

每项服务都有自己的 api。这确实意味着更多 classes。我将它们拆分为 api、服务、模型。 API 是您将使用和公开的高级别。服务或多或少只是一个电话列表。而Model就是模型(数据对象)。

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);
    }
}

服务就是接口。它或多或少只是暴露的 api 个调用列表。

public interface UserService {

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

然后使用它。

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

这是项目结构。对我来说,目前看起来很干净。我相信当我有 20 多个时,它最终会变大。但是当这 20 个有多个调用时,它可能会更干净一些。

您可以尝试以下方法。

创建2接口class.

  • ARetrofitApi
  • BRetrofitApi

拨打电话

// ARetrofitApi

@FormUrlEncoded
@POST("get_favorite_food/{id}")
Call<JsonObject> getFavoriteFood(@Path("id") String userId,
                          @Field("image") String image,
                          @Field("name") String foodName);

// BRetrofitApi

@FormUrlEncoded
@POST("get_favorite_sport/{id}")
Call<JsonObject> getFavoriteSport(@Path("id") String userId,
                          @Field("image") String image,
                          @Field("name") String sportName);

In You Builder

private static final String BASE_URL = "http://192.168.1.4/myapp/";

private static RetrofitClient mInstance;
private Retrofit retrofit;

private RetrofitClient() {

retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
                                     .addConverterFactory(GsonConverterFactory.create())
                                     .build();
}

public static synchronized RetrofitClient getInstance() {
    if (mInstance == null) {
        mInstance = new RetrofitClient();
    }
    return mInstance;
}

public ARetrofitApi aGetApi() {
    return retrofit.create(ARetrofitApi.class);
}

public BRetrofitApi bGetApi() {
    return retrofit.create(BRetrofitApi.class);
}

来电

// Use RetroFit To Feed To MySQL
Call<JsonObject> call = RetrofitClient.getInstance()
                                  .aGetApi()
                                  .getFavoriteFood(userId,
                                               image,
                                               foodName);

// Use RetroFit To Feed To MySQL
Call<JsonObject> call = RetrofitClient.getInstance()
                                  .bGetApi()
                                  .getFavoriteSport(userId,
                                               image,
                                               sportName);

遗憾的是,Retrofit 似乎不支持也不会支持多接口。我很遗憾地说,因为这意味着我们必须开始找出 not-so-optimal 解决方案。

如果对某人有帮助,这是我目前的解决方案(在 Kotlin 中,使用 Moshi 作为转换器):

class MyApi {
    companion object {
        private val interceptor = HttpLoggingInterceptor()
            .setLevel(HttpLoggingInterceptor.Level.BODY)

        private val moshiConverterFactory = MoshiConverterFactory
            .create(Moshi.Builder().build())

        private val retrofit = Retrofit.Builder()
            .baseUrl("http://example.com/")
            .addConverterFactory(moshiConverterFactory)
            .addClient(client)
            .build()

        val firstApi: MyFirstApi by lazy {
            retrofit.create(MyFirstApi::class.java)
        }

        val secondApi: MySecondApi by lazy {
            retrofit.create(MySecondApi::class.java)
        }
    }
}

如您所见,我正在使用伴随对象来保留单个实例。此外,by lazy 以确保每次调用 firstApisecondApi 时不会调用 create()