漫漫Android路

Retrofit 2.3.0 源码解析

2017-07-03  本文已影响125人  osan

前言

Retrofit A type-safe HTTP client for Android and Java

Retrofit,是一个基于http请求库二次封装的HTTP客户端,将 REST API 转换为 Java 接口。

基于注解,进一步解放了生产力,使得http请求就像调用方法一样简单,如丝般顺滑。

结构概览

architecture.png

项目结构整体分四个部分,Builder -> Proxy -> Invocation -> RawCall
这里我们把基于Retrofit的HTTP通信比做是邮递信件。

邮递信件

基于Retrofit的HTTP通信

来看看寄信和Retrofit之间的对比:

arch_flow.png

大概过程就是这样,邮递员会把信送出去,并在适合的时机把对方的回信取回来送给你,当然如果你的信件是表白情书,那也很可能会收不到回信的,毕竟表白成功的概率要看人品的。不要伤心,HTTP通信也会有时候收不到服务端的回信噢。

目录概览

官方 Javadoc

│  BuiltInConverters.java               # 内建Converter
│  Call.java                            # 发送请求接收响应的retrofit方法调用
│  CallAdapter.java                     # 适配Call的响应类型,将默认响应类型R转换为类型T
│  Callback.java                        # 返回服务端或离线请求的响应体
│  Converter.java                       # HTTP交互中,转换对象为数据 或 从数据转换为对象
│  DefaultCallAdapterFactory.java       # 默认CallAdapter工厂
│  ExecutorCallAdapterFactory.java      # http请求执行器工厂
│  HttpException.java                   # 非2xx HTTP响应的异常处理
│  OkHttpCall.java                      # 真正调用OkHttp3发送Http请求的类
│  package-info.java                    # 包描述
│  ParameterHandler.java                # 参数注解解析器
│  Platform.java                        # 平台适配(Java/Android)
│  RequestBuilder.java                  # 请求拼装
│  Response.java                        # 原汁原味的HTTP 响应体,所谓 T body
│  Retrofit.java                        # 组装工厂,基于建造者模式拼装自定义HTTP交互所需的组件,并作为总调度暴露接口
│  ServiceMethod.java                   # 框架核心处理类,注解解析器调度,生成请求(包含api url、path、http请求方法、请
                                        # 求头、是否是multipart等等),并返回用于发起http请求的Call对象
│  Utils.java                           # 工具类
│  
└─http                              # http注解定义 (直接引用了Javadoc中的描述,均为提高生产力的注解)

        Body.java                       # control the request body of a POST/PUT request
        DELETE.java                     # Make a DELETE request
        Field.java                      # Named pair for a form-encoded request
        FieldMap.java                       # Named key/value pairs for a form-encoded request
        FormUrlEncoded.java                 # Denotes that the request body will use form URL encoding
        GET.java                        # Make a GET request
        HEAD.java                       # Make a HEAD request
        Header.java                     # Replaces the header with the value of its target
        HeaderMap.java                      # Adds headers specified in the Map
        Headers.java                        # Adds headers literally supplied in the value
        HTTP.java                       # Use a custom HTTP verb for a request
        Multipart.java                      # Denotes that the request body is multi-part
        OPTIONS.java                        # Make an OPTIONS request
        package-info.java                   # Package description
        Part.java                       # Denotes a single part of a multi-part request
        PartMap.java                        # Denotes name and value parts of a multi-part request
        PATCH.java                      # Make a PATCH request
        Path.java                       # Named replacement in a URL path segment
        POST.java                       # Make a POST request
        PUT.java                        # Make a PUT request
        Query.java                      # Query parameter appended to the URL
        QueryMap.java                       # Query parameter keys and values appended to the URL
        QueryName.java                      # Query parameter appended to the URL that has no value
        Streaming.java                      # Treat the response body on methods returning Response as is, i.e. 
                                # without converting body() to byte[]
        Url.java                        # URL resolved against the base URL

Retrofit的基本用法

让我们从基本用法开始,先看如何使用,顺着这个藤,摸摸如何实现的瓜。

用 Java 接口的方式定义一个HTTP API.

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

Retrofit 类生成一个 GitHubService 接口的实现实例.

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

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

Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
GitHubService实例的每一个方法调用都支持同步或异步HTTP请求.

Call<List<Repo>> repos = service.listRepos("octocat");

执行同步或异步HTTP请求,得到HTTP响应数据.

Response<List<Repo>> response = repos.execute();

Retrofit的源码解析

首先我们心里要有个概念,Retrofit的核心关键词:注解、动态代理、转换器、适配器

Retrofit就是基于这四个关键词搭建起来的充分解耦,灵活,可插拔的优秀框架。

下面我们结合Retrofit设计图流程来解读代码。 还记得流程吗? Builder -> Proxy -> Invocation -> RawCall.

Flow - Builder

Retrofit.Builder() .baseUrl("https://api.github.com/") ... .build();
Tips.设计模式之Builder模式

基于Builder模式,装配一系列零部件,比如base请求地址,gson转换器,Rxjava适配器,HTTP请求client(比如装配OKHTTP)等。

// Retrofit.java -> class Builder

public Retrofit build() {
      
      ...

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

返回一个装配了 callFactory,converterFactories,adapterFactories,callbackExecutor 和指定了 baseUrl 的 Retrofit 实例。
注:validateEagerly,用于指定是否预先解析注解,加速接口访问效率。

Flow - Proxy

GitHubService service = retrofit.create(GitHubService.class);
我们知道,Java 接口是不可以直接 new 实例的,那么这个 create 方法看起来又像是返回了一个 GitHubService 接口类型的实现实例,这是怎么回事呢?我们来看下 create 的实现。

// Retrofit.java

  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() {
          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);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

create方法主要就一个return,返回了一个Proxy.newProxyInstance生成的动态代理对象。原来这里是通过动态代理的方式生成了 GitHubService 接口的代理实例,那么后续 GitHubService 接口的方法都可以通过代理去调用了。
为什么用动态代理?
这是Retrofit设计的核心思路,基于动态代理,可以为后续在调用 GitHubService 接口的相关方法时先拦截下来,做完一系列工作后(即注解解析,请求转换,适配等),再去完成方法本尊想要完成的工作,这就是动态代理的魅力。

Tips.动态代理

Call<List<Repo>> repos = service.listRepos("octocat");
通过代理对象 service 调用接口方法 listRepos ,会被动态代理拦截,调用Proxy.newProxyInstance方法中的InvocationHandler对象的 invoke 方法。

invoke中主要由ServiceMethod和CallAdapter完成了三件事:

ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);

ServiceMethod.java

// ServiceMethod.java

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      
      ...

      responseConverter = createResponseConverter();

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

      ...

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        
        ...

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];

        ...

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

      ...

      return new ServiceMethod<>(this);
    }

获取callAdapter、responseType、responseConverter接口对象

解析Method的注解

解析Method的参数注解

解析Method的参数中使用了依赖请求API的动态参数的注解,交由ParameterHandler处理

CallAdapter.java

public interface CallAdapter<R, T> {
 
  Type responseType();

  ...

  T adapt(Call<R> call);

  ...
  
  }

适配Call的响应类型,将默认响应类型R转换为类型T.比如官方的RxJavaCallAdapter可以结合Rxjava特性对Call的响应做RxJava观察者模式转换,进一步解放生产力。

注:未在Builder阶段指定CallAdapter(如 RxJavaCallAdapterFactory )的情况下,默认的 CallAdapter 不对Call做任何处理。
见 DefaultCallAdapterFactory:

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  ...
      ...

      @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
  }
}

Flow - Invocation

Response<List<Repo>> response = repos.execute();

这一步开始基于同步的方式执行HTTP请求,并得到返回的HTTP响应数据.

本质上是执行了 OkHttpCall 的 execute方法.

// OkHttpCall.java

@Override public Response<T> execute() throws IOException {

    synchronized (this) {
     
     ...
        ...
          call = rawCall = createRawCall();

    }

    ...

    return parseResponse(call.execute());
  }

如你所见,这里创建了RawCall,即真正的去执行HTTP请求任务的对象。
这里还负责HTTP请求的响应数据解析。
我们看下createRawCall()干了什么。

// OkHttpCall.java

private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    ...
    return call;
  }

serviceMethod.toRequest()的功能:

// ServiceMethod.java

/** Builds an HTTP request from method arguments. */
  Request toRequest(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    ...

    return requestBuilder.build();
  }

toRequest 方法通过 RequestBuilder 创建了 okhttp3 做 HTTP 请求时需要的 Request 对象。

serviceMethod.callFactory.newCall(request)的功能:
建立一个请求通道,为执行HTTP请求做准备。
这里callFactory可以由使用者指定,默认为 OkHttpClient,见:

// Retrofit.java

okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

回头看下 OkHttpCall 中 execute 方法最后一句: return parseResponse(call.execute());
这里调用真正的HTTP请求客户端的请求执行方法。也就是来到了接下来的一个流程。

Flow - RawCall

上个 Flow 中最后一步, call.execute(),开启了真正的HTTP请求,即通过 okhttp3 完成HTTP请求。
这个部分没什么代码可讲,属于面向接口开发的典范,要讲就该去讲 Okhttp 框架的源码了。

这个部分引出了 Retrofit 的开源拥有者-Square 公司的另一个优秀的开源项目 Okhttp,是不是也很想一探究竟?

End

上一篇下一篇

猜你喜欢

热点阅读