Retrofit详解(下)

Retrofit详解(下)

上一篇文件介绍了Retrofit的基本使用,接下来我们通过从源码的角度分析一下Retrofit的实现。

首先看一下它的基本使用方法:

// 1
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
// 2
GitHubService gitHubService = retrofit.create(GitHubService.class);

// 3
Call<List<Repo>> call = gitHubService.listRepos("CharonChui");

// 4
call.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        List<Repo> data = response.body();
        Log.i("@@@", "data size : " + (data == null ? "null" : data.size() + ""));
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {

    }
});

我把上面主要分为4个部分,接下来逐一分析:

1. 创建Retrofit并进行配置。

Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("https://api.github.com/")
  .addConverterFactory(GsonConverterFactory.create())
  .build();

简单的一句话,却埋藏了很多。

这是典型的建造者模式、外观模式

就想平时我们写的下载模块,作为一个公共的模块,我们可以对外提供一个DownloadManager供外界使用,而对于里面的实现我们完全可以闭门造车。

具体baseUrl()addConverterFactory()方法里面的具体实现就不去看了,比较简单。当然这里也用到了工厂设计模式。

2. 创建对应的服务类

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

这一部分是如何实现的呢?我们看一下retrofit.create()方法的实现:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          // 这里的Platform主要是为了检测当前的运行平台,是java还是android,会根据当前的平台来返回默认的CallAdapter
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, 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);
            }
            // 1,根据动态代理的方法去生成ServiceMethod这里动态代理的方法就是listRepos方法
            ServiceMethod serviceMethod = loadServiceMethod(method);
            // 2,根绝ServiceMethod和参数去生成OkHttpCall,这里args是CharonChui
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            // 3, serviceMethod去进行处理并返回Call对象,拿到这个Call对象才能去执行网络请求。
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

看到Proxy.newProxyInstance()就明白了,这里使用了动态代理。简单的说动态代理是在你要调用某个Class的方法前或后,插入你想要执行的代码。那这里要代理的是什么方法? Call<List<Repo>> call = gitHubService.listRepos("CharonChui");,这里就是listRepos()方法。 就是说在调用listRepos()方法时会被动态代理所拦截,然后执行Proxy.newProxyInstance()里面的InvocationHandler.invoke()中的部分。 而invoke()方法的三个参数分别是啥? 分别是Object proxy: 代理对象,Method method:调用的方法,就是listRepos()方法,Object... args:方法的参数,这里是CharonChui

有关动态代理介绍可以看张孝祥老师的java1.5高新技术系列中的动态代理

这里就不仔细介绍动态代理了,上面的代码中又分为三部分:

  • loadServiceMethod()
  • new OkHttpCall()
  • serviceMethod.callAdapter.adapt()

我们这里分别来进行分析。

  • loadServiceMethod()
    实现如下:

    ServiceMethod loadServiceMethod(Method method) {
        ServiceMethod result;
        synchronized (serviceMethodCache) {
          // ServiceMethod包含了请求的所有相关数据,以及获取请求的request和把请求结果转换成java对象,所以相比较而言较重,用缓存来提高效率。
          result = serviceMethodCache.get(method);
          if (result == null) {
            // build
            result = new ServiceMethod.Builder(this, method).build();
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }

    会通过缓存的方式来获取一个ServiceMethod类。通过缓存来保证同一个API的同一个方法只会创建一次。
    有关ServiceMethod类的文档介绍是:

    Adapts an invocation of an interface method into an HTTP call.

    大体翻译一下就是将一个请求接口的方法转换到Http Call中调用。

    而上面第一次使用的时候会通过new ServiceMethod.Builder(this, method).build()创建,那我们看一下它的实现:

    public ServiceMethod build() {
           // 创建CallAdapter用来代理Call
          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用来解析结果将json等返回结果解析成java对象
          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 = new ParameterHandler<?>[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);
            }
            //  解析对应method的注解,这里是listRepos方法的注解。
            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
            if (parameterAnnotations == null) {
              throw parameterError(p, "No Retrofit annotation found.");
            }
            // 通过注解和参数类型,解析并赋值到parameterHandlers中
            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.");
          }
          // 创建`ServiceMethod()`对象
          return new ServiceMethod<>(this);
        }

    总起来说,就是创建CallAdapterresponseConverter、解析注解、设置参数,然后创建ServiceMethod对象。

    ServiceMethod的构造函数如下:

    ServiceMethod(Builder<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;
      }

    看到吗? 这一部分我们应该都稍微有点印象,因为在上一篇文章介绍使用方法的时候,基本会用到这里。

至于这里的CallAdapterResponseConverterHeadersParamterHandlers等这里就不分析了,最后我们再简单介绍下。

  • 创建OkHttpCall

    接下来会创建OkHttpCall,而OkHttpCallCall的子类,那Call是什么鬼? 它是具体的网络请求类。

    文档中对Call类的介绍如下:

    /**
     * An invocation of a Retrofit method that sends a request to a webserver and returns a response.
     * Each call yields its own HTTP request and response pair. Use {@link #clone} to make multiple
     * calls with the same parameters to the same webserver; this may be used to implement polling or
     * to retry a failed call.
     *
     * <p>Calls may be executed synchronously with {@link #execute}, or asynchronously with {@link
     * #enqueue}. In either case the call can be canceled at any time with {@link #cancel}. A call that
     * is busy writing its request or reading its response may receive a {@link IOException}; this is
     * working as designed.
     *
     * @param <T> Successful response body type.
     */
    public interface Call<T> extends Cloneable {
        // 这里我特地把这句话放上,Call集成了Cloneable接口。
        // 每一个 call 对象实例只能被用一次,所以说 request 和 response 都是一一对应的。你其实可以通过 Clone 方法来创建一个一模一样的实例,这个开销是很小的。比如说:你可以在每次决定发请求前 clone 一个之前的实例。
        ....
    }

    Retrofit底层默认使用OkHttp,所以当然要创建OkHttpCall了。

    • serviceMethod.callAdapter.adapt(okHttpCall)

    这个CallApdater是什么鬼?

    /**
     * Adapts a {@link Call} into the type of {@code T}. Instances are created by {@linkplain Factory a
     * factory} which is {@linkplain Retrofit.Builder#addCallAdapterFactory(Factory) installed} into
     * the {@link Retrofit} instance.
     */

    可以很简单的看出来这是一个结果类型转换的类。就是Call的适配器,作用就是创建/转换Call对象,把Call转换成预期的格式。CallAdatper创建是通过CallAdapter.factory工厂类进行的。DefaultCallAdapterRetrofit2自带默认Call转换器,用来生成OKHTTPcall请求调用。

    而它里面的adapt()方法的作用呢?

    /**
       * Returns an instance of {@code T} which delegates to {@code call}.
       * <p>
       * For example, given an instance for a hypothetical utility, {@code Async}, this instance would
       * return a new {@code Async<R>} which invoked {@code call} when run.
       * <pre><code>
       * &#64;Override
       * public &lt;R&gt; Async&lt;R&gt; adapt(final Call&lt;R&gt; call) {
       *   return Async.create(new Callable&lt;Response&lt;R&gt;&gt;() {
       *     &#64;Override
       *     public Response&lt;R&gt; call() throws Exception {
       *       return call.execute();
       *     }
       *   });
       * }
       * </code></pre>
       */
      <R> T adapt(Call<R> call);

    所以分析到这里我们基本明白了retrofit.create()方法的作用,就是将请求接口的服务类转换成Call,然后将Call的结果转换成实体类。

3. 调用方法,得到Call对象

Call<List<Repo>> call = gitHubService.listRepos("CharonChui");

这个就不分析了,就是在ServiceMethod中返回的Call

4. 调用Call.enqueue()

在上面分析了,这个Call其实是OkHttpCall,那我们来看一下OkHttpCall.enqueue()方法的实现:

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("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 {
          // 调用createRawCall()方法创建Call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
    // 把请求任务加入到okhttp的请求队列中,执行网络请求,注意这里的Call是okhttp3.Call
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          // 解析结果,该方法内部会将OkHttp中Request的执行结果转换成对应的Java对象。
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

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

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

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

上面的部分也主要分为三部分:

  • 创建Call
  • 执行网络请求
  • 解析结果
第一部分:创建Call

我们分别进行分析,首先是createRawCall()方法的实现:

private okhttp3.Call createRawCall() throws IOException {
  // ServiceMethod.toRequest()方法的作用是将ServiceMethod中的网络请求相关的数据转换成一个OkHttp的网络请求所需要的Request对象。因为之前分析过所有Retrofit解析的网络请求相关的数据都是在ServiceMethod中
  Request request = serviceMethod.toRequest(args);
  // 调用Factory.newCall方法
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

然后看一下Factory.newCall()方法:

interface Factory {
    Call newCall(Request request);
  }

它的实现类是OkHttpClient类中的newCall()方法,并且创建RealCall对象:

@Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }
第二部分:执行网络请求

这一部分是在call.enqueue()方法中执行的,上面我们分析了创建的Call最终是RealCall类,所以这里直接到看RealCall.enqueue()方法:

@Override public void enqueue(Callback responseCallback) {
    enqueue(responseCallback, false);
  }

  void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }

    // 调用OkHttpClient中的Dispatcher中的enqueue方法
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }

我们接着看client.dispatcher().enqueue()方法:

synchronized void enqueue(AsyncCall call) {
    // maxRequests的个数是64;
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      // 线程池执行
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

而这个参数AsyncCall是什么鬼? 它是RealCall的内部类,它里面的execute()方法是如何实现的? 只要找到该方法的实现就算是完成了。

首先看一下AsyncCall的声明和构造:

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private final boolean forWebSocket;

    private AsyncCall(Callback responseCallback, boolean forWebSocket) {
      super("OkHttp %s", redactedUrl().toString());
      this.responseCallback = responseCallback;
      this.forWebSocket = forWebSocket;
    }
    ...
  }

NamedRunnableRunnable的实现类。我们看一下它的execute()方法:

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        // 获取请求结果
        Response response = getResponseWithInterceptorChain(forWebSocket);
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          // 将响应结果设置给之前构造函数传递回来的回调
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

接着看一下RealCall.getResponseWithInterceptorChain():

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
    Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);
  }

  class ApplicationInterceptorChain implements Interceptor.Chain {
    private final int index;
    private final Request request;
    private final boolean forWebSocket;

    ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
      this.index = index;
      this.request = request;
      this.forWebSocket = forWebSocket;
    }

    @Override public Connection connection() {
      return null;
    }

    @Override public Request request() {
      return request;
    }

    @Override public Response proceed(Request request) throws IOException {
      // If there's another interceptor in the chain, call that.
      if (index < client.interceptors().size()) {
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        Interceptor interceptor = client.interceptors().get(index);
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }

        return interceptedResponse;
      }

      // No more interceptors. Do HTTP.
      return getResponse(request, forWebSocket);
    }
  }

又会调用getResponse()方法:

/**
   * Performs the request and returns the response. May return null if this call was canceled.
   */
  Response getResponse(Request request, boolean forWebSocket) throws IOException {
    // Copy body metadata to the appropriate request headers.
    RequestBody body = request.body();
    if (body != null) {
      Request.Builder requestBuilder = request.newBuilder();

      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }

      request = requestBuilder.build();
    }

    // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = 0;
    while (true) {
      if (canceled) {
        engine.releaseStreamAllocation();
        throw new IOException("Canceled");
      }

      boolean releaseConnection = true;
      try {
        engine.sendRequest();
        engine.readResponse();
        releaseConnection = false;
      } catch (RequestException e) {
        // The attempt to interpret the request failed. Give up.
        throw e.getCause();
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), true, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }
        // Give up; recovery is not possible.
        throw e.getLastConnectException();
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        HttpEngine retryEngine = engine.recover(e, false, null);
        if (retryEngine != null) {
          releaseConnection = false;
          engine = retryEngine;
          continue;
        }

        // Give up; recovery is not possible.
        throw e;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          StreamAllocation streamAllocation = engine.close();
          streamAllocation.release();
        }
      }

      Response response = engine.getResponse();
      Request followUp = engine.followUpRequest();

      if (followUp == null) {
        if (!forWebSocket) {
          engine.releaseStreamAllocation();
        }
        return response;
      }

      StreamAllocation streamAllocation = engine.close();

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (!engine.sameConnection(followUp.url())) {
        streamAllocation.release();
        streamAllocation = null;
      } else if (streamAllocation.stream() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,
          response);
    }
  }

完了没有? 完了….

上面只是简单的分析了下大体的调用流程和主要的类,但是好像并没有什么乱用,因为没有具体的去分析里面各部分的实现,如果都分析下来内容太多了。这里就不仔细看了,大体总结一下。

通过上面的分析,最终的网络请求是在OkHttpCall中去执行,也就是说Retrofit其实是将一个Java接口通过注解等方式来解析参数等然后转换成一个请求交给OkHttp去执行,然后将执行结果进行解析转换暴露给上层调用者。
而这一切是如何实现的呢?
Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了动态代理的方式,动态的将这个接口的注解转换成一个HTTP请求,然后再将这个Http请求交给OkHttp执行。

动态代理用的太妙,而它的过程中也使用了大量的工厂模式,这里就不分析了。

参考:
- simple-http-retrofit-2
- Retrofit API


更多文章请查看AndroidNote

你的star是我的动力!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值