【问题标题】:Retrofit2 MVP AndroidRetrofit2 MVP Android
【发布时间】:2018-08-30 12:10:24
【问题描述】:

我在我的项目中使用改造,我想知道是否有办法在不同的类上分离对 api 的调用,例如: 仅登录活动 /api/users/login 仅限电影活动 /api/movies/all 我都在同一个界面上,我发现这不是一个好方法......你能指导我如何让它更干净吗?我正在使用 MVP 架构来做到这一点。

这是我的NetworkService.class

public class NetworkService {

    private NetworkAPI networkAPI;
    private OkHttpClient okHttpClient;
    private LruCache<Class<?>, Observable<?>> apiObservables;

    public NetworkService() {
        this(BASE_URL);
    }

    public NetworkService(String baseUrl) {
        okHttpClient = buildClient();
        apiObservables = new LruCache<>(10);

        Gson gson = new GsonBuilder()
                .setLenient()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .create();
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        // set your desired log level
        //logging.setLevel(Level.BASIC);
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);

        Builder httpClient = new Builder()
                .connectTimeout(100, TimeUnit.SECONDS)
                .readTimeout(100, TimeUnit.SECONDS);

        httpClient.addInterceptor(logging);
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(httpClient.build())
                .build();

        networkAPI = retrofit.create(NetworkAPI.class);
    }

    /**
     * Method to return the API interface.
     *
     * @return
     */
    public NetworkAPI getAPI() {
        return networkAPI;
    }


    /**
     * Method to build and return an OkHttpClient so we can set/get
     * headers quickly and efficiently.
     *
     * @return
     */
    public OkHttpClient buildClient() {

        Builder builder = new Builder();

        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response response = chain.proceed(chain.request());
                // Do anything with response here
                //if we want to grab a specific cookie or something..
                return response;
            }
        });

        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                //this is where we will add whatever we want to our request headers.
                Request request = chain.request().newBuilder().addHeader("Accept", "application/json").build();
                return chain.proceed(request);
            }
        });

        return builder.build();
    }

    /**
     * Method to clear the entire cache of observables
     */
    public void clearCache() {
        apiObservables.evictAll();
    }
}

我的NetworkAPI.class 有这个

public interface NetworkAPI {


    @POST(LOGIN)
    Call<LoginResponse> login(@Body LoginRequest loginRequest);
    //And more calls...
}

你们知道我能不能做得更干净吗?

【问题讨论】:

  • 我真的建议你看看一些开源项目。 GitHub 上有数以千计的项目。祝你好运!

标签: java android retrofit2 android-mvp


【解决方案1】:

制作retrofit 实例单例,然后您可以为登录电影创建服务,例如

   LoginService login =  retrofit.create(LoginService.class);
   login.login(loginRequest)

   MovieService movie =  retrofit.create(MovieService.class);
   movie.movies(movieRequest)

你的界面是这样的

public interface LoginService {


    @POST(LOGIN)
    Call<LoginResponse> login(@Body LoginRequest loginRequest);
    //And more calls...
}

public interface MovieService {


    @POST(MOVIE)
    Call<MovieResponse> movies(@Body MovieRequest movieRequest);
    //And more calls...
}

【讨论】:

  • 如何更改 NetworkService 以使其干净?我给你介绍了接口,这是一个很好的观点,但是 NetworkService 很好吗?
  • 有时我也看到人们将其设为自己的.addConverterFactory() 我应该吗?它的目的是什么?
  • 这不好。 RetrofitBuilder 需要一个基本 url(每个 API 接口都不同),除非你为每个 API 传递完整的 URL,否则你不能拥有像 retrofit instance singleton 这样的东西。
  • 这是为了将您的数据可能的 json 转换为对象。例如,如果你想使用 rxjava,你需要使用适配器来实现 kotlin 协程,你需要不同的适配器
  • 那么他需要另一个带有该 url 的改造对象。或更改单例中的网址。但我不认为我需要这么多 api 也使用单例会比在每个活动和片段中创建 Retrofit 对象要快得多。
【解决方案2】:

肯定需要优化的一件事是您应该重用 OkHttpClient,而不是为每个 API 创建一个。

Retrofit API 创建也应该重构为实用程序类:

public class RetrofitFactory {

    private static OkHttpClient baseClient = new Builder().addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            // Do anything with response here
            //if we want to grab a specific cookie or something..
            return response;
        }
    }).addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            //this is where we will add whatever we want to our request headers.
            Request request = chain.request().newBuilder().addHeader("Accept", "application/json").build();
            return chain.proceed(request);
        }
    }).build();

    private static Gson gson = new GsonBuilder()
            .setLenient()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
            .create();

    private static Retrofit.Builder baseRetrofitBuilder = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson));


    public Builder baseClientBuilder() {
        return baseClient.newBuilder();
    }

    public static <T> T createApi(String url, Class<T> apiClass) {
        return baseRetrofitBuilder.baseUrl(url).build().create(apiClass);
    }

    public static <T> T createApi(String url, Class<T> apiClass, OkHttpClient client) {
        return baseRetrofitBuilder.baseUrl(url).client(client).build().create(apiClass);
    }
}

要使用额外的拦截器创建 NetworkAPI,您可以:

    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    // set your desired log level
    //logging.setLevel(Level.BASIC);
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient httpClient = RetrofitFactory.baseClientBuilder()
            .connectTimeout(100, TimeUnit.SECONDS)
            .readTimeout(100, TimeUnit.SECONDS)
            .addInterceptor(logging);
    networkApi = RetrofitFactory.createApi(url, NetworkApi.class, httpClient);

使用默认设置创建 XApi:

    xApi = RetrofitFactory.createApi(url, XApi.class);

【讨论】:

  • 是的,但既然我在做一个“干净的代码”,你不觉得这不是一个好方法吗?
  • 你的代码有问题:需要共享 OkHttpClient / Gson 以提高性能。创建 API 的代码放在不好的服务中(因为,例如,如果你想为所有 API 添加另一个通用拦截器,你应该重写所有其他地方)。重构为像我这样的 RetrofitFactory 实用程序类以使其干净。
  • 你能举个例子来说明你在说什么吗?
  • 抱歉,用静态方法好不好?没有其他办法吗?
  • 而且每次我想调用登录​​或看电影时,我都必须调用 KttpLogginInterceptor 和 OkHttpClient?然后用retrofitFactory创建networkApi?
【解决方案3】:

也许您可以创建不同的接口并修改您的 NetworkService 以创建改造适配器,具体取决于您希望在不同情况下使用的接口。

让我告诉你这个想法:

接口:

  public interface LoginInterface { 

        @POST(LOGIN) 
        Call<LoginResponse> login(@Body LoginRequest loginRequest); 
    }

    public interface MovieInterface {

        @GET(MOVIE) 
        Call<MovieResponse> getMovies(); 
    }

NetworkService 类

在这里您可以看到 NetworkService 的 Singleton 和使用此方法创建的方法,您可以使用单例实例创建不同的接口:

public class NetworkService {
    private final String API_BASE_URL = "";

    private static NetworkService INSTANCE = null;
    private static Retrofit retrofit;

    private OkHttpClient okHttpClient;
    private LruCache<Class<?>, Observable<?>> apiObservables;


    public NetworkService() {
        createInstance();
    }

    public static NetworkService getInstance() {

        if(INSTANCE == null){
            return new NetworkService();
        }

        return INSTANCE;
    }

    private void createInstance(){
        okHttpClient = buildClient();
        apiObservables = new LruCache<>(10);

        Gson gson = new GsonBuilder()
                .setLenient()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .create();

        retrofit = new Retrofit.Builder()
                .baseUrl(API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(httpClient.build())
                .build();


        }

    public static <T> T create(Class<T> apiClass) {
        return retrofit.create(apiClass);
    }

    private OkHttpClient buildClient() {

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

        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.addInterceptor(loggingInterceptor)
                .connectTimeout(100, TimeUnit.SECONDS)
                .readTimeout(100, TimeUnit.SECONDS);

        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response response = chain.proceed(chain.request());
                // Do anything with response here
                //if we want to grab a specific cookie or something..
                return response;
            }
        });

        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                //this is where we will add whatever we want to our request headers.
                Request request = chain.request().newBuilder().addHeader("Accept", "application/json").build();
                return chain.proceed(request);
            }
        });


        return builder.build();
    }

    /**
     * Method to clear the entire cache of observables
     */
    public void clearCache() {
        apiObservables.evictAll();
    }
}

然后您可以使用修改后的 NetworService 实例化它来实例化您想要使用的接口:

网络服务实例化:

public class LoginActiviy extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);

            //Instantiation of LoginInterface
            LoginInterface loginInterface = 
            NetworkService.getInstance().create(LoginInterface.class);
            //Then we use the method for login
            loginInterface.login(/* Handling the callback here*/);

            }
        }

public class MoviesActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_movies);

            //Instantiation of MovieInterface
            MovieInterface movieInterface = 
            NetworkService.getInstance().create(MovieInterface.class);
            //Then we use the method for login
            loginInterface.getMovies(/* Handling the callback here*/);

            }
        }

我希望它可以帮助您获得更好的想法。

【讨论】:

  • 那么 NetworkService 类呢?我该如何改进它@Juanjo Berenguer?
  • 不错!请您解释一下您的答案与 Mạnh Quyết Nguyễn 答案之间的区别吗?
  • 你好@StuartDTO。主要区别在于我正在实现一个单例模式来创建类 NetworkService,这意味着我将始终拥有此类的唯一实例,并且我将在方法 createInstance()中创建对象> 一次(第一次当 INSTANCE 为空时)。然后我们用所需的不同接口调用方法create。
猜你喜欢
  • 2018-03-09
  • 2019-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多