美文网首页
Retrofit 源码简单分析

Retrofit 源码简单分析

作者: jh352160 | 来源:发表于2017-11-22 09:14 被阅读73次

    众所周知,在现在的Android开发中,针对与网络请求,Retrofit+okHttp的组合绝对是不二之选,而在网上针对于Retrofit分析的文章也有很多了,这次我也分享一些阅读Retrofit源码的心得,希望能够对大家有所帮助。由于我在工作中使用的版本为2.1,所以本次也是针对2.1版本进行分析,首先来看看Retrofit一种简单的用法:

    Retrofit简单使用示例

    首先创建出Retrofit对象,进行相应的初始化配置:

        retrofit=new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(getClient())
            .build();
    

    然后在Service接口中写入相应方法,并加上相应的注解:

        public interface Api {
            @GET(apiUrl)
            Call<Response> methodName();
        }
    

    最后传入请求的回调方法Callback就完成了:

        retrofit.create(Api.class).methodName().enqueue(Callback);
    

    对于Retrofit对象的Build中,主要的都是对于参数的初始化,所以本次就从Retrofit类中的create(final Class<T> service)入手

    Retrofit.create

    public <T> T create(final Class<T> service) {
        //api类必须为接口,且不能实现或继承其他接口,否则在动态代理步骤将会抛出异常
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
            //立即开始遍历调用service中的所有方法
            eagerlyValidateMethods(service);
        }
        //返回service类的动态代理对象,当调用service中的方法时,就会进入其中进行处理
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              //获取当前平台(Android或Java8)
              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.
                // 如果该方法为Class类中的方法,则直接执行
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                //判断给方法是否为default method,针对Android平台,该值永远为false,所以不应在Service类中加入default方法,经尝试可能会导致崩溃
                //关于default method可以参考 http://ebnbin.com/2015/12/20/java-8-default-methods/
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                //1.获取调用方法的ServiceMethod对象
                ServiceMethod serviceMethod = loadServiceMethod(method);
                //2.创建OkHttpCall对象
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                //3.将上一步生成的OkHttpCall对象转换为在service中所配置的Call对象
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
     }
    

    在这段代码的最后所返回的Call对象就是用来使用的Call对象了。

    其中有一个很有趣的地方,在 Platform 类中有一个通过判断特定的类是否存在的方式获取当前平台的步骤,在我的2.1版本中有这么一段代码:

    try {
        Class.forName("org.robovm.apple.foundation.NSObject");
        return new IOS();
    } catch (ClassNotFoundException ignored) {}
    

    但在最新的2.3版本中这段代码已被移除,这说明Retrofit在之前版本中有对IOS平台的支持,但是我在网上却没有找到相关的资料,不知道有没有哪位朋友能为我解答一下。

    在上面的代码中,重点是最后的三行代码,下面就分别对于这三行代码进分析:

    1.ServiceMethod serviceMethod = loadServiceMethod(method);

    Retrofit.loadServiceMethod

    下面来通过loadServiceMethod方法看看ServiceMethod对象是如何创建出来的

      ServiceMethod loadServiceMethod(Method method) {
        ServiceMethod result;
        synchronized (serviceMethodCache) {
          //该方法的ServiceMethod对象是否已存在缓存中,如果是,则直接使用缓存中的对象
          result = serviceMethodCache.get(method);
          if (result == null) {
            //创建ServiceMethod对象并将其放入缓存
            result = new ServiceMethod.Builder(this, method).build();
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }
    

    在代码中可以看出创建ServiceMethod对象主要都在ServiceMethod.Builder之中

    在其中我发现了一点,在我所使用的2.1版本中,作为缓存使用的serviceMethodCache为LinkedHashMap,但在最新的2.3版本中已被换成了线程安全的ConcurrentHashMap。

    ServiceMethod.Builder.Builder

    首先看看Build中的构造方法:

        public Builder(Retrofit retrofit, Method method) {
          this.retrofit = retrofit;  //retrofit对象
          this.method = method;  //所调用的方法
          this.methodAnnotations = method.getAnnotations();  //方法上的注解
          this.parameterTypes = method.getGenericParameterTypes();  //方法中参数的类型
          this.parameterAnnotationsArray = method.getParameterAnnotations();  //方法中参数上的注解
        }
    

    可以看出,构造方法中的代码很简单,就是针对各个变量的赋值操作。下面就是ServiceMethod中的重头戏,也就是build方法,对于注解的解析也都是在这里完成的,这一部分的代码虽多,但是大部分都是对于注解的解析与异常的判断,还是一样先来看看代码:

    ServiceMethod.Builder.build

        public ServiceMethod build() {
          //代码中将略去其中的合法性检测部分
        
          //通过retrofit中的CallAdapterFactory生成CallAdapter,用以生成网络请求所需的执行器
          callAdapter = createCallAdapter();
          //数据数据转换工厂,例如常用的GsonConverterFactory
          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);
          }
    
          //返回ServiceMethod对象
          return new ServiceMethod<>(this);
        }
    

    其中最重要的应该是属于对于方法与参数的注解解析的部分了,这也是Retrofit的特色之一,先来看看对于方法部分的注解解析:

    ServiceMethod.parseMethodAnnotation

        private void parseMethodAnnotation(Annotation annotation) {
          //除去异常判断后的代码
          if (annotation instanceof DELETE) {
            parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
          } else if (annotation instanceof GET) {
            parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
          } else if (annotation instanceof HEAD) {
            parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
          } else if (annotation instanceof PATCH) {
            parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
          } else if (annotation instanceof POST) {
            parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
          } else if (annotation instanceof PUT) {
            parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
          } else if (annotation instanceof OPTIONS) {
            parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
          } else if (annotation instanceof HTTP) {
            HTTP http = (HTTP) annotation;
            parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
          } else if (annotation instanceof retrofit2.http.Headers) {
            String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
            headers = parseHeaders(headersToParse);
          } else if (annotation instanceof Multipart) {
            isMultipart = true;
          } else if (annotation instanceof FormUrlEncoded) {
            isFormEncoded = true;
          }
        }
    

    可以看出来,这个方法中的逻辑很简单,主要是判断出注解的类型,并将对应的字符串传入之后的处理中,真正的处理还是在parseHttpMethodAndPath与parseHeaders两个方法中

    ServiceMethod.parseHttpMethodAndPath

        private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
          //除去异常判断后的代码
          
          //方法的类型(如GET,POST,PUT等)
          this.httpMethod = httpMethod;
          //是否含有Body
          this.hasBody = hasBody;
    
          //判断相对路径是否为空
          if (value.isEmpty()) {
            return;
          }
    
          //将相对路径
          this.relativeUrl = value;
          //解析相对路径中的参数
          this.relativeUrlParamNames = parsePathParameters(value);
        }
    

    在除去其中异常判断后,这个方法的逻辑就变得非常清晰了

    ServiceMethod.parsePathParameters

      static Set<String> parsePathParameters(String path) {
        // PARAM_URL_REGEX = \{([a-zA-Z][a-zA-Z0-9_-]*)\}
        Matcher m = PARAM_URL_REGEX.matcher(path);
        Set<String> patterns = new LinkedHashSet<>();
        while (m.find()) {
          patterns.add(m.group(1));
        }
        return patterns;
      }
    

    这个方法也很好理解,使用正则表达式匹配出相对路径中以"{}"包裹的变量,然后放入Set集合中

    ServiceMethod.parseHeaders

    该方法的作用为Header注解的解析:

        private Headers parseHeaders(String[] headers) {
          Headers.Builder builder = new Headers.Builder();
          for (String header : headers) {
            //解析出header中的key和value
            int colon = header.indexOf(':');
            String headerName = header.substring(0, colon);
            String headerValue = header.substring(colon + 1).trim();
            //针对Content-type的头,将其存入contentType变量中
            if ("Content-Type".equalsIgnoreCase(headerName)) {
              MediaType type = MediaType.parse(headerValue);
              contentType = type;
            } else {
              builder.add(headerName, headerValue);
            }
          }
          //创建Headers对象
          return builder.build();
        }
    

    ServiceMethod.parseParameterAnnotation

    这个方法用于参数注解的解析

        private ParameterHandler<?> parseParameterAnnotation(
            int p, Type type, Annotation[] annotations, Annotation annotation) {
          if (annotation instanceof Url) {
            //do something
          } else if (annotation instanceof Path) {
            //do something
          } else if (annotation instanceof Query) {
            //do something
          } else if (annotation instanceof QueryMap) {
           //do something
          } else if (annotation instanceof Header) {
            //do something
          } else if (annotation instanceof HeaderMap) {
            //do something
          } else if (annotation instanceof Field) {
            //do something
          } else if (annotation instanceof FieldMap) {
            //do something
          } else if (annotation instanceof Part) {
            //do something
          } else if (annotation instanceof PartMap) {
            gotPart = true;
            //do something
          } else if (annotation instanceof Body) {
            //do something
          }
    
          return null; // Not a Retrofit annotation.
        }
    

    parseParameterAnnotation这个方法中的代码虽然多,但是在简化之后可以看出来,就是针对不同的参数类型进行不同的处理然后返回相应的ParameterHandler对象,因为其中针对各个类型参数的处理比较繁琐,就不一一列举了。

    需要提到的是,在这个方法中,有一个类的出现频率非常的高,这就是ParameterHandler类,这个类的主要作用是将注解中的各项参数通过RequestBuilder类转换为okHttp中使用的Request。

    2.OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

    在OkHttpCall类中,最主要的就是execute与enqueue两个方法,分别为同步与异步的网络请求,但是需要注意的是,这两个方法并不是我们在使用中所调用的方法,真正提供给外界调用的方法,会在文章之后的部分讲到。

    OkHttpCall.execute

      @Override public Response<T> execute() throws IOException {
        //okHttp中的call对象,由于本次分析限于Retrofit中,所以不做深究
        okhttp3.Call call;
    
        synchronized (this) {
          //一个OkhttpCall对象只能执行一次网络请求
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;
    
          //省略部分异常判断代码
    
          call = rawCall;
          if (call == null) {
            try {
              //如果okHttp对象未创建,则在创建后赋值给变量call
              call = rawCall = createRawCall();
            } catch (IOException | RuntimeException e) {
              creationFailure = e;
              throw e;
            }
          }
        }
    
        //防止当调用过cancel方法后新创建的Call未被cancel的情况
        if (canceled) {
          call.cancel();
        }
    
        //使用okhttp开始执行网络请求
        return parseResponse(call.execute());
      }
    

    OkHttpCall.enqueue

      @Override public void enqueue(final Callback<T> callback) {
        if (callback == null) throw new NullPointerException("callback == null");
    
        //okHttp中的call对象,由于本次分析限于Retrofit中,所以不做深究
        okhttp3.Call call;
        Throwable failure;
    
        //一个OkhttpCall对象只能执行一次网络请求
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;
    
          call = rawCall;
          failure = creationFailure;
          if (call == null && failure == null) {
            try {
              //如果okHttp对象未创建,则在创建后赋值给变量call
              call = rawCall = createRawCall();
            } catch (Throwable t) {
              failure = creationFailure = t;
            }
          }
        }
    
        //创建okhttp对象失败时,直接进行Fail的回调方法
        if (failure != null) {
          callback.onFailure(this, failure);
          return;
        }
    
        //防止当调用过cancel方法后新创建的Call未被cancel的情况
        if (canceled) {
          call.cancel();
        }
    
        //使用okhttp开始执行网络请求,并针对返回结果调用Callback中相应的回调方法
        call.enqueue(new okhttp3.Callback() {
          @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
              throws IOException {
            Response<T> response;
            try {
              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();
            }
          }
        });
      }
    

    enqueue方法在主要的逻辑上,与上面的execute是基本相同的,但是有一点需要注意,就是当收到Response时并不会马上进行Callback中的onResponse回调,而是会调用parseResponse方法对返回的Response进行一次解析,如果解析中出现了异常,依然会进行onFailure的回调方法。

    OkHttpCall.parseResponse

      Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        // 取出Response中的Body
        ResponseBody rawBody = rawResponse.body();
    
        // 这个变量就是最后获得的Response中的rawResponse,这实际上是一个在请求中返回的,去除了body部分的okhttp.Response原始对象
        // 移除okhttp3.Response中的Source部分并重建一个okhttp.Response对象
        // 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();
    
        int code = rawResponse.code();
        //如果code不在200~299的范围内,则代表请求异常
        if (code < 200 || code >= 300) {
          try {
            // Buffer the entire body to avoid future I/O.
            ResponseBody bufferedBody = Utils.buffer(rawBody);
            return Response.error(bufferedBody, rawResponse);
          } finally {
            rawBody.close();
          }
        }
    
        // 204 NO CONTENT 与 205 RESET CONTENT 两个code是不含有body的
        // 具体可以查阅https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204与
        //           https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/205
        if (code == 204 || code == 205) {
          return Response.success(null, rawResponse);
        }
    
        // ExceptionCatchingRequestBody是为了捕获在解析rawBody中的内容的buffer时抛出的异常
        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
          // 通过Converter(如:GsonResponseBodyConverter)将Response中的流解析为所需的返回类型
          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;
        }
      }
    

    这个方法主要是对okhttp中返回的response进行一次解析,对其进行一些预处理,并将其转换为更易于使用的Response对象

    OkHttpCall.createRawCall

      private okhttp3.Call createRawCall() throws IOException {
        //获取ServiceMethod生成的Request对象
        Request request = serviceMethod.toRequest(args);
        //通过callFactory将Request转换为Okhttp3.Call
        okhttp3.Call call = serviceMethod.callFactory.newCall(request);
        if (call == null) {
          throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
      }
    

    这个方法主要用于创建okHttp中的Call对象,逻辑非常简单,就不多做分析了

    3.serviceMethod.callAdapter.adapt(okHttpCall)

    这个方法实际是使用在创建Retrofit对象时传入的CallAdapterFactory将OkHttpCall对象转换为最后使用所需的对象,Retrofit中会默认配置一个ExecutorCallAdapterFactory,除此之外还有常用的RxJavaCallAdapterFactory与一个最简单的DefaultCallAdapterFactory。我们抽取出其中的adapt方法来看一看:

    DefaultCallAdapterFactory

    final class DefaultCallAdapterFactory extends CallAdapter.Factory {
      //DefaultCallAdapterFactory在返回时为一个单例对象
      static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
    
      //构造方法
      @Override
      public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
          return null;
        }
    
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
          //获取返回值的类型
          @Override public Type responseType() {
            return responseType;
          }
    
          //将传入的Call对象转换为最后处理所需要的对象返回,在DefaultCallAdapterFactory中为将传入其中的对象直接返回
          @Override public <R> Call<R> adapt(Call<R> call) {
            return call;
          }
        };
      }
    }
    

    这个是Retrofit中最为基本的CallAdapter,也只有在平台为Java8或者无法判断平台时才会使用,也由于这个类的逻辑非常简单,也可以通过这个类了解到CallAdapter.Factory最核心的两个方法的作用

    ExecutorCallAdapterFactory

    ExecutorCallAdapterFactory类为Android平台的默认CallAdapter:

    final class ExecutorCallAdapterFactory extends CallAdapter.Factory { 
      final Executor callbackExecutor;
      
      //构造方法
      ExecutorCallAdapterFactory(Executor callbackExecutor) {
        this.callbackExecutor = callbackExecutor;
      }
    
      //获取CallAdapter对象
      @Override
      public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
          return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
          //获取返回参数的类型
          @Override public Type responseType() {
            return responseType;
          }
    
          //将传入的Call对象转换为最后处理所需要的对象返回,在这里的返回类型为内部类ExecutorCallbackCall
          @Override public <R> Call<R> adapt(Call<R> call) {
            return new ExecutorCallbackCall<>(callbackExecutor, call);
          }
        };
      }
    
      //这就是在ExecutorCallAdapterFactory中返回给外界去使用的对象,也就是在文章开头调用了相应的api方法后所返回的对象
      static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;
    
        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
          this.callbackExecutor = callbackExecutor;
          this.delegate = delegate;
        }
    
        //在文章开头的使用示例中所传入的CallBack实际就是在这里调用相应的回调方法
        @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来源于针对当前平台的对象Platform中的defaultCallbackExecutor方法
              //在Android平台中,该方法的返回语句为 return new MainThreadExecutor(); 
              //MainThreadExecutor的实现为通过handler将Runnable对象发送至主线程执行
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  if (delegate.isCanceled()) {
                    // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                    callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                  } else {
                    callback.onResponse(ExecutorCallbackCall.this, response);
                  }
                }
              });
            }
    
            @Override public void onFailure(Call<T> call, final Throwable t) {
              //此处callbackExecutor作用同上
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  callback.onFailure(ExecutorCallbackCall.this, t);
                }
              });
            }
          });
        }
        
        //do something
      }
    

    结语

    那么,到现在为止,我们简单梳理了Retrofit中的Call的创建流程与ServiceMethod类中的主要逻辑,而这些,也只是Retrofi中的一部分而已,而我也是第一次写这样的文章,加上我的技术还不到位,所以肯定会有很多的不足,欢迎大家来批评指正。

    相关文章

      网友评论

          本文标题:Retrofit 源码简单分析

          本文链接:https://www.haomeiwen.com/subject/evfnvxtx.html