Ali two faces: How much do you know about Retrofit?

First, the whole idea

From using the method, first and foremost how to use, followed by the function we use internally is how to achieve, to achieve what skills the program, what paradigm. The full text is basically a source of analysis and Guide Retrofit, Retrofit very suggest that you download the source code after, followed by paper, go over the source code.
Ali two faces: How much do you know about Retrofit?

The figure knowledge summary of PDF content GitHub subsequent updates, like the impact of gold and three silver four small partners can look and see, welcome Star
( pulled left GitHub link, you need access to relevant content such as interviews can find their own )
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

Second, the basic example

2.1 Creating objects Retrofit

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

2.2 API defined API and get examples

  public interface GitHubService { 
    @GET("users/{user}/repos") 
    Call<List<Repo>> listRepos(@Path("user") String user); 
   }
  GitHubService github = retrofit.create(GitHubService.class);

Look at the definition, very simple, and there is nothing special, except for two notes: @GETand @Path. Their usefulness again later analysis, we then look to create API instance: retrofit.create(GitHubService.class). This creates an instance of the API, you can call the API method sends an HTTP request to the network, too convenient. But the createmethod is how to create API instance of it?

  public <T> T create(final Class<T> service) { 
    // 省略非关键代码 
    return (T) Proxy.newProxyInstance(service.getClassLoader(), 
       new Class<?>[] { service }, 
       new InvocationHandler() { 
         @Override 
         public Object invoke(Object proxy, Method method, Object ... args) 
            throws Throwable { 
         // 先省略实现 
         } 
      }); 
  }

API instance is created using a dynamic agent technology.

简而言之,就是动态生成接口的实现类(当然生成实现类有缓存机制),并创建其 实例(称之为代理),代理把对接口的调用转发给 InvocationHandler 实例, 而在 InvocationHandler 的实现中,除了执行真正的逻辑(例如再次转发给真 正的实现类对象),我们还可以进行一些有用的操作,例如统计执行时间、进行初 始化和清理、对接口调用进行检查等。 为什么要用动态代理?因为对接口的所有方法的调用都会集中转发到 InvocationHandler#invoke 函数中,我们可以集中进行处理,更方便了。你可 能会想,我也可以手写这样的代理类,把所有接口的调用都转发到 InvocationHandler#invoke 呀,当然可以,但是可靠地自动生成岂不更方便?

2.3 调用 API 方法

获取到 API 实例之后,调用方法和普通的代码没有任何区别:

  Call<List<Repo>> call = github.listRepos("square"); 
  List<Repo> repos = call.execute().body();

这两行代码就发出了 HTTP 请求,并把返回的数据转化为了 List&lt;Repo&gt;,太方 便了!

现在我们来看看调用 listRepos 是怎么发出 HTTP 请求的。上面 Retrofit#create 方法返回时省略的代码如下:

  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, Object.. . args) 
              throws Throwable { 
            // If the method is a method from Object then defer to n ormal invocation. 
            if (method.getDeclaringClass() == Object.class) { 
              return method.invoke(this, args); 
            }
            if (platform.isDefaultMethod(method)) { 
              return platform.invokeDefaultMethod(method, service, p roxy, args); 
            }
            ServiceMethod serviceMethod = loadServiceMethod(method); 
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); 
            return serviceMethod.callAdapter.adapt(okHttpCall); 
            } 
   });

如果调用的是 Object 的方法,例如 equalstoString,那就直接调用。 如果是 default 方法(Java 8 引入),就调用 default 方法。这些我们都先不管,因 为我们在安卓平台调用 listRepos ,肯定不是这两种情况,那这次调用真正干活 的就是这三行代码了(好好记住这三行代码,因为接下来很长的篇幅都是在讲它们 :) ):

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

在继续分析这三行代码之前,先看一个流程图
Ali two faces: How much do you know about Retrofit?
这三行代码基本就是对应于流程图中轴上部了, ServiceMethodbuild OkHttpCallCallAdapter adapt

2.4 ServiceMethod

ServiceMethod&lt;T&gt; 类的作用正如其 JavaDoc所言:

Adapts an invocation of an interface method into an HTTP call. 把对接口方法 的调用转为一次 HTTP 调用。

一个 ServiceMethod 对象对应于一个 API interface 的一个方 法, loadServiceMethod(method) 方法负责加载 ServiceMethod

  ServiceMethod loadServiceMethod(Method method) { 
     ServiceMethod result; 
     synchronized (serviceMethodCache) { 
        result = serviceMethodCache.get(method); 
        if (result == null) { 
            result = new ServiceMethod.Builder(this, method).build(); 
            serviceMethodCache.put(method, result); 
        }
   }
   return result; 
  }

这里实现了缓存逻辑,同一个 API 的同一个方法,只会创建一次。这里由于我们每 次获取 API 实例都是传入的 class 对象,而 class 对象是进程内单例的,所 以获取到它的同一个方法 Method 实例也是单例的,所以这里的缓存是有效的。

我们再看看 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;
   }

成员很多,但这里我们重点关注四个成 员: callFactorycallAdapterresponseConverterparameterHandlers

  1. callFactory 负责创建 HTTP 请求,HTTP 请求被抽象为了okhttp3.Call 类,它表示一个已经准备好,可以随时执行的 HTTP 请求;
  2. callAdapterretrofit2.Call&lt;T&gt;转为 T (注意和 okhttp3.Call 区分开来,retrofit2.Call&lt;T&gt;表示的是对一个 Retrofit 方法的调用),这个过程会发送一个 HTTP 请求,拿到服务器返回的数据(通 过 okhttp3.Call实现),并把数据转换为声明的 T 类型对象(通过 Converter&lt;F, T&gt; 实现);
  3. responseConverterConverter&lt;ResponseBody, T&gt; 类型,负责把服 务器返回的数据(JSON、XML、二进制或者其他格式,由 ResponseBody封装)转化为 T 类型的对象;
  4. parameterHandlers 则负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数;

它们的使用稍后再分析,这里先看看它们的创建(代码比较分散,就不贴太多代码 了,大多是结论):

2.4.1 callFactory

this.callFactory = builder.retrofit.callFactory(),所以 callFactory 实际上由 Retrofit 类提供,而我们在构造 Retrofit 对象 时,可以指定 callFactory,如果不指定,将默认设置为一个 okhttp3.OkHttpClient

2.4.2 callAdapter
  private CallAdapter<?> createCallAdapter() { 
    // 省略检查性代码 
    Annotation[] annotations = method.getAnnotations(); 
    try { 
      return 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); 
   }
  }

可以看到, callAdapter 还是由 Retrofit 类提供。在 Retrofit 类内部, 将遍历一个 CallAdapter.Factory 列表,让工厂们提供,如果最终没有工厂能 (根据 returnTypeannotations )提供需要的 CallAdapter ,那将抛出 异常。而这个工厂列表我们可以在构造 Retrofit 对象时进行添加。

2.4.3, responseConverter
  private Converter<ResponseBody, T> createResponseConverter() { 
    Annotation[] annotations = method.getAnnotations(); 
    try { 
     return retrofit.responseBodyConverter(responseType, annotati ons); 
    } catch (RuntimeException e) { 
     // Wide exception range because factories are user code. 
    throw methodError(e, "Unable to create converter for %s", re sponseType); 
    } 
  }

同样, responseConverter 还是由 Retrofit 类提供,而在其内部,逻辑和创 建 callAdapter 基本一致,通过遍历 Converter.Factory列表,看看有没有 工厂能够提供需要的 responseBodyConverter。工厂列表同样可以在构造 Retrofit 对象时进行添加。

2.4.4 parameterHandlers

每个参数都会有一个 ParameterHandler ,由 ServiceMethod#parseParameter 方法负责创建,其主要内容就是解析每个参数 使用的注解类型(诸如 Path , Query , Field 等),对每种类型进行单独的 处理。构造 HTTP 请求时,我们传递的参数都是字符串,那 Retrofit 是如何把我们 传递的各种参数都转化为 String 的呢?还是由 Retrofit 类提供 converter!

Converter.Factory 除了提供上一小节提到的 responseBodyConverter,还提 供 requestBodyConverterstringConverter,API 方法中除了 @Body@Part 类型的参数,都利用 stringConverter 进行转换,而 @Body@Part 类型的参数则利用 requestBodyConverter 进行转换。

这三种 converter 都是通过“询问”工厂列表进行提供,而工厂列表我们可以在构造 Retrofit 对象时进行添加。

2.4.5 工厂让各个模块得以高度解耦

上面提到了三种工厂: okhttp3.Call.FactoryCallAdapter.FactoryConverter.Factory ,分别负责提供不同的模块,至于怎么提供、提供何种模 块,统统交给工厂,Retrofit 完全不掺和,它只负责提供用于决策的信息,例如参 数/返回值类型、注解等。

这不正是我们苦苦追求的高内聚低耦合效果吗?解耦的第一步就是面向接口编程, 模块之间、类之间通过接口进行依赖,创建怎样的实例,则交给工厂负责,工厂同 样也是接口,添加(Retrofit doc 中使用 install 安装一词,非常贴切)怎样的工 厂,则在最初构造 Retrofit 对象时决定,各个模块之间完全解耦,每个模块只 专注于自己的职责,全都是套路,值得反复玩味、学习与模仿。

除了上面重点分析的这四个成员, ServiceMethod 中还包含了 API 方法的 url 解 析等逻辑,包含了众多关于泛型和反射相关的代码,有类似需求的时候,也非常值 得学习模仿

2.5 OkHttpCall

终于把 ServiceMethod 看了个大概,接下来我们看看 OkHttpCallOkHttpCall 实现了 retrofit2.Call ,我们通常会使用它的 execute()enqueue(Callback&lt;T&gt; callback) 接口。前者用于同步执行 HTTP 请求,后者 用于异步执行。

2.5.1,先看 execute()
  @Override 
  public Response<T> execute() throws IOException { 
    okhttp3.Call call; 
    synchronized (this) { 
     // 省略部分检查代码 

    call = rawCall;
    if (call == null) { 
      try { 
        call = rawCall = createRawCall(); 
      } catch (IOException | RuntimeException e) { 
        creationFailure = e; 
        throw e; 
     } 
    } 
  }
   return parseResponse(call.execute()); 
   ......
 }

主要包括三步:

  1. 创建 okhttp3.Call ,包括构造参数;
  2. 执行网络请求;
  3. 解析网络请求返回的数据;

createRawCall() 函数中,我们调用了 serviceMethod.toRequest(args) 来创建 okhttp3.Request ,而在后者中,我们之前准备好的 parameterHandlers 就派上了用场。

然后我们再调用 serviceMethod.callFactory.newCall(request) 来创建 okhttp3.Call ,这里之前准备好的 callFactory 同样也派上了用场,由于工 厂在构造 Retrofit 对象时可以指定,所以我们也可以指定其他的工厂(例如使 用过时的 HttpURLConnection 的工厂),来使用其它的底层 HttpClient 实现。

我们调用 okhttp3.Call#execute() 来执行网络请求,这个方法是阻塞的,执行 完毕之后将返回收到的响应数据。收到响应数据之后,我们进行了状态码的检查, 通过检查之后我们调用了 serviceMethod.toResponse(catchingBody) 来把响 应数据转化为了我们需要的数据类型对象。在 toResponse 函数中,我们之前准 备好的 responseConverter 也派上了用场。

好了,之前准备好的东西都派上了用场,还好没有白费 :)

2.5.2 再看 enqueue(Callback&lt;T&gt; callback)

这里的异步交给了 okhttp3.Call#enqueue(Callback responseCallback) 来 实现,并在它的 callback 中调用 parseResponse 解析响应数据,并转发给传入 的 callback

2.6 CallAdapter

终于到了最后一步了, CallAdapter&lt;T&gt;#adapt(Call&lt;R&gt; call) 函数负责把 retrofit2.Call&lt;R&gt; 转为 T 。这里 T 当然可以就是 retrofit2.Call&lt;R&gt; ,这时我们直接返回参数就可以了,实际上这正是 DefaultCallAdapterFactory创建的 CallAdapter 的行为。至于其他类型的 工厂返回的 CallAdapter 的行为,这里暂且不表,后面再单独分析。

至此,一次对 API 方法的调用是如何构造并发起网络请求、以及解析返回数据,这 整个过程大致是分析完毕了。对整个流程的概览非常重要,结合 stay 画的流程图, 应该能够比较轻松地看清整个流程了。

虽然我们还没分析完,不过也相当于到了万里长征的遵义,终于可以舒一口气了 :)

三、retrofit-adapters 模块

retrofit 模块内置了 DefaultCallAdapterFactoryExecutorCallAdapterFactory ,它们都适用于 API 方法得到的类型为 retrofit2.Call 的情形,前者生产的 adapter 啥也不做,直接把参数返回,后 者生产的 adapter 则会在异步调用时在指定的 Executor 上执行回调。

retrofit-adapters 的各个子模块则实现了更多的工 厂: GuavaCallAdapterFactoryJava8CallAdapterFactoryRxJavaCallAdapterFactory 。这里我主要分析 RxJavaCallAdapterFactory ,下面的内容就需要一些 RxJava 的知识了,不过 我想使用 Retrofit 的你,肯定也在使用RxJava :)

RxJavaCallAdapterFactory#get 方法中对返回值的类型进行了检查,只支持 rx.Singlerx.Completablerx.Observable ,这里我主要关注对 rx.Observable 的支持。

RxJavaCallAdapterFactory#getCallAdapter 方法中对返回值的泛型类型进行 了进一步检查,例如我们声明的返回值类型为 Observable&lt;List&lt;Repo&gt;&gt;,泛型 类型就是 List&lt;Repo&gt; ,这里对 retrofit2.Responseretrofit2.adapter.rxjava.Result 进行了特殊处理,有单独的 adapter 负责 进行转换,其他所有类型都由 SimpleCallAdapter负责转换。

那我们就来看看 SimpleCallAdapter#adapt

  @Override 
  public <R> Observable<R> adapt(Call<R> call) { 
    Observable<R> observable = Observable.create(new CallOnSubscri be<>(call)) 
       .lift(OperatorMapResponseToBodyOrError.<R>instance()); if (scheduler != null) { 
      return observable.subscribeOn(scheduler); 
    }
    return observable; 
  }

这里创建了一个 Observable ,它的逻辑由 CallOnSubscribe 类实现,同时使 用了一个 OperatorMapResponseToBodyOrError 操作符,用来把 retrofit2.Response 转为我们声明的类型,或者错误异常类型。

我们接着看 CallOnSubscribe#call

  @Override 
  public void call(final Subscriber<? super Response<T>> subscribe r) {
    // Since Call is a one-shot type, clone it for each new subscr iber. 
    Call<T> call = originalCall.clone(); 
   // Wrap the call in a helper which handles both unsubscription and backpressure. 
   RequestArbiter<T> requestArbiter = new  RequestArbiter<>(call, subscriber); 
   subscriber.add(requestArbiter); 
   subscriber.setProducer(requestArbiter); 
  }

代码很简短,只干了三件事:

  1. clone 了原来的 call,因为 okhttp3.Call 是只能用一次的,所以每次都是 新 clone 一个进行网络请求;
  2. 创建了一个叫做 RequestArbiterproducer,别被它的名字吓懵了,它就 是个 producer
  3. 把这个 producer设置给 subscriber

简言之,大部分情况下 Subscriber 都是被动接受 Observable push 过来的数据, 但要是 Observable 发得太快,Subscriber 处理不过来,那就有问题了,所以就有 了一种 Subscriber 主动 pull 的机制,而这种机制就是通过 Producer 实现的。给 Subscriber 设置 Producer 之后(通过 Subscriber#setProducer 方法), Subscriber 就会通过 Producer 向上游根据自己的能力请求数据(通过 Producer#request 方法),而 Producer 收到请求之后(通常都是 Observable 管理 Producer,所以“相当于”就是 Observable 收到了请求),再根据请求的量给 Subscriber 发数据。

那我们就看看 RequestArbiter#request

  @Override 
  public void request(long n) { 
     if (n < 0) throw new IllegalArgumentException("n < 0: " + n); 
     if (n == 0) return; // Nothing to do when requesting 0. 
     if (!compareAndSet(false, true)) return; // Request was alread y triggered. 
     try { 
       Response<T> response = call.execute(); 
       if (!subscriber.isUnsubscribed()) { 
         subscriber.onNext(response); 
       } 
     } catch (Throwable t) { 
       Exceptions.throwIfFatal(t); 
       if (!subscriber.isUnsubscribed()) { 
         subscriber.onError(t); 
       }
       return; 
     }

    if (!subscriber.isUnsubscribed()) { 
       subscriber.onCompleted(); 
    } 
   }

producer 相关的逻辑非常简单,这里就不在赘述。实际干活的逻辑就是执行 call.execute() ,并把返回值发送给下游。

OperatorMapResponseToBodyOrError#call 也相当简短:

  @Override 
  public Subscriber<? super Response<T>> call(final Subscriber<? s uper T> child) { 
    return new Subscriber<Response<T>>(child) { 
       @Override 
       public void onNext(Response<T> response) { 
         if (response.isSuccessful()) { 
          child.onNext(response.body()); 
         } else { 
          child.onError(new HttpException(response)); 
        } 
      }

      @Override 
      public void onCompleted() { 
         child.onCompleted(); 
      }

      @Override
      public void onError(Throwable e) { 
         child.onError(e); 
      } 
    }; 
  }

关键就是调用了 response.body() 并发送给下游。这里, body() 返回的就是 我们声明的泛型类型了,至于 Retrofit 是怎么把服务器返回的数据转为我们声明的 类型的,这就是 responseConverter 的事了,还记得吗?

最后看一张返回 Observable 时的调用栈:
Ali two faces: How much do you know about Retrofit?
执行路径就是:

  1. Observable.subscribe ,触发 API 调用的执行;
  2. CallOnSubscribe#callclone call,创建并设置 producer
  3. RequestArbiter#requestsubscriber 被设置了 producer 之后最终调用 request,在 request 中发起请求,把结果发给下游;
  4. OperatorMapResponseToBodyOrError$1#onNext, To responsethe body sent downstream;
  5. We finally went to subscribean incoming callback inside;

Four, retrofit-converters module

retrofit module built BuiltInConverters, only processing ResponseBody, RequestBodyand conversion of type String (switch is not actually required). Retrofit- converters in the sub-module is provided JSON, XML, ProtoBufand other types of data conversion, but there are many ways to choose conversion. My main concern here GsonConverterFactory.

The code is very simple:

  @Override 
  public Converter<ResponseBody, ?> responseBodyConverter(Type typ e, Annotation[] annotations, Retrofit retrofit) { 
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); 
    return new GsonResponseBodyConverter<>(gson, adapter); 
  }

  final class GsonResponseBodyConverter<T> implements Converter<Re sponseBody, T> { 
    private final Gson gson; 
    private final TypeAdapter<T> adapter; 

    GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) { 
      this.gson = gson; 
      this.adapter = adapter; 
    }

    @Override public T convert(ResponseBody value) throws IOExcept ion {
      JsonReader jsonReader = gson.newJsonReader(value.charStream( )); 
      try { 
        return adapter.read(jsonReader); } finally { 
           value.close(); 
           } 
       } 
  }

The target type, use Gson#getAdapterto obtain the corresponding Adapter, conversion using Gsonthe API can.

The figure knowledge summary of PDF content GitHub subsequent updates, like the impact of gold and three silver four small partners can look and see, welcome Star
( pulled left GitHub link, you need access to relevant content such as interviews can find their own )
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)

Guess you like

Origin blog.51cto.com/14541311/2471193