• Review it. Sort it out again
  • How to read the source code?
    • Read in large pieces. You read from rough to close.
    • Write an example, then follow the example to the source code, and then analyze retrofit step by step.
    • Be sure to follow the step-by-step process of writing examples, skipping links, and writing demo code.

1. What is Retrofit?

  • This is an HTTP library for Android and Java backend.
  • It used to be for Android, and then slowly made it loose enough, light enough, interface enough,
  • It is now available for both Android and Java.

2. How does it work?

  • Let me just tell you how to use it, because some people haven’t used it before.
  • See the website square. Making. IO/retrofit
  1. First you have to make an interface that declares all of your API calls. That’s what the HTTP request looks like, and that’s described by these method declarations, without having to write a specific URL.

  1. Create a retrofit object and use this object to create an interface instance of the object. It’s an instance of the GitHubService above, which is an instance of your interface list, which contains all of your interfaces.

  1. Network calls are made using this interface.

Use the official website as an example to outline how to use it.

  1. First, add the retrofit and add the dependencies
implementation 'com. Squareup. Retrofit2: retrofit: 2.4.0'
Copy the code
  1. Create an interface named GithHubService. Then declare a GET request, this format is not memorized, with the familiar, the request path filled in
public interface GitHubService {
    @GET("users/renwuxian/repos")
    Call<ResponseBody> getRepos(a);
}
Copy the code
  1. Create a Retrofit object and call the API
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com")
                .build();

        GitHubService api =retrofit.create(GitHubService.class);

        api.getRepos().enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                System.out.println("success!");
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {}});/ / asynchronous
// api.getRepos().execute(); / / synchronization
Copy the code
  1. Add permissions
<uses-permission android:name="android.permission.INTERNET"/>
Copy the code
  1. The ResponseBody returns an OKhttp ResponseBody, so you need to do a conversion and add a converter factory, which requires json support
implementation 'com. Squareup. Retrofit2: converter - gson: 2.4.0'
Copy the code
  • Add converter factory
Gson gson = new Gson();
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();
Copy the code
  1. So we can change the ResponseBody to the bean class that corresponds to the JSON string.
@GET("users/renwuxian/repos")
Call<ResponseBody> getRepos(a); ⬇ ️@GET("users/renwuxian/repos")
Call<BaseBean> getRepos(a);
Copy the code
  • The corresponding return should also be changed, and the result will be printed
api.getRepos().enqueue(new Callback<BaseBean>() {
    @Override
    public void onResponse(Call<BaseBean> call, Response<BaseBean> response) {
        System.out.println("success!"+gson.toJson(response.body()));
    }

    @Override
    public void onFailure(Call<BaseBean> call, Throwable t) {}});Copy the code

3 source code from beginning to end roughly analysis

  • Where to start?

    • Do not read source code line by line, start from an entry, from the program most directly interacted with the port ->enqueue
    • We know that enqueue is for network requests, so if I understand enqueue, don’t I have to understand anything else?
    • It’s possible that your project is super big, and if it has a core configuration, like Retrofit, I look at it from that point of view, and it might give me too big a picture.
    • I look at the framework from the most direct point of view, maybe the framework has 8 functions, I look at one function, slowly expand to other places.
  • What does enqueue do?

    • He’s going back to the background to execute our network requests,
    • Enqueue does not execute every request in retrofit in order.
    • Instead, after you execute this method, your request will be thrown into his list, and everything in his list will be executed immediately,
      • For example, if you have eight requests, all eight requests are executed together, without waiting for one request to end.
      • What does this thing do? Why do you need to queue when you execute immediately?
        • He’ll keep track of the number of requests you’re running at the same time,
        • If the number of requests is too high (about 64), he will give you a stop for performance, other new, I will not let him directly to execute, first in line,
        • When something else makes room, enough performance room, like the CPU, you do it.

1. Find the entry: create()

  • See the source code:
    • Enqueue goes in, it’s an abstract method, and the location, it’s not even a class, it’s an interface called Call. Now, I don’t know how this enqueue executes, because it’s an abstract method in an interface.
    • So what do we do? I want to know how this object that he’s in was created in the first place, how this getRepos() was created.
    • But the API object for getRepos() is GitHubService, which is the interface we just declared. So let’s look at how this API object is created.
GitHubService api = retrofit.create(GitHubService.class);
Copy the code
  • Retrofit.create How did he do it?
    • If there’s still an interface inside create, I’ll have to re-link, but if there isn’t, that’s fine.
    • Click in. Great, finally a code I can look at.
  • So what does create do?
    • He creates the interface object, which is all the interfaces that contain your network definition API, and create is the heart of the project, where everything comes from.
public <T> T create(final Class<T> service) {
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    eagerlyValidateMethods(service);
  }
  return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
            throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method);
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
          returnserviceMethod.adapt(okHttpCall); }}); }Copy the code
  • Go ahead and read the code, line one
Utils.validateServiceInterface(service);
Copy the code
  • Click in to have a look. He did a test, he did two tests,
    • First, if you pass in an interface,
    • Second, his inherited interface cannot be greater than 0,
      • This means that it must be a native interface, not an inherited interface
static <T> void validateServiceInterface(Class<T> service) {
  if(! service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");
  }
  // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
  // Android (http://b.android.com/58753) but it forces composition of API declarations which is
  // the recommended pattern.
  if (service.getInterfaces().length > 0) {
    throw new IllegalArgumentException("API interfaces must not extend other interfaces."); }}Copy the code
  • Don’t worry about it, it’s not the point,
  • The same line down here, if you do the radicalization configuration, you verify,
    • Basically, verify the validity of the GitHubServic interface you write early.
  • Why do this configuration?
    • If you do this configuration and all the validation is done the moment your Service is created, it will help you debug your code and catch it early, but it will hurt performance.
    • In the actual operation of Retrofit, whatever method you use, he will be momentarily stuck, but it doesn’t matter if you are stuck, you will be split this way and that way, so you can’t do this in a formal project, you can’t let him verify it too early. Because early validation leads to centralized validation. Will be the user obviously know card once. So that’s not the point. These are both checks.
if (validateEagerly) {
  eagerlyValidateMethods(service);
}
Copy the code
  • The point is this, this line of code, this newProxyInstance has three arguments in it, and the third argument is really long.
  return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
            throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method);
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
          returnserviceMethod.adapt(okHttpCall); }});Copy the code
  • What is this method newProxyInstance?

    • The dynamic proxy design pattern is obvious at first glance. Design pattern – Proxy
  • Three parameters,

    • The first argument is classloader,
      • Classloader means that when you create a new class, you provide a classloader to load it in, whether it’s a class you’ve written before or a class you’ve defined on site.
      • So you don’t have to do any work on this classloader, you just take it out, you don’t have to worry about this argument, it says you give me a classloader. Just throw one out, just give her the classloader I’m on.
    • The second argument is that you provide a set of interfaces,
      • You provide a list of interfaces, and my newProxyInstance is going to implement all of those interfaces into one object.
      • But Retrofit only provides one interface, the one that holds all of our API method calls. That’s understandable, but how did he do it? How did he implement something that I had declared but had never been implemented anywhere?
    • The third parameter. What is this thing? He’s an object,
      • Object contains a method called invoke.
        • You pass in an object, and there’s a method in that object, what is it actually?
        • It’s actually a callback. What is a callback?
          • You pass me this thing, I’ll leave it there, I’ll need it later, I’ll call it.
  • What’s inside the Invoke method is probably our key point, but not to mention,

  • Let’s simulate what this newProxyInstance does.

    1. We first create a class called RealService that implements GitHubService. This is what the second argument of newProxyInstance requires. The first argument actually provides the classloader needed to automatically generate the class, but we simulate the generation process, so it doesn’t matter.
public class RealService implements GitHubService {
    @Override
    public Call<BaseBean> getRepos(a) {
        return null; }}Copy the code
  1. Add functionality to this class by directly sticking in the callback method for the third argument
    • Ignore the red part and pretend it’s usable. Then modify the getRepos method. NewProxyInstance actually does that. (See proxy Design Patterns for details on how proxy classes are dynamically generated by Asm.)
    • First, he’s going to create an object that implements all the interfaces that you gave me,
    • In addition, I will implement each of his methods by calling my invocationHandler’s invoke method and loading in the method information of your method, which is exactly what he does.
public class RealService implements GitHubService {
    InvocationHandler invocationHandler =  new InvocationHandler() {
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
                return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                    (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            returnserviceMethod.adapt(okHttpCall); }};@Override
    public Call<BaseBean> getRepos(a) {
        Method method = null;
        try {
        // The parameters of the method are actually parsed and passed
            method = GitHubService.class.getMethod("getRepos");
            return (Call<BaseBean>)invocationHandler.invoke(this,method,null);
        } catch(NoSuchMethodException e) { e.printStackTrace(); }}}Copy the code
  • The next thing I know what’s going on inside Invoke, I’ll know how Retrofit works.
  • The handler of the agent what did he do?
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
      throws Throwable {
    // If the method is a method from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
        return method.invoke(this, args);
    }
    if (platform.isDefaultMethod(method)) {
        return platform.invokeDefaultMethod(method, service, proxy, args);
    }
    ServiceMethod<Object, Object> serviceMethod =
            (ServiceMethod<Object, Object>) loadServiceMethod(method);
    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
    return serviceMethod.adapt(okHttpCall);
}
Copy the code
  • Line by line
if (method.getDeclaringClass() == Object.class) {
    return method.invoke(this, args);
}
Copy the code
  1. First explain these three lines,
    • These three lines simply say no, he is to ensure that my code does not error.
    • What is DeclaringClass?
      • In which class do you declare this method, and all the methods in a class, as long as it can call them, are either declared by it or declared by its superclass.
    • In this case, if the method is declared by the Object class, then I call it directly.
      • What that means is that if the method is not declared in the GitHubService, such as a toString(), hashCode(), etc., which is a method found in all objects, then you can use it exactly as you declare it. I won’t rewrite you.
if (platform.isDefaultMethod(method)) {
    return platform.invokeDefaultMethod(method, service, proxy, args);
}
Copy the code
  1. And then the top ones don’t work either.
    • First of all, I need to know platform. What did he do?
    • To see how platform is created, click the get method
 private final Platform platform = Platform.get();
Copy the code
  • FindPlatform (), which doesn’t have any description, and then click, there’s an android, a java8, and a Platform();
  • My guess is that he behaves differently on different platforms.
private static final Platform PLATFORM = findPlatform();

static Platform get(a) {
  return PLATFORM;
}

private static Platform findPlatform(a) {
  try {
    Class.forName("android.os.Build");
    if(Build.VERSION.SDK_INT ! =0) {
      return newAndroid(); }}catch (ClassNotFoundException ignored) {
  }
  try {
    Class.forName("java.util.Optional");
    return new Java8();
  } catch (ClassNotFoundException ignored) {
  }
  return new Platform();
}
Copy the code
  1. And then go back, and this method is to see if it’s the default method of an interface,
    • What is DefaultMethod?
      • This is a new feature that started in java8.
      • We all know that Java interfaces can’t be implemented directly. All your methods must be implemented by the class that implements them. You can’t give default implementations. Java8 allows it.
      • That’s it. That’s it. This thing is useless for Retrofit because none of our interfaces are implemented. That doesn’t help either.
if (platform.isDefaultMethod(method)) {
    return platform.invokeDefaultMethod(method, service, proxy, args);
}
Copy the code

2. Find the core three lines of code

  • The last three lines are what Retrofit actually does.
  • So again, I don’t know any of these three lines, what is serviceMethod? What is okHttpCall? Adapt?
  • I want to know all three lines. I don’t understand them. What should I do?
    • When I read the code, I need to find a single entry, because there is one entry where I can read a complete little event faster, and then I have a complete plan, and then my head can clear some memory out, and I can continue to read,
    • Now what if I have three rows? Who do I choose? And I don’t seem to know who’s more important. And they’re all connected to each other.
    • At this point I choose to take a look at each one, and then decide which one I should look at first. Definitely, but when there’s a lot of stuff, I just skim through it and see who I see first.
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code

3. Read three lines of code

  • ServiceMethod comes in, it’s a little obscure, it’s a big file,

    • /** Adapts an INVOCATION of an interface method into an HTTP call. */
    • That’s probably fine, but if I try to relate it to the code, I can’t. I know what you’re talking about but I have no idea.
  • Let me first explain one thing here. What is adapter?

    • We usually do android development will use a variety of adapter, but may not have enough understanding of what an adapter is.
    • An adapter is for switching, for turning a two-hole plug into a three-hole plug.
    • He ADAPTS an interface method to an HTTP call.
    • What it means is that the API we defined corresponds to an HTTP call.
    • But that’s my guess. When you ask me how this method works I still don’t know. The comments are very short, the code is very long, so turn around and look somewhere else.
  • LoadServiceMethod, let me see if I can understand this. “Cash” is a “cash”. How do I know?

ServiceMethod<? ,? > loadServiceMethod(Method method) { ServiceMethod<? ,? > result = serviceMethodCache.get(method);if(result ! =null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); }}return result;
}
Copy the code
  • The serviceMethodCache declaration is a Map,
  • It’s very common to use Map for cash.
  • Cash:
    • Do I have one of these? Have I ever produced, have I ever produced this thing?
    • If it has been generated, I can find it in my map, then I will use it directly, if not, I will generate it and add it.
private finalMap<Method, ServiceMethod<? ,? >> serviceMethodCache =new ConcurrentHashMap<>();
Copy the code
  • Cash is the way it works, so I need to see where it’s being generated. Click on Builder and see if I can understand this.
result = new ServiceMethod.Builder<>(this, method).build();
Copy the code
  • Two parameters,
    • One is a Retrofit, I don’t know what a Retrofit is, but it feels like a general configuration.
    • And then there’s method, which I’ve defined in the API.
Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}
Copy the code
  • I don’t think there’s a lot of arguments, so he’s going to do three lines
    • One is to take every note from his method
    • Second, take each type of his method parameter
    • Third, take each annotation for the method parameter (one parameter can have multiple annotations)
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
Copy the code
  • Let’s see what build() does.
  • Click in. It’s kind of gross. There’s too much stuff. A bunch of initialization
public ServiceMethod build(a) {
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  if(! hasBody) {if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST)."); }}int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if(! isFormEncoded && ! isMultipart && ! hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  if(isFormEncoded && ! gotField) {throw methodError("Form-encoded method must contain at least one @Field.");
  }
  if(isMultipart && ! gotPart) {throw methodError("Multipart method must contain at least one @Part.");
  }

  return new ServiceMethod<>(this);
}
Copy the code
  • After initialization, put your various initialization information in the ServiceMethod
  return new ServiceMethod<>(this);
Copy the code
  • Create a ServiceMethod with a Builder pattern,
  • I know it’s a Builder mode, but I don’t know what it is, so I’m going to give it up for now and leave it alone. I’ll turn around and look.
ServiceMethod(Builder<R, T> builder) {
  this.callFactory = builder.retrofit.callFactory();
  this.callAdapter = builder.callAdapter;
  this.baseUrl = builder.retrofit.baseUrl();
  this.responseConverter = builder.responseConverter;
  this.httpMethod = builder.httpMethod;
  this.relativeUrl = builder.relativeUrl;
  this.headers = builder.headers;
  this.contentType = builder.contentType;
  this.hasBody = builder.hasBody;
  this.isFormEncoded = builder.isFormEncoded;
  this.isMultipart = builder.isMultipart;
  this.parameterHandlers = builder.parameterHandlers;
}
Copy the code
  • Now I know that the function of ServiceMethod is to encapsulate method information (annotations, return types, parameter annotations…) After that, it ADAPTS to an okHttpCall,
  • Not really, but I think I know something about it.
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code

Here is a brief description of the advantages of builder model.

  • How do we use builder?
Person person = new Person.Builder()
    .name("xx")
    .gender("male")
    .age(25)
    .builder();
Copy the code
  • What’s in it for him?
    • I’m this person right now and his name and age and so forth are related to other attributes, they might be related to each other’s attributes. And they all have initialization costs.
    • What does that mean? Such as:
      • Person person = new Person();
      • And then I initialize this guy in memory, like I’m a graphical program, and THEN I draw this guy, I draw a normal guy, and then,
      • person.setName(“qiulong”);
      • person.gendder(“male”);
      • At this point, I have a problem. The default gender in my program is female, and now you’re male, so I’m going to erase the female figure. “And be a man again. At this point, the woman’s performance is wasted, the time is wasted, and NOW I have to recreate the man, and then I have,
      • person.age(25);
      • My system is 24 by default, so you’re 25, you’re done, and I’m going to erase my 24, and I’m going to draw a 25.
    • There’s a hook between each parameter, or there’s an initialization cost, and once your object is born, there’s a performance cost to making changes to it. A lot of times it’s not necessary. The Builder lets you do no extra work before creating it. I just sort of make a configuration list and initialize it directly at the end. This is one of the most important benefits of the Builder.
    • The other thing is that you have so many properties that it is very difficult to initialize them, and you can define them in one line with the Builder.

4. Read OkHttpCall coarse

  • So let’s go ahead and read the source code,
  • In the case of ServiceMethod, it reads the method information in my API and turns it into an HTTP call.
  • But what is this call? I don’t know how it works.
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code
  • So let’s see what is OkHttpCall again? The construction point goes in,
  • How could it be so simple? Just two parameters. So that’s the problem, this serviceMethod is kind of translated to the method, and then this OkHttpCall he gives me is very simple, that’s it, he saves these two parameters, what do we do?
OkHttpCall(ServiceMethod<T, ? > serviceMethod,@Nullable Object[] args) {
  this.serviceMethod = serviceMethod;
  this.args = args;
}
Copy the code
  • What is OkHttpCall?
  • First, he implemented the Call interface,
final class OkHttpCall<T> implements Call<T> {... }Copy the code
  • Remember what “Call” is?
    • The method that we define in the API returns a Call, and retrofit teaches us to use that Call, they’re one thing.
public interface GitHubService {
    @GET("users/renwuxian/repos")
    Call<BaseBean> getRepos(a);
}
Copy the code
  • Let me see what OkHttpCall does with its enqueue, because the final response to the user is the enqueue() of the Call returned by getRepos.
api.getRepos().enqueue(new Callback<BaseBean>() {
    @Override
    public void onResponse(Call<BaseBean> call, Response<BaseBean> response) {
        System.out.println("success!"+gson.toJson(response.body()));
    }

    @Override
    public void onFailure(Call<BaseBean> call, Throwable t) {}});Copy the code
  • I’ll leave it at that for the moment. Anyway, this thing seems to be getting a little closer to my final answer, they’re connected.
  • I’m going to return a call object in an API defined method, and then I generate a dynamic object using a dynamic proxy method, and his invoke method finally gives me a call.
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) {
      try {
        call = rawCall = createRawCall();
      } catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! =null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }

  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      try {
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }

      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch(Throwable t) { t.printStackTrace(); }}@Override public void onFailure(okhttp3.Call call, IOException e) {
      callFailure(e);
    }

    private void callFailure(Throwable e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch(Throwable t) { t.printStackTrace(); }}}); }Copy the code

5. Thick read adapt

  • But this is confused again, what does this mean? Adapter? You don’t want me to use it? How to do? Yeah, I read the first two lines anyway.
    • The first line, I guess, is the parsing of the method.
    • The second line, I guess, is the final call.
    • But by the third row I’m not sure if the second row is what I guessed. But I think the first two lines are pretty much fixed, because the first line is passed as an argument to the second line. So let me double-check my guess.
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code
  • After looking at adapter, I can decide where MY three lines should come from.
T adapt(Call<R> call) {
  return callAdapter.adapt(call);
}
Copy the code
  • I’m starting to get bored, there’s a new thing called a callAdapter, and I don’t care what this callAdapter is, but what does this Adpter do, click in
T adapt(Call<R> call);
Copy the code
  • It’s another interface. So what? I don’t understand any of these three lines. Regardless of him, at least the first two lines are very similar to the structure in my heart. How amazing do I think your code can be? Don’t you just parse my code and generate a call? This one looks like it, first row parsing, second row generating.
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code

6. Decide the order in which to read the code

  • Now, with this three-line structure, I have two lines that are a little bit clear, so after reading those two lines, let me look at the third line, and that’s the order I decide to read the code.

7. Peruse the first line loadServiceMethod()

  • So let me look at the first row and say, the first row that I was a little scared of,
ServiceMethod<? ,? > loadServiceMethod(Method method) { ServiceMethod<? ,? > result = serviceMethodCache.get(method);if(result ! =null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); }}return result;
}
Copy the code
  • I’ve looked at the Cash part, I’ve looked at the Builder, I’m going to look at build() now.
  • A lot of times, when you read this, it’s kind of painful, because the big structure I already know, is proxy.newproxyinstance (1,2,3), and then you end up with three lines like this.
public ServiceMethod build(a) {
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  if(! hasBody) {if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST)."); }}int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if(! isFormEncoded && ! isMultipart && ! hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  if(isFormEncoded && ! gotField) {throw methodError("Form-encoded method must contain at least one @Field.");
  }
  if(isMultipart && ! gotPart) {throw methodError("Multipart method must contain at least one @Part.");
  }

  return new ServiceMethod<>(this);
}
Copy the code
  • Look at the first line
callAdapter = createCallAdapter();
Copy the code
  • CallAdapter? Isn’t that what just happened? Take a look at createCallAdapter()
private CallAdapter<T, R> createCallAdapter(a) {
  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        "Method return type must not include a type variable or wildcard: %s", returnType);
  }
  if (returnType == void.class) {
    throw methodError("Service methods cannot return void.");
  }
  Annotation[] annotations = method.getAnnotations();
  try {
    //noinspection unchecked
    return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    throw methodError(e, "Unable to create call adapter for %s", returnType); }}Copy the code
  • The above is validation, and the above is the core code
retrofit.callAdapter(returnType, annotations)
Copy the code
  • Retrofit’s CallAdapter method is called and passed to my ServiceMethod, so what is this CallAdapter?
publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
}
Copy the code
  • It’s a little bit long
publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
    Annotation[] annotations) {
  checkNotNull(returnType, "returnType == null");
  checkNotNull(annotations, "annotations == null");

  int start = callAdapterFactories.indexOf(skipPast) + 1;
  for (inti = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations,this);
    if(adapter ! =null) {
      return adapter;
    }
  }

  StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
      .append(returnType)
      .append(".\n");
  if(skipPast ! =null) {
    builder.append(" Skipped:");
    for (int i = 0; i < start; i++) {
      builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
    }
    builder.append('\n');
  }
  builder.append(" Tried:");
  for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName());
  }
  throw new IllegalArgumentException(builder.toString());
}
Copy the code
  • The key point is this
CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations,this);
Copy the code
  • Now to review what I’m going to do, I just saw a calladapter, and I’m going to see how it’s created. I’m going to look at what a CallAdapter is, look at a callAdapterFactorys, and see where I modified it.
final List<CallAdapter.Factory> callAdapterFactories;
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
    List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
    @Nullable Executor callbackExecutor, boolean validateEagerly) {
  this.callFactory = callFactory;
  this.baseUrl = baseUrl;
  this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
  this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
  this.callbackExecutor = callbackExecutor;
  this.validateEagerly = validateEagerly;
}
Copy the code
  • To see who called Retrofit(), click Retrofit().
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
    unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
Copy the code
  • And then you go to this place, where is this callAdapterFactories actually being generated? A little bit on top
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
Copy the code
  • There are two lines, and the first line he fills in callAdapterFactories, but it’s empty.
  • The second line he will add an object, adjust the defaultCallAdapterFactory (), some come in
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
  if(callbackExecutor ! =null) {
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }
  return DefaultCallAdapterFactory.INSTANCE;
}
Copy the code
  • ExecutorCallAdapterFactory () order in
final Executor callbackExecutor;

ExecutorCallAdapterFactory(Executor callbackExecutor) {
  this.callbackExecutor = callbackExecutor;
}
Copy the code
  • Let’s see who called callbackExecutor
return new ExecutorCallbackCall<>(callbackExecutor, call);
Copy the code
  • ExecutorCallbackCall point in
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}
Copy the code
  • Let’s see who called callbackExecutor
delegate.enqueue(new Callback<T>() {
  @Override public void onResponse(Call<T> call, final Response<T> response) {
    callbackExecutor.execute(new Runnable() {
      @Override public void run(a) {
        if (delegate.isCanceled()) {
          // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
          callback.onFailure(ExecutorCallbackCall.this.new IOException("Canceled"));
        } else {
          callback.onResponse(ExecutorCallbackCall.this, response); }}}); }@Override public void onFailure(Call<T> call, final Throwable t) {
    callbackExecutor.execute(new Runnable() {
      @Override public void run(a) {
        callback.onFailure(ExecutorCallbackCall.this, t); }}); }});Copy the code
  • Finally, you get to a place where you can forget everything on the way.
  • Remember there was an onResponse and an onFailure, and it ended up here, and there was an onResponse and an onFailure, and it proxy-ed my callback, and it asked callbackExecutor. Execute to handle the success and failure of the request.
api.getRepos().enqueue(new Callback<BaseBean>() {
    @Override
    public void onResponse(Call<BaseBean> call, Response<BaseBean> response) {
        System.out.println("success!"+gson.toJson(response.body()));
    }

    @Override
    public void onFailure(Call<BaseBean> call, Throwable t) {}});Copy the code
  • What is execute?
    • He’s a thread handler. He can cut threads. So how does execute cut the thread, and what does execute do to it?
  • If you look back, I was passing in a callbackExecutor, and I need to know what this callbackExecutor did,
    • So now I know that if I click on it, callbackExecutor is doing a transfer, it’s doing a proxy job, it’s doing a delegate job, and it’s telling me that the final result that I return whether it succeeds or fails, I need to transfer it.
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
Copy the code
  • For what? If you look at what callbackExecutor does,
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
  callbackExecutor = platform.defaultCallbackExecutor();
}
Copy the code
  • It’s empty, but don’t be afraid. We just said that Platform is a common thing for both android and java8, so let’s see if android has an implementation for it. Is there a corresponding rewrite?
@Nullable Executor defaultCallbackExecutor(a) {
  return null;
}
Copy the code
  • Yes, at the bottom, it’s called MainThreadExecutor(), which is the MainThreadExecutor.
  • Again, let’s see what he does with execute(),
    • The agent whose execute() does the thing.
    • His representation is in the hands of Handler.
    • I might get a little dizzy in the middle of this, and I did get dizzy when I looked at it myself. What do we have here?
      • He cut your Internet request to the front desk. How did you do that? It’s inside this execute().
  • Okhttp, that’s what he does, which thread do you do it on, I can put it in the background,
  • What Retrofit does is bring all of your backend task results to the foreground and help you get to the main thread, which is what the callbackExecutor does.
  • And this callbackExecutor eventually gives to the callAdapterFactory, which is the factory that creates the callAdapters. He creates CallAdapters for different custom API (GitHubService) interfaces, with one Call for each method and one callAdapter for each Call.
  • What does this Calladapter do? Thread switching.
static class Android extends Platform {
  @Override public Executor defaultCallbackExecutor(a) {
    return new MainThreadExecutor();
  }

  @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    if (callbackExecutor == null) throw new AssertionError();
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }

  static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) { handler.post(r); }}}Copy the code
  • I just read the third line of code, and I don’t know what the Adapter is doing,
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code
  • All I know is that he used calladapter.adapt ().
  • Now reading this, I was going to read ServiceMethod,
  • But by accident, NOW that I know what a callAdapter does,
    • He’s an agent. He’ll cut the thread for me. Help me push the content to the main thread. Well, then, one of my stones is on the ground.
T adapt(Call<R> call) {
  return callAdapter.adapt(call);
}
Copy the code
  • What I’m going to show you is a little bit of acceleration, a little bit of speed, something about how one line of code is nested with another line of code, and I’m not going to go into a lot of detail. So it’s perfectly normal that you didn’t understand my process, but his function, you have to be very clear.
  • The structure is clear, and then the things in the middle are easy for you to understand. That’s what this row does,
  • But it’s not just about cutting threads. He can do other things, he can do rxJava adaptation. This is where the rxJava adaptation is.

So I’m going to go ahead and read the first two lines briefly. I can’t go into detail. The code is still very complex, there’s a lot of detail, but the structure is still very simple. Just now,

  • See this in loadServiceMethod()
result = new ServiceMethod.Builder<>(this, method).build();
Copy the code
  • Builder,
  • We understand the first line.
public ServiceMethod build(a) {
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  responseConverter = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  if(! hasBody) {if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST)."); }}int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if(! isFormEncoded && ! isMultipart && ! hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  if(isFormEncoded && ! gotField) {throw methodError("Form-encoded method must contain at least one @Field.");
  }
  if(isMultipart && ! gotPart) {throw methodError("Multipart method must contain at least one @Part.");
  }

  return new ServiceMethod<>(this);
}
Copy the code
  • The second line is responseType, which is the return value type of your API.
 responseType = callAdapter.responseType();
Copy the code
  • What follows is something simple in structure but complicated in detail.
  • Such as createResponseConverter (),
    • What is a responseConverter?
    • The bottom of Retrofit is OKHTTP, so what do we get?
    • We can get a concrete object (bESN).
    • We don’t get a ResponseBody, what do we do? Do the conversion. How to convert, is to use the Converter.
responseConverter = createResponseConverter();
Copy the code
  • And responseConverter goes over and says,
Converter<ResponseBody, T> responseConverter;
Copy the code
  • In Retrofit, he had this thing called A Converter,
    • What does Converter do to convert, In addition to doing the Converter return the result he will give you to convert to the specific object,
    • And what you’re asking for, sometimes you pass in an Integer, and you need to convert it to a String to do network data splice, which is what this Converter does.
    • Sometimes you want to upload an image. If you are uploading a File, how do you convert the File into a RequestBody?
    • Converter converts both your request and your response. This is what the Converter does.

The following

 parseMethodAnnotation(annotation);
Copy the code
  • ParseMethodAnnotation, which is the notation that reads your method, each annotation that reads the method. How do you interpret that? Just look at it, and I can click on it and see what it’s doing, even though I don’t understand it.
private void parseMethodAnnotation(Annotation annotation) {
  if (annotation instanceof DELETE) {
    parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
  } else if (annotation instanceof GET) {
    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
  } else if (annotation instanceof HEAD) {
    parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
    if(! Void.class.equals(responseType)) {throw methodError("HEAD method must use Void as response type."); }}else if (annotation instanceof PATCH) {
    parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
  } else if (annotation instanceof POST) {
    parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
  } else if (annotation instanceof PUT) {
    parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  } else if (annotation instanceof OPTIONS) {
    parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
  } else if (annotation instanceof HTTP) {
    HTTP http = (HTTP) annotation;
    parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
  } else if (annotation instanceof retrofit2.http.Headers) {
    String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
    if (headersToParse.length == 0) {
      throw methodError("@Headers annotation is empty.");
    }
    headers = parseHeaders(headersToParse);
  } else if (annotation instanceof Multipart) {
    if (isFormEncoded) {
      throw methodError("Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {
    if (isMultipart) {
      throw methodError("Only one encoding annotation is allowed.");
    }
    isFormEncoded = true; }}Copy the code
  • There are different interpretations of each note. Let’s say I hit GET
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
  if (this.httpMethod ! =null) {
    throw methodError("Only one HTTP method is allowed. Found: %s and %s.".this.httpMethod, httpMethod);
  }
  this.httpMethod = httpMethod;
  this.hasBody = hasBody;

  if (value.isEmpty()) {
    return;
  }

  // Get the relative URL path and existing query string, if present.
  int question = value.indexOf('? ');
  if(question ! = -1 && question < value.length() - 1) {
    // Ensure the query string does not have any named parameters.
    String queryParams = value.substring(question + 1);
    Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
    if (queryParamMatcher.find()) {
      throw methodError("URL query string \"%s\" must not have replace block. "
          + "For dynamic query parameters use @Query.", queryParams); }}this.relativeUrl = value;
  this.relativeUrlParamNames = parsePathParameters(value);
}
Copy the code
  • Going back to Builder (), which reads the method annotations, next,
parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
  Type parameterType = parameterTypes[p];
  if (Utils.hasUnresolvableType(parameterType)) {
    throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
        parameterType);
  }

  Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
  if (parameterAnnotations == null) {
    throw parameterError(p, "No Retrofit annotation found.");
  }

  parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
Copy the code
  • ParameterHandler, which reads each of your parameters,
  • So let’s take an example of parseParameter,
privateParameterHandler<? > parseParameter(intp, Type parameterType, Annotation[] annotations) { ParameterHandler<? > result =null;
  for(Annotation annotation : annotations) { ParameterHandler<? > annotationAction = parseParameterAnnotation( p, parameterType, annotations, annotation);if (annotationAction == null) {
      continue;
    }

    if(result ! =null) {
      throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
    }

    result = annotationAction;
  }

  if (result == null) {
    throw parameterError(p, "No Retrofit annotation found.");
  }

  return result;
}
Copy the code
  • ParseParameterAnnotation is the key, and it’s the way to interpret a bunch of annotations
  • Read each annotation for each of your parameter types, such as Url, Path
privateParameterHandler<? > parseParameterAnnotation(int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) {
    if (gotUrl) {
      throw parameterError(p, "Multiple @Url method annotations found.");
    }
    if (gotPath) {
      throw parameterError(p, "@Path parameters may not be used with @Url.");
    }
    if (gotQuery) {
      throw parameterError(p, "A @Url parameter must not come after a @Query");
    }
    if(relativeUrl ! =null) {
      throw parameterError(p, "@Url cannot be used with @%s URL", httpMethod); }...Copy the code
  • In the process of reading the method, he will try to determine if you are using the method correctly.
    • For example, if you are a GET, but at the same time you add body to it, THEN I will give you a wrong report. Get is to take something, and his body does not add anything. If you add something, I will give you a wrong report.
  • In addition to being more useful than OKHTTP, retrofit has more requirements for your USE of the HTTP specification, which in turn makes it easier to use.
  • You don’t have to know all of this, just know the build process of ServiceMethod,
    • Is to parse out your method,
    • To read the method’s configuration, to read its return value, to read each of its parameters,
    • After you’ve read them, parse them, save them,
    • When you actually create a Call.

8. Read the enqueue ()

  • So far, it has been confirmed
    • The first line is reading method,
    • The second line is to fill in the ServiceMethod parameter to okHttpCall,
    • The third line is for thread switching, and all three lines are read
  • But I still have a question, which is what I couldn’t read before. The enqueue.
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
Copy the code
  • I just clicked on it once, but it was so much that I gave it up for a while, and I thought it was disgusting, but now it’s not disgusting, because there’s only so much left.
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) {
      try {
        call = rawCall = createRawCall();
      } catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! =null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }

  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      try {
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }

      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch(Throwable t) { t.printStackTrace(); }}@Override public void onFailure(okhttp3.Call call, IOException e) {
      callFailure(e);
    }

    private void callFailure(Throwable e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch(Throwable t) { t.printStackTrace(); }}}); }Copy the code
  • There are only two keys to this part
call = rawCall = createRawCall();
Copy the code
  • Our call is a retrofit call, it’s an outer package,
  • The actual network request is okHTTP, so this place generates a call to OKHTTP. This is the first key, and the second key
 call.enqueue(newokhttp3.Callback() {... }Copy the code
  • Isn’t it a little weird to look at a paragraph, you enqueue it on the outside, but you enqueue it on the inside? Why is that?

    • Because the call is no longer a Retrofit call, but an OKHTTP call,
    • He drops this enqueue method, and he does the network request,
    • (okhttp3.response) rawResponse
    • This callback may be given to you by the user, or it may be a CallAdapter (it is possible to switch threads).
  • Take a quick look at parseResponse() to get a sense of the relationship between okHTTP and Retrofit,

    • If code is less than 200 and greater than 300, because only 2xx is normal, all other errors will be reported.
    • But 301, 302, they will do the automatic jump, this is okHTTP, but retrofit he does not care about these, I returned this I will make an error. Normally, okHTTP will do this without retrofit.
    • And then 204, 205 I’ll give you an empty object back.
  • He does these things with a little extra interference outside of OKHTTP, and he makes us more comfortable with them, but there are some limitations.

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  ResponseBody rawBody = rawResponse.body();

  // Remove the body's source (the only stateful object) so we can pass the response along.
  rawResponse = rawResponse.newBuilder()
      .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
      .build();

  int code = rawResponse.code();
  if (code < 200 || code >= 300) {
    try {
      // Buffer the entire body to avoid future I/O.
      ResponseBody bufferedBody = Utils.buffer(rawBody);
      return Response.error(bufferedBody, rawResponse);
    } finally{ rawBody.close(); }}if (code == 204 || code == 205) {
    rawBody.close();
    return Response.success(null, rawResponse);
  }

  ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
  try {
    T body = serviceMethod.toResponse(catchingBody);
    return Response.success(body, rawResponse);
  } catch (RuntimeException e) {
    // If the underlying source threw an exception, propagate that rather than indicating it was
    // a runtime exception.
    catchingBody.throwIfCaught();
    throwe; }}Copy the code
  • He does parse
T body = serviceMethod.toResponse(catchingBody);
Copy the code
  • Point in
R toResponse(ResponseBody body) throws IOException {
  return responseConverter.convert(body);
}
Copy the code
  • You give me an okHTTP ResponseBody, and I’ll convert it for you to the type of the argument that your method is going to return.

  • What does OkHttpCall’s enqueue do?

    • First, we created an OKHTTP call,
    • Then, you use it to make background requests,
    • After the request, parse the body. In addition, if the status code is not correct, I will directly report an error.
    • This is parsed and given to the callback, which may be a callAdapter (switching threads) or returned directly to the user
  • How does createRawCall() create an OKHTTP call?

private okhttp3.Call createRawCall(a) throws IOException {
  okhttp3.Call call = serviceMethod.toCall(args);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}
Copy the code
  • Again servicemethod.toCall (), which is just the result of the response converted with toResponse(). This also uses toCall to convert a call
  • ServiceMethod is the semi-finished information that has previously been parsed from the interfaces in the API and then converted into a real call. I don’t need to look at that, it’s just an interaction with OKHTTP.
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
  RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
      contentType, hasBody, isFormEncoded, isMultipart);

  @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
  ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

  intargumentCount = args ! =null ? args.length : 0;
  if(argumentCount ! = handlers.length) {throw new IllegalArgumentException("Argument count (" + argumentCount
        + ") doesn't match expected count (" + handlers.length + ")");
  }

  for (int p = 0; p < argumentCount; p++) {
    handlers[p].apply(requestBuilder, args[p]);
  }

  return callFactory.newCall(requestBuilder.build());
}
Copy the code
  • At this point, the rough source code of Retrofit is finished. The structure is simple.

4. How to use Adapter rxJava?

  • Add the dependent
implementation 'com. Squareup. Retrofit2: adapter - rxjava: 2.4.0'
Copy the code
  • Add addCallAdapterFactory ()
  • Why is it called CallAdapterFactory,
    • Because it’s used to create the CallAdapter,
    • And CallAdapter is something that can transform threads, automatically switch to the main thread, or it can be an RXJava Observable, Single
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();
Copy the code
  • To define the API, I can use either Call or Observable, which can fit multiple Adapters
@GET("users/renwuxian/repos")
Call<BaseBean> getRepos(a);
@GET("users/renwuxian/repos")
Observable<BaseBean> getRepos1(a);
Copy the code
  • The callAdapterFactories in Retrofit are a collection of factories, not a factory.
final List<CallAdapter.Factory> callAdapterFactories;
Copy the code