Retrofit框架源码解读

Posted 潇潇凤儿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Retrofit框架源码解读相关的知识,希望对你有一定的参考价值。

之前对Retrofit框架一直怀有敬重之心,别人能把网络请求框封装得如此好用。以前只知道他内部是调用OkHttp进行网络请求的,可它内部是怎么实现的呢?光会用框架那是初级开发者的水平,总不能当一辈子随时可替代的螺丝钉吧,我也要有一点研究源码的心态,要不到时到时被各大面试官鄙视了。为了更有目的的研究源码,先问自己几个问题,如果闭着眼也能答上来,那算熟练掌握了Retrofit框架。

Question:

1. Retrofit进行网络请求的步骤?

2. Retrofit是怎么封装OkHttp的?

3. Retrofit是怎么和Rxjava对接的?

4. Retrofit中为什么能直接调用接口的方法?

 

在进行Retrofit框架前,我们先要明白OkHttp网络请求的步骤。

OKHttp网络请求三步骤:


1)将所需参数构建成Request对象
2)根据Request对象构建Call对象
3)执行call.execute/call.enqueue完成同步/异步请求

OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(url).build();//将所需参数构建成Request对象
Call call = okHttpClient.newCall(request);//据Request对象构建Call对象
call.enqueue(new Callback() //执行call.execute/call.enqueue完成同步/异步请求
    @Override
    public void onFailure(Call call, IOException e) 

    

    @Override
    public void onResponse(Call call, Response response) throws IOException 
        String str = response.body().string();

    
);


从OkHttp的网络请求步骤中我们可以看出,OkHttp主要是借助 Call进行网络请求的,而Retrofit是对OkHttp进行封装的,那它一定对OkHttp的Call进行了封装,我们来对比下Retrofit的Call接口与OkHttp的Call接口。从下图可以看出,Retrofit内部的Call接口和OkHttp的Call最大区别是Retrofit的Call接口是个泛型接口,提供了泛型的Response<T>和泛型的Callback<T>。Retrofit的具体请求实现类是在OkHttpCall类中。

Retrofit的Call接口结构                OkHttp的Call接口结构                          Retrofit的OkHttpCall接口

                                  

1. Retrofit进行网络请求的步骤

我们都知道Retrofit框架发起网络请求主要由以下几个步骤构成:

1) 根据请求地址构建Retrofit.Builder对象

2) 创建OkHttpClient对象,根据OkHttpClient对象和Retrofit.Builder对象构建Retrofit对象

3) 利用Retrofit对象生成接口实例

4) 利用接口实例生成OkHttpCall对象

5) 调用OkHttpCall.execute()/enqueue()完成同步或异步请求

//1) 根据请求地址构建Retrofit.Builder对象
Retrofit.Builder builder = new Retrofit.Builder()
                        .baseUrl(baseUrl)//网络地址基址
                        .addConverterFactory(GsonConverterFactory.create())//请求前后对象转换器
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create());//返回值的类类型适配器
//2) 创建OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
//根据OkHttpClient对象和Retrofit.Builder对象构建Retrofit对象
Retrofit retrofit = builder.client(okHttpClient).build();
//3)利用Retrofit对象生成接口实例
GithubService service = retrofit.create(GithubService.class);
//4) 利用接口实例生成OkHttpCall对象
Call<AccountResponse> call = service.account("id");
//5) 调用OkHttpCall.execute()/enqueue()完成同步或异步请求
AccountResponse response = call.execute();//进行网络请求

2、生成接口实例

首先看上面第三步中构建GithubService实例,它主要采用Java动态代理来生成接口的实例,拦截接口的方法,统一解析方法中的注解信息,减少代码的编写。

2.1、生成接口实例

public <T> T create(final Class<T> service) 
    Utils.validateServiceInterface(service);//验证接口合法性
    if (validateEagerly) 
      eagerlyValidateMethods(service);//验证接口中方法的合法性
    
    //采用java动态代理生成service接口的实例
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]  service ,
        new InvocationHandler() //接口中所有方法都会进来
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable 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);
            
            //一般接口带注解的方法都会进入这里
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          
        );
  

2.2、解析接口中声明方法的注解信息,保存到ServiceMethod对象中
2.2.1 从缓存中取

//Retrofit.loadServiceMethod()方法,利用缓存技术,下次调用方法时不需要重新解析
  ServiceMethod<?> loadServiceMethod(Method method) 
    ServiceMethod<?> result = serviceMethodCache.get(method);//从缓存中获取method方法对应的ServiceMethod对象
    if (result != null) return result;//如果缓存中存在方法的ServiceMethod对象,则直接返回

    synchronized (serviceMethodCache) //用到了单例中的双重校验锁,防止在多线程情况下对方法进行多次解析,保证一个方法只会被解析一次,线程安全
      result = serviceMethodCache.get(method);
      if (result == null) 
        //解析方法中的注解参数
        result = ServiceMethod.parseAnnotations(this, method);
        //将解析的结果放到缓存中,下次调用方法时直接从缓存中取,而不用重新解析注解
        serviceMethodCache.put(method, result);
      
    
    return result;
  

2.2.2 调用ServiceMethod.parseAnnotations()方法解析方法注解信息

//如果缓存中没有method方法的ServiceMethod对象,则调用ServiceMethod的parseAnnotations方法进行注解解析
/**
   * 解析方法的注解信息,封装成一个ServiceMethod对象
   */
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) 
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    //方法不能没有返回值
    if (returnType == void.class) 
      throw methodError(method, "Service methods cannot return void.");
    

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  

ServiceMethod.parseAnnotations(retrofit, method)方法两步走:

(1)调用RequestFactory.parseAnnotations方法解析方法的注解,构建RequestFactory对象

RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//RequestFactory.parseAnnotations()方法,调用了Builder()生成一个RequestFactory对象
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) 
    return new Builder(retrofit, method).build();
  
//RequestFactory.build()方法,在构建RequestFactory实例时,对方法进行了解析
RequestFactory build() 
//1. 解析方法的注解,如@FormUrlEncoded @POST @Multipart等
      for (Annotation annotation : methodAnnotations) //遍历方法的注解信息
        parseMethodAnnotation(annotation);
      

      //...对注解信息进行合法性校验,若不符合规格,抛出相应错误

      int parameterCount = parameterAnnotationsArray.length;//获取方法中注解参数个数
      parameterHandlers = new ParameterHandler<?>[parameterCount];
//2. 解析方法中参数注解,如@Field @Header @Query @Url @Part @Body等
      for (int p = 0; p < parameterCount; p++) 
        parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
      

      //...对注解参数的合法性进行校验,若不符合规格,抛出相应错误

      return new RequestFactory(this);//返回RequestFactory实例
    

(2)调用ServiceMethod实现类HttpService.parseAnnotations方法

//HttpServiceMethod.parseAnnotations()方法
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) 
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);//构建CallAdapter实例
    Type responseType = callAdapter.responseType();//根据callAdapter实例得到接口的返回值类型
//由于Retrofit对返回值进行了封装,所以不应该是okHttp.Response或是其父类的返回类型
    if (responseType == Response.class || responseType == okhttp3.Response.class) 
      throw methodError(method, "'"
          + Utils.getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) 
      throw methodError(method, "HEAD method must use Void as response type.");
    
    //得到返回值转换器Converter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    //返回代表方法的HttpServiceMethod对象,包含callAdapter\\Converter
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  

1)CallAdapter起作用的地方,通过调用Retrofit.callAdapter()方法找到相应的CallAdpater,转换想要的类型

/**
   * 创建CallAdapter,将返回值类型转换成想要的类型
   */
  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method) 
    Type returnType = method.getGenericReturnType();//获取方法中返回值类型(是个泛型)
    Annotation[] annotations = method.getAnnotations();//获取方法的注解
    try 
      //调用Retrofit.callAdapter方法获取到相应的适配器
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
     catch (RuntimeException e)  // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    
  

2)Converter起作用的地方

//HttpServiceMethod.createResponseConverter方法,最终将返回值的F类型转换成T类型
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
      Retrofit retrofit, Method method, Type responseType) 
    Annotation[] annotations = method.getAnnotations();
    try 
      //调用Retrofit.responseBodyConverter方法获取相应的Converter转换器
      return retrofit.responseBodyConverter(responseType, annotations);
     catch (RuntimeException e)  // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create converter for %s", responseType);
    
  

public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) 
    return nextResponseBodyConverter(null, type, annotations);
  

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) 
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) 
      //从converterFactories工厂中找到相应的适配器
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) 
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      
    

  

Retrofit.loadServiceMethod(Method method)会调用ServiceMethod.parseAnnotations(retrofit, method)方法,ServiceMethod是个抽象类,它的实现类是HttpServiceMethod,HttpServiceMethod主要是将接口方法调用转换成Http方法调用。ServiceMethod的parseAnnotations方法又会调用实现类HttpServiceMethod的parseAnnotations(retrofit, method, requestFactory)方法,其中requestFactory是由RequestFactory.parseAnnotations(retrofit, method)构建的对象,主要对方法中的注解进行解析。

 

3、Retrofit的OkHttpCall执行execute方法

1中的第五步,执行Call.execute()方法的真正实现如下:

//OkHttpCall的execute()方法
public Response<T> execute() throws IOException 
    okhttp3.Call call;

    synchronized (this) 
      if (executed) throw new IllegalStateException("Already executed.");//execute()方法只能被执行一次
      executed = true;
      //...省略错误处理代码
      call = rawCall;
      if (call == null) 
        try 
//1.构建okhttp3.Call对象
          call = rawCall = createRawCall();
         catch (IOException | RuntimeException | Error e) 
          //...错误处理
        
      
    

    if (canceled) 
      call.cancel();
    
//分两步:1.调用OkHttp.Call的execute()方法,发起网络请求;
//2.调用parseResponse()方法解析返回结果。将OkHttp.Response转换成Retrofit的Response<T>
    return parseResponse(call.execute());
  

3.1 构建okhttp3.Call对象

前面说过Retrofit是对OkHttp封装的框架,内部的网络请求还是由OkHttp发起的,而OkHttp进行网络请求前两步分别是构建Request对象和Call对象,这里也不例外。

//OkHttpCall的createRawCall()方法 
private okhttp3.Call createRawCall() throws IOException 
//1.requestFactory.create(args)根据请求参数构建okhttp3.Request对象
//2.callFactory.newCall(Request)根据request对象构建okhttp3.Call对象
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) 
      throw new NullPointerException("Call.Factory returned null.");
    
    return call;
  


//第一步:根据接口方法中的参数调用RequestFactory.create()方法构建okhttp3.Request对象
okhttp3.Request create(Object[] args) throws IOException 
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    if (argumentCount != handlers.length) 
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    

    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
        headers, contentType, hasBody, isFormEncoded, isMultipart);//构建RequestBuilder对象

    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) 
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    

    return requestBuilder.get()
        .tag(Invocation.class, new Invocation(method, argumentList))
        .build();//利用Request.Builder的build()方法构建出Request对象
  

//第二步:调用OkHttpClient.newCall(Request)方法构建Call对象,Call对象的实现类是RealCall
@Override public Call newCall(Request request) 
    return RealCall.newRealCall(this, request, false /* for web socket */);
  

//最终调用RealCall.newRealCall()方法来构建okhttp3.Call对象实例
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) 
    // 发布Call实例到EventListener,这样Call能回调EventListener方法
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  

3.2 发起网络请求

Retrofit对Call的实现类是RealCall,所以OkHttpCall中的call.execute()方法最终会调用RealCall.execute()方法。

@Override public Response execute() throws IOException 
    synchronized (this) //Call对象只能被执行一次
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    
    captureCallStackTrace();//对Call对象调用的栈信息进行追踪记录
    timeout.enter();
    eventListener.callStart(this);//回调EventListener.start()方法,开始进行网络请求
    try 
      client.dispatcher().executed(this);//执行网络请求
      Response result = getResponseWithInterceptorChain();//对结果进行各个拦截器链调用
      if (result == null) throw new IOException("Canceled");
      return result;
     catch (IOException e) 
      e = timeoutExit(e);
      eventListener.callFailed(this, e);//回调EventListener.fail方法,请求网络失败
      throw e;
     finally 
      client.dispatcher().finished(this);
    
  

3.3 对请求结果解析,转换成泛型真实对象

OkHttpCall.parseResponse(call.execute())方法,将okHttp3.Respose结果转换成Retrofit.Response

//OkHttpCall.parseResponse(okhttp3.Response)方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException 
    ResponseBody rawBody = rawResponse.body();//获取ResponseBody对象

    //只取ResponseBody需要的部分,移除其他信息
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) //如果返回值不在200~300之间,说明请求失败了,如服务拒绝请求,或是服务器关闭了、url地址错误等
      try 
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
       finally 
        rawBody.close();
      
    

    if (code == 204 || code == 205) 
      rawBody.close();
      return Response.success(null, rawResponse);
    

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try 
      //调用相应的Conveter将okHttp3.Response结果转换成Retrofit.Response结果
      T body = responseConverter.convert(catchingBody);
      //回调success方法
      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框架是对OkHttp网络请求框架的封装,实际网络请求还是调用OkHttp进行网络请求的,它的请求流程可以归纲成下面这张流程图

 

它主要的工作是在以下几方面:

1、网络请求参数用注解方式,简单明晰,且方法的注解解析只需进行一次就被放入缓存,下次直接从缓存中取,并不会对运行效率造成很大影响;

2、添加了CallAdapter,对网络返回的参数进行了泛化,和Rxjava能进行无缝连接,用户也可以自己定义相应的CallAdapter。

3、添加了Converter,对网络请求对象和返回结果进行转换,如返回结果okhttp3.Response转换成Retrofit.Response。框架还实现了Gson和xml解析器。

 

 

 

以上是关于Retrofit框架源码解读的主要内容,如果未能解决你的问题,请参考以下文章

Retrofit2源码解读

深入浅出安卓热门网络框架 OKHttp3 和 Retrofit 原理

logback之Appender源码解读

Android 网络框架之Retrofit2使用详解及从源码中解析原理

Retrofit源码分析以及MVP框架封装使用

带你一步步剖析Retrofit 源码解析:一款基于 OkHttp 实现的网络请求框架