Retrofit源码解析

87 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

前言

记得上次学习Retrofit源码是在四年年前,同时也记录了文章,Retrofit的设计思想很值得我们去学习。前段日子翻看一遍感觉有的地方理解得不妥,表达出来也不科学,所以打算重新写一篇关于Retrofit的源码解读,重在流程和思想,而不在于细节。阅读源码需要有耐心,然后不要去钻牛角尖,因为你往细节进击,将会导致无法掌握整个大局的流程,甚至在陷在某个实现而无法自拔,除非需要学习它在某个方面的具体实现。这也是很多大神推荐的阅读源码方式。

开始

1. Retrofit的构建

Retrofit.Builder()
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl(Constant.PRODUCT_BASE_URL)
        .build()

Retrofit的构建采用了Builder模式,build方法里边会添加默认的adapter的工厂以及callFactory

  public Retrofit build() {
    //校验baseUrl
    if (baseUrl == null) {
      throw new IllegalStateException("Base URL required.");
    }
​
    //1.
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
      callFactory = new OkHttpClient();
    }
​
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
      callbackExecutor = platform.defaultCallbackExecutor();
    }
​
    //2.
    List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
    adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
​
    //3.
 List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
//...
    return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
        callbackExecutor, validateEagerly);
  }
}
  1. 创建默认的callFactory,可以看到是一个OkHttpClient对象
  2. 创建一个Executor,用于创建一个默认的CallAdapter工厂对象,CallAdapter主要作用是将Call转换为对应的方法返回类型,例如RxJava2CallAdapter(转换为RxJava中的Observable),其中Executor为MainThreadExecutor、Factory为ExecutorCallAdapterFactory,后面会讲到。
  3. 获取converterFactories参数,主要用于将服务端饭回数据,按照我们想要的格式进行转换的converter,例如上面我们传入的GsonConverterFactory,它创建的Converter专门用于将数据格式为json的数据转换为对应的类型。

2. 获取定义接口的对象

平时使用Retrofit进行网络请求的时候,一般我们会这样定义:

@POST("User/login")
@FormUrlEncoded
fun loginByPassword(@Field("phone")phone: String,@Field("password")password: String): Call<BaseEntity<LoginResponse>>

然后会使用如下代码来该接口的对象:

apiStores = retrofit.create(
    ApiStores::class.java
)

那么为什么retrofit调用它的create方法就可以去创建接口的实例呢?我们一起看看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() {
        private final Platform platform = Platform.get();
​
        @Override public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
          //如果方法是定义在Object的,直接调用
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          //如果是默认的方法,则不重新处理
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          //1.
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method);
          //2.
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
          //3.
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

通过源码我们可以知道,它用的是动态代理的方式去处理每个service的方法:

  1. 通过method去构建一个ServiceMethod
  2. 创建OkHttpCall
  3. 使用CallAdapter将okHttpCall转换成目标的返回类型

1.以method为参数,通过loadServiceMethod方法去获取一个ServiceMethod方法:

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;
}

首先通过method从缓存中获取对应的ServiceMethod,获取不到则使用同步方式区创建ServiceMethod对象并放入缓存中,这样操作提高了效率。我们来看看ServiceMethod.Builder的构造函数:

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

里面执行的操作时将方法、方法上的注解、参数类型、参数注解进行赋值,以供后面执行的时候使用。 最后是build方法来构建一个ServiceMethod对象,这里贴出它的部分代码:

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];
    //...
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }
//...省略了异常检查
  return new ServiceMethod<>(this);
}

build方法对方法返回类型的适配器进行赋值,也就是将方法返回的Call类型转换成我们想要的类型,还有对服务端返回的数据转换成我们想要类型的Converter。接下来就是对方法上的注解还有方法参数注解进行解析。

ServiceMethod构建完成之后,接下来是以ServiceMethod为参数创建一个OkHttpCall对象,OkHttpCall实现了Call,它用于在发起请求时的一个代理。

最后是通过call的适配器,将OkHttpCall转换成我们的目标方法返回类型。如果最初我们没有指定对应的Adapter工厂类,那么它会原地返回一个ExecutorCallbackCall对象给我们。

到这里接口的对象创建完成了,接下来看看它的发起请求的过程。

3.发起请求

默认我们执行接口方法后,会得到一个Call对象,然后我们可以使用这个Call对象进行发起请求,可以是异步的,也可以是同步的。

异步请求:

我们调用默认返回的Call的enqueue方法即可进行异步网络请求,那么这个Call对象也是我们上面所说的默认为ExecutorCallbackCall:

@Override public void enqueue(final Callback<T> callback) {
  if (callback == null) throw new NullPointerException("callback == null");
​
  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          if (delegate.isCanceled()) {
            //被取消掉了
            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() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

我们可以看到是交给了delegate进行处理,那么这个delegate又是谁呢,查看了ExecutorCallbackCall的构造函数还有结合前面的分析可以得知原来是我们前面讲到在动态代理方法里边创建的的OkHttpCall。我们看看它的enqueue方法:

@Override public void enqueue(final Callback<T> callback) {
  //检查callback
  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 {
        //1.
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        failure = creationFailure = t;
      }
    }
  }
​
  if (failure != null) {
    callback.onFailure(this, failure);
    return;
  }
​
  if (canceled) {
    call.cancel();
  }
​
  //2.
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
        throws IOException {
      Response<T> response;
      try {
        //3.
        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();
      }
    }
  });
}
  1. OkHttpCall的enqueue方法中会创建一个okhttp3.Call对象:
private okhttp3.Call createRawCall() throws IOException {
  Request request = serviceMethod.toRequest(args);
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

首先通过serviceMethod去构建一个request对象,也就是将url、参数、请求方法等构建出一个request,然后再通过callFactory去构建出call,其实callFactory就是我们构造Retrofit时,初始化的OkHttpClient,相信使用过OkHttp的同学对它不陌生。

  1. Call构建完成后,则将网络请求交给了OkHttp去完成。
  2. 解析服务端返回的数据:
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();
//省略对状态码的检查
  ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
  try {
    T body = serviceMethod.toResponse(catchingBody);
    return Response.success(body, rawResponse);
  } catch (RuntimeException e) {
    catchingBody.throwIfCaught();
    throw e;
  }
}

我们主要看看serviceMethod.toResponse方法,将body转换成我们的目标类型:

R toResponse(ResponseBody body) throws IOException {
  return responseConverter.convert(body);
}

该方法通过Converter将ResponseBody转换成我们想要的类型,如果是json数据格式,那么将会以json的数据格式转换成T类型的对象。

解析成功后,我们回到发起请求的地方:它会回调到ExecutorCallbackCall.enqueue方法中,并且执行callbackExecutor.execute进行回调,当我们在Android使用时,callbackExecutor为MainThreadExecutor:

private static class MainThreadExecutor implements Executor {
    final Handler mHandler = new Handler(Looper.getMainLooper());
    MainThreadExecutor() {}
    @Override
    public void execute(@NonNull Runnable command) {
        mHandler.post(command);
    }
}

可以看到当执行execute的时候,会将该任务post到主线程中去执行,也就是异步情况下,当我们执行回调时会在主线程中执行回调。到了这里整个网络异步请求与回调便结束了。

同步操作: 同步操作由execute发起:

@Override public Response<T> execute() throws IOException {
  return delegate.execute();
}

同步的操作首先是执行ExecutorCallbackCall的execute方法,然后交由上面分析的OkHttpCall的execute方法:

@Override public Response<T> execute() throws IOException {
  okhttp3.Call call;
​
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;
​
    //...
​
    call = rawCall;
    if (call == null) {
      try {
        call = rawCall = createRawCall();
      } catch (IOException | RuntimeException e) {
        //...
      }
    }
  }
  //...
  return parseResponse(call.execute());
}

同样是创建一个okhttp3.Call对象,然后调用该对象的execute方法真正发起同步网络请求并返回Responce,最后对返回的Response进行解析并返回。这里的同步操作底层也是交由OkHttp去完成的。

结语

本次的分享涵盖了Retrofit的构建,接口方法的调用与发起请求的流程,里边使用到了Builder模式、适配器模式、代理模式等,很完美地将每个地方进行解耦,极大地提高了灵活性,这些都很值得我们去学习。本次的分享希望大家能够喜欢,同时欢迎讨论与指出不妥之处。