服务生成器改造
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 对象(OkHttpClient
、Retrofit.Builder
、Retrofit
)。从而节省处理时间和内存。
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) {
}
});
谁能帮我理解下面代码中的 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 对象(OkHttpClient
、Retrofit.Builder
、Retrofit
)。从而节省处理时间和内存。
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) {
}
});