It's our wits that make us men.

Retrofit解析!

Posted on By yan zi jie

First POST build by Jekyll.github:https://github.com/nishibaiyang

Retrofit解析

Retrofit 概览

Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit 来完成的。Retrofit 2.0 开始内置 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,我们的应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。

Retrofit 使用

省略

Retrofit 原理

Retrofit使用接口+注解的形式来定义一个网络请求,在通过OkHttp来执行网络请求。现在有很多开源使用了注解Annotation,如Dagger2、ButterKnife等,以及Retrofit。所不同的是他们利用注解干的事却不一样。Dagger2、ButterKnife他们在编译期间就处理注解生成代码,提供依赖注入。Retrofit则是则运行期间处理注解,通过动态代理的方式来提供AOP能力。

是谁实际上完成了接口请求的处理?

前面讲了这么久,我们始终只看到了我们自己定义的接口,比如:

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

其实它是 Retrofit 创建的一个代理对象了,这里涉及点儿 Java 的动态代理的知识,直接来看代码:

Github github = retrofit.create(Github.class);
Call<List<Contributor>> call = github.getContributors("square", "retrofit");

它使用了动态代理的方式来创建一个网络请求任务,create方法返回了一个动态代理对象github。

Java动态代理就是Java开发给了开发人员一种可能:当你要调用某个类的方法前,插入你想要执行的代码比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱。这么简单的一句话,我相信可以把一个不懂技术的人也讲明白Java动态代理是什么东西了。那么问题来了,我们的接口是Github,但是并未提供Github的实现类,动态代理对象到底代理的是哪个具体类的方法啊?

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //这里返回一个 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, 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);
            }
            //DefaultMethod 是 Java 8 的概念,是定义在 interface 当中的有实现的方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //每一个接口最终实例化成一个 ServiceMethod,并且会缓存
            ServiceMethod serviceMethod = loadServiceMethod(method);
            
            //由此可见 Retrofit 与 OkHttp 完全耦合,不可分割
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //下面这一句当中会发起请求,并解析服务端返回的结果
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

简单的说,在我们调用 GitHubService.listRepos 时,实际上调用的是这里的 InvocationHandler.invoke 方法~~

可以看到实际上是构造了一个okhttpcall对象,这么可以猜测网络请求调用的okhttpcall. execute.

完整的一次请求

前面我们已经看到 Retrofit 为我们构造了一个 OkHttpCall ,实际上每一个 OkHttpCall 都对应于一个请求,它主要完成最基础的网络请求,而我们在接口的返回中看到的 Call 默认情况下就是 OkHttpCall 了,如果我们添加了自定义的 callAdapter,那么它就会将 OkHttp 适配成我们需要的返回值,并返回给我们。

我们来看下OkHttpCall.execute

@Override public Response<T> execute() throws IOException {
    //这个 call 是真正的 OkHttp 的 call,本质上 OkHttpCall 只是对它做了一层封装
    okhttp3.Call call;

    synchronized (this) {
      //处理重复执行的逻辑
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }
      
      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }
    //发起请求,并解析结果
    return parseResponse(call.execute());
  }

parseResponse 主要完成了由 okhttp3.Response 向 retrofit.Response 的转换,同时也处理了对原始返回的解析:

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

    //略掉一些代码
    try {
      //在这里完成了原始 Response 的解析,T 就是我们想要的结果,比如 GitHubService.listRepos 的 List<Repo>
      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();
      throw e;
    }
  }

在Retrofit的默认实现中,CallBack回调运行在主线程上,而不用管retrofit是否是在主线程中调用。

1、 因为Retrofit底层调用的是OkHttp,而OkHttp的回调并非运行在主线程,所以Retrofit对它进行了封装,使用主线程Handler进行分发,使得CallBack回调运行在主线程上

2、 也就说不管在哪个线程中调用retrofit,我们都可以在Retrofit的回调中直接操作UI,这个特性是不是很屌!

结果适配

前面提到serviceMethod.callAdapter.adapt(okHttpCall);实际上这个okhttpcall传进去没有进行任何处理。

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

  @Override
  public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    ... 毫不留情的省略一些代码 ...
    return new CallAdapter<Call<?>>() {
      ... 省略一些代码 ...

      @Override public <R> Call<R> adapt(Call<R> call) {
        //看这里,直接把传入的 call 返回了
        return call;
      }
    };
  }
}

如果我想要接入 RxJava,让接口的返回结果改为 Observable,那就需要实现一个adapter队这个call进行处理了。Retrofit 的开发者们早就想到了这个问题,并且为我们提供了相应的 Adapter:RxJavaCallAdapterFactory

public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {

  ... 请叫我省略君,为了省地方,一个都不放过! ...

  @Override
  public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //注意下面的代码主要是判断 returnType 是否为 RxJava 支持的类型
    Class<?> rawType = getRawType(returnType);
    String canonicalName = rawType.getCanonicalName();
    boolean isSingle = "rx.Single".equals(canonicalName);
    boolean isCompletable = "rx.Completable".equals(canonicalName);
    if (rawType != Observable.class && !isSingle && !isCompletable) {
      return null;
    }
    ... 这里省略掉的代码主要是根据返回类型获取合适的 Adapter ...
    return callAdapter;
  }

  ... 我又来了,继续略去一些代码 ...

  static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
    private final Type responseType;
    private final Scheduler scheduler;

    SimpleCallAdapter(Type responseType, Scheduler scheduler) {
      this.responseType = responseType;
      this.scheduler = scheduler;
    }

    @Override public Type responseType() {
      return responseType;
    }

    @Override public <R> Observable<R> adapt(Call<R> call) {
      //在这里创建需作为返回值的 Observable 实例,并持有 call 实例
      //可以想象得到,在 Observable.subscribe 触发时, call.execute 将会被调用
      Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) 
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }
  }

  ... 略去一些代码 ...
}

RxJavaCallAdapterFactory 提供了不止一种 Adapter,但原理大同小异,有兴趣的读者可以自行参阅其源码。

最后还是回到最还是的作为总结:Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。

参考:https://segmentfault.com/a/1190000005638577 https://blog.csdn.net/lyric_315/article/details/52710464