Service Generator Retrofit

混江龙づ霸主 提交于 2020-01-31 20:08:26

问题


Can someone help me understand the createService method in below code. I need to understand what is method's parameter Class S and the code underneath in depth

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

}


回答1:


I had the same problem with this. After a few minutes studying what's going on I realized the issue. Basically you have not to use this line of code:

private static OkHttpClient httpClient = new OkHttpClient();

Here is the problem, using a static variable like that each time you do:

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

you are creating another same instance of the object and the older one are using the same reference and adding N interceptor objects, and each interceptor objects is a Rest client (take care about this, <---- Main problem. For that reason you must to check if the HttpClient is already made or not. So the final solution to fix this problem is the next one:

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

As you can see I do a check if the object is already null, in this case just create a instance in other case skip.

By the way, if you use add a new interceptor like a logger you could see in your Android Studio console the number of petitions that you do, if the number of petitions is just 1, you fixed the defect.




回答2:


Found the answer myself. The entire createservice method is not absolute necessity in this code and one can live without interceptor declarations as shown below. If someone wants to set interceptor methods for httpClient such as cache-control, authorization token etc, one can use the complete code block to set them. The simplest version of createService is

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

Here "S" is a class type parameter. It is used to specify that the output type class to be same as input class. Then you can create you own api interface simply using this code in any activity/fragment

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

Define your MyApiService as below

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



回答3:


I’ve decided to post an optimized version that will add the necessary Interceptors (like authorization Interceptors).

This version is nice because it does not recreate/build any of or necessary ApiClient objects (OkHttpClient, Retrofit.Builder, Retrofit). Thus saving processing time and memory.

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

}

Here’s the HttpBasicAuth class an Interceptor for basic (username & password) authorization, like when you need to authorize a user for the first time, after which you’ll be using a authorization token.

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

Here’s the AuthenticationInterceptor class an Interceptor for token authorization, this will be applied to every request.

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 to documentation on how to use the OkHttpClient efficiently.



来源:https://stackoverflow.com/questions/35666960/service-generator-retrofit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!