服务生成器改造

Service Generator Retrofit

谁能帮我理解下面代码中的 createService 方法。我需要了解什么是方法的参数 Class S 和下面的代码

public class ServiceGenerator {

public static final String API_BASE_URL = Constant.BASE_URL;

private static OkHttpClient httpClient = new OkHttpClient();

private static Retrofit.Builder builder =
        new Retrofit.Builder()
                .baseUrl(API_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create());

public static <S> S createService(Class<S> serviceClass) {
    httpClient.interceptors().add(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request original = chain.request();

            Request.Builder requestBuilder = original.newBuilder()
                    .header("cache-control","no-cache")
                    .method(original.method(), original.body());

            Request request = requestBuilder.build();
            return chain.proceed(request);
        }
    });

    Retrofit retrofit = builder.client(httpClient).build();
    return retrofit.create(serviceClass);
}

}

自己找到了答案。整个 createservice 方法在此代码中并不是绝对必要的,没有 interceptor 声明也可以,如下所示。如果有人想为 httpClient 设置拦截器方法,例如 cache-control, authorization token 等,可以使用完整的代码块来设置它们。 createService 的最简单版本是

public class ServiceGenerator {
    public static <S> S createService(Class<S> serviceClass) {
        Retrofit retrofit = builder.client(httpClient).build();
        return retrofit.create(serviceClass);
    }
}

这里"S"是一个class类型的参数。它用于指定输出类型 class 与输入 class 相同。然后你可以创建你自己的 api 界面,只需在任何 activity/fragment

中使用此代码
MyApiService myapiservice = ServiceGenerator.createService(MyApiServiceInterface.class)
Call<YourDataResponseClass> call = myapiService.getMyData(YourDataRequestClass);
call.enqueue ({.....remaining code})

定义你的 MyApiService 如下

public interface MyApiServiceInterface {
        /*Base url is already defined in service generator class*/
        @GET("/your/api/endpoint/")
        /*YourDataResponseClass and YourDataRequestClass are 
        pojo object notation of your request and response json*/
        YouDataResponseClass getMyData(
                @Body YourDataRequestClass request
        );
    }

我遇到了同样的问题。在研究了发生的事情几分钟后,我意识到了这个问题。基本上你不必使用这行代码:

private static OkHttpClient httpClient = new OkHttpClient();

这是问题所在,每次都使用这样的静态变量:

Retrofit retrofit = builder.client(httpClient).build();

您正在创建该对象的另一个相同实例,而较旧的实例正在使用相同的引用并添加 N 个拦截器对象,并且每个拦截器对象都是一个 Rest 客户端(注意这一点,<---- 主要问题。因此你必须检查是否已经创建了HttpClient。所以解决这个问题的最终解决方案是下一个:

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.fastjson.FastJsonConverterFactory;
import utils.Constantes;

/**
 * Created by abeld on 19/05/2016.
 */
public class ServiceGenerator {

    public static final String API_BASE_URL = Constantes.URL_SERVER;

    private static OkHttpClient.Builder httpClient;

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(FastJsonConverterFactory.create());

    public static <S> S createService(Class<S> serviceClass) {
        return createService(serviceClass, null);
    }

    public static <S> S createService(Class<S> serviceClass, final AccessToken token) {

        if(httpClient == null){
            httpClient = new OkHttpClient.Builder();
            if (token != null) {
                httpClient.addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        Request original = chain.request();

                        Request.Builder requestBuilder = original.newBuilder()
                                .header("Accept", "application/json")
                                .header("Authorization", token.getTypeTokenAndToken())
                                .method(original.method(), original.body());

                        Request request = requestBuilder.build();
                        return chain.proceed(request);
                    }
                });
            }
            httpClient.connectTimeout(50, TimeUnit.SECONDS);
            httpClient.addInterceptor(addLoggin());
        }

        OkHttpClient client = httpClient.build();
        Retrofit retrofit = builder.client(client).build();
        return retrofit.create(serviceClass);
    }

    private static HttpLoggingInterceptor addLoggin(){

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        return logging;
    }
}

如您所见,我会检查对象是否已经为空,在这种情况下只需创建一个实例,在其他情况下跳过。

顺便说一下,如果您使用添加一个新的拦截器,例如记录器,您可以在 Android Studio 控制台中看到您执行的请愿数量,如果请愿数量仅为 1,则您修复了缺陷。

我决定 post 一个优化版本,它将添加必要的 Interceptors(例如授权 Interceptors)。

这个版本很好,因为它没有 recreate/build 任何或必需的 ApiClient 对象(OkHttpClientRetrofit.BuilderRetrofit)。从而节省处理时间和内存。

public class ApiClient
{
    public static final String BASE_URL = "https://my.auth.url/";

    // The singleton HTTP client (do initial customizations here).
    // We can add more customizations later by using the client.newBuilder() method, which will essentially clone this instance (more efficient to do that).
    private static OkHttpClient client = new OkHttpClient().newBuilder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS).build();

    // You want to make this static and initialize because once again,
    // we can custom later on the fly.
    private static Retrofit.Builder adapterBuilder = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(client)
            .addConverterFactory(GsonCustomConverterFactory.
                    create(new GsonBuilder()
                                   .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
                                   .create()));

    // Important to build when declaring the Retrofit object,
    // for optimization purposes (so you don’t have to rebuild it every time).
    private static Retrofit retrofit = adapterBuilder.build();


    public static <S> S createService(Class<S> serviceClass)
    {
        return retrofit.create(serviceClass);
    }

    public static <S> S createService(Class<S> serviceClass, final String authTokenString)
    {
        if (!TextUtils.isEmpty(authTokenString))
        {
            AuthenticationInterceptor authenticationInterceptor =
                    new AuthenticationInterceptor(authTokenString);
            /*
               Check if the AuthenticationInterceptor has already been applied;
             */
            if (!client.interceptors().contains(authenticationInterceptor))
            {
                /*
                    Clone the client and set the AuthenticationInterceptor, build, and then
                    set this to be our new client, because we want this in every request.
                 */
                client = client.newBuilder().addInterceptor(authenticationInterceptor).build();
                // Clone the Retrofit builder and set it to be our new Retrofit.Builder.
                adapterBuilder = retrofit.newBuilder();
                /*
                    Add our client to the Retrofit.Builder, then build the new Retrofit instance
                    We have now set up ApiClient to add the Authorization to every request.
                 */
                retrofit = adapterBuilder.client(client).build();
            }
        }

        return retrofit.create(serviceClass);
    }


    public static <S> S createService(
            Class<S> serviceClass, String username, String password)
    {
        if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password))
        {
            // Create the interceptor
            HttpBasicAuth basicAuth = new HttpBasicAuth(username, password);
            /* Here we clone our OkHttpClient that we built as a static class variable.
               Notice we use:
               "client.newBuilder()”, this clones the builder with everything we 
               initially set up, it’s very efficient, and actually how the OkHttp3
               documentation suggests it be used.
            */
            OkHttpClient basicClient = client.newBuilder().addInterceptor(basicAuth).build();
            // We clone the Retrofit.Builder, add the client, build and create the service class, all very efficient.
           // This only sets the “HttpBasicAuth” interceptor for this request since it should only be used once.
            return retrofit.newBuilder()
                    .client(basicClient)
                    .build()
                    .create(serviceClass);
        }

        return retrofit.create(serviceClass);
    }

}

这里是 HttpBasicAuth class 和 Interceptor 用于基本(用户名和密码)授权,例如当您需要第一次授权用户时,之后您将正在使用授权令牌。

    public class HttpBasicAuth implements Interceptor
    {

    private String username;
    private String password;

    public HttpBasicAuth(String username, String password)
    {
        this.username = username;
        this.password = password;
    }

    @Override
    public Response intercept(Chain chain) throws IOException
    {
        Request request = chain.request();

        // If the request already have an authorization (eg. Basic auth), do nothing
        if (request.header("Authorization") == null)
        {
            String credentials = Credentials.basic(username, password);
            request = request.newBuilder()
                    .header("Authorization", credentials)
                    .header("Accept", "application/json")
                    .build();
        }
        return chain.proceed(request);
    }
}

这里是 AuthenticationInterceptor class 一个 Interceptor 令牌授权,这将应用于每个请求。

public class AuthenticationInterceptor implements Interceptor
{

    private String authToken;

    public AuthenticationInterceptor(String token)
    {
        this.authToken = token;
    }

    @Override
    public Response intercept(Chain chain) throws IOException
    {
        Request original = chain.request();
        if (authToken != null && !authToken.isEmpty())
        {
            /*
             Using ".header("Authorization", authToken)”, will overwrite
             any old Authorization header values, which we want to do.
            */
            original = original.newBuilder()
                    .header("Authorization", authToken).build();
        }
        return chain.proceed(original);
    }
}

Link 到有关如何有效使用 OkHttpClient 的文档。

这是我的解决方案

import android.os.SystemClock;

import java.io.IOException;

import okhttp3.Dispatcher;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class GenericClient<T> {
    public T getClient(Class<T> repoClass) {

        // It add the retry logic
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(10);

        // It add the delay logic
        Interceptor networkInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                SystemClock.sleep(2000);
                return chain.proceed(chain.request());
            }
        };

        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(networkInterceptor)
                .dispatcher(dispatcher)
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Network.SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();

        return retrofit.create(repoClass);
    }
}

及其实现

AboutClient.getClient().about().enqueue(new Callback<AboutResponse>() {
            @Override
            public void onResponse(Call<AboutResponse> call, Response<AboutResponse> response) {
                
            }

            @Override
            public void onFailure(Call<AboutResponse> call, Throwable t) {
                
            }
        });