Retrofit2 源码解读

开源库地址:https://github.com/square/retrofit

解读版本:2.1.0

基本概念

Retrofit 是一个针对Java/Android类型安全的Http请求客户端。

基本使用如下:

  1. 首先定义一个接口,抽象方法的返回值必须为Call<XX>

    public interface GitHubService {
      @GET("users/{user}/repos")
       Call<List<Repo>> listRepos(@Path("user") String user);//默认CallAdapter返回值必须为`Call<XX>`。
     }
  2. 通过Retrofit的create创建接口实现
    //初始化Retrofit
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/") //必须以斜杠结尾
    .addConverterFactory(GsonConverterFactory.create())//添加Gson转换工厂
    .build();
    
    //通过Retrofit创建接口实现
    GitHubService service = retrofit.create(GitHubService.class);
  3. 接口调用相关方法获取Call<XX>,然后就能和Okhttp一样进行同步/异步调用。
    Call<List<Repo>> repos = service.listRepos("octocat");
    //异步调用
    repos.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                     List<String> list=response.body();//调用body()
            }
    
            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
    
            }
        });
     //同步调用
      Response<List<Repo>> response=repos.execute();

源码解读

Retrofit中的相关注解

请求方法注解

该类注解用于指明请求方法,在使用时必须指定且只可指定一种请求方法。

  1. @GET

    用来指明请求方法和相对/绝对/完整路径。当然也可以配合@Url使用。

    public interface RequestService {
       @GET("xx/xxx") //指定相对路径
       Call<ResponseBody> getContent1();
    
       @GET("/xxx") //指定绝对路径,相对路径和绝对路径的区别见后文
       Call<ResponseBody> getContent1();
    
       @GET("http://www.baidu.com") //指定完整Url
       Call<ResponseBody> getContent2();
    
       @GET //配合@Url使用,此时@GET只能指明请求方法,路径参数必须留空
       Call<ResponseBody> getContent3(@Url String url);
     }
  2. @POST

    用来指明请求方法和相对/绝对/完整路径,同时也可以配合@Url使用。

    public interface RequestService {
       @POST("xx/xxx") //指定相对路径
       Call<ResponseBody> getContent1();
       @POST("/xxx") //指定绝对路径
       Call<ResponseBody> getContent1();
       @POST("http://www.baidu.com") //指定完整Url
       Call<ResponseBody> getContent2();
    
       @POST //配合@Url使用,此时@POST只能指明请求方法,路径参数必须留空
       Call<ResponseBody> getContent3(@Url String url);
     }
  3. @DELETE,@HEAD,@OPTIONS,@PATCH,@PUT

    这几种请求方法和POST/GET在使用上没有太大区别,由于不太常用,这里就不做介绍。

请求参数注解

  1. @Path

    该注解用于替换Url的路径(path)部分。

    public interface RequestService {
       @GET("/image/{id}") //替换{}内的路径
       Call<ResponseBody> getImage(@Path("id") int id);
    
       @GET("/user/{name}") //替换{}内的路径
       Call<ResponseBody> getUserInfo(@Path("name") String name);
    
       @GET("/user/{name}") //替换{}内的路径,encoded=true则表示自己处理编码,
       Call<ResponseBody> getUserInfo(@Path(value="name", encoded=true) String name);
    
     }

    上述代码有一个是否需要编码的问题,这里举个例子:假如输入名字maple+jaw,默认情况下为GET中的注解为/user/maple%2Bjaw,如果指明自己处理编码,此时输入maple+jaw,路径参数为/user/maple+jaw

  2. @Query

    该注解用于在Url后追加查询参数

    public interface RequestService {
       @GET("/list") //追加查询参数后,此时路径为/list?page=xx
       Call<ResponseBody> getList(@Query("page") int page);
    
       @GET("/list") //追加查询参数后,此时路径为/list?page=xx&category=xx
       Call<ResponseBody> getList(@Query("page") int page,@Query("category") String category);
    
       @GET("/list") //同理也可指定是否编码
       Call<ResponseBody> getList(@Query(value="category",encoded=true) String category);
    
     }
  3. @QueryMap

    @QueryMap和@Query的作用是一样的,只不过@QueryMap是以map的形式追加参数,可以一次性追加多个参数

    public interface RequestService {
       @GET("/search")
       Call<ResponseBody> getList(@QueryMap Map<String, String> filters);
    
     }
  4. @Field

    该注解用于添加表单数据,须配合@FormUrlEncoded一起使用。

    public interface RequestService {
       @FormUrlEncoded
       @POST("/register") //使用@Field提交表单数据
       Call<ResponseBody> register(@Field("name") String name,@Field("password") String pwd,);
    
       @FormUrlEncoded
       @POST("/register") //使用@Field提交表单数据,encoded=true表示该数据自己编码
       Call<ResponseBody> register(@Field(value="name",encoded=true) String name,@Field("password") String pwd,);
    
     }
  5. @FieldMap

    @Field和@FieldMap的作用一样,后者以map的方式提交表单数据

    public interface RequestService {
       @FormUrlEncoded
       @POST("/register") //使用@FieldMap提交表单数据
       Call<ResponseBody> register(@FieldMap Map<String, String> fields);
    
     }
  6. @Header

    该注解用于指明请求头参数,相同名字的请求头是惟一的,新的会直接覆盖旧的。

    public interface RequestService {
       @GET("/")
       Call<ResponseBody> getContent(@Header("Accept-Language") String lang
                                    ,@Header("Cache-Control") String cache);        
    
     }
  7. @Headers

    该注解用于指明请求头参数,和@Header的区别在于不会覆盖掉相同名字的请求参数

    public interface RequestService {
       @Headers("Cache-Control: max-age=640000")
       @GET("/")
       Call<ResponseBody> getContent();   
    
       @Headers({"Cache-Control: max-age=640000","Accept-Language: gzip, deflate"})
       @GET("/")
       Call<ResponseBody> getContent();  
    
     }
  8. @HeaderMap

    @HeaderMap和@Headers的作用一样,区别在于以Map的形式指明请求头参数。

    public interface RequestService {
       @GET("/search")
       Call<ResponseBody> getList(@HeaderMap Map<String, String> headers);       
    
     }
  9. @Part

    该注解用于定义分块表单请求中的块。需配合@Multipart一起使用。

    如果类型是MultipartBody.Part,内容将直接被使用。(@Part MultipartBody.Part part

    如果类型是RequestBody,将会和它的contentType一起使用。(@Part("foo") RequestBody foo

    其他类型将会通过转换器转换后使用。(@Part("foo") Image photo

    @Part(XX),XX表示表单名。

      public interface RequestService {
       @Multipart
       @POST("/")
       Call<ResponseBody> example(@Part("description") String description,
                               @Part(value = "image", encoding = "8-bit") RequestBody image);       
    
     }
  10. @PartMap

    和@Part的作用一样,区别在于以Map的形式定义,其中Map的Value类型转换同@Part。

      public interface RequestService {
       @Multipart
       @POST("/upload")
       Call<ResponseBody> example(@Part("file\"; filename=\"test.txt") RequestBody file,
                               @PartMap Map<String, RequestBody> params);       
    
     }
  11. @Body

    当请求体不是表单/分块类型时,该注解用于直接指定请求体。请求体内部转换时通过Converter转换为RequestBody实现的。

      public interface RequestService {
       @POST("/upload")
       Call<ResponseBody> example(@Body String conent);       
    
     }
  12. @HTTP

    该注解用于自定义请求。

    interface Service {
    @HTTP(method = "DELETE", path = "remove/", hasBody = true)
    Call<ResponseBody> deleteObject(@Body RequestBody object);
    
    @HTTP(method = "CUSTOM", path = "custom/endpoint/")
    Call<ResponseBody> customEndpoint();
    }
  13. @Streaming

    使用该注解后,将不会缓冲流到内存中,只在返回类型为Call<ResponseBody>时起效。如果使用Retrofit下载大文件,务必要加上该注解。

Retrofit构造

按照老规矩,从Retrofit的构造方法下手,要想了解Retrofit的构造方法,必须先了解以下几个类

Call接口

注意,此Call非Okhttp中的Call,结构如下,通过Call可以进行同步和异步请求,还能判断取消与否。

CallAdapter

CallAdapter主要用来代理call(默认为OkhttpCall)以方便实现自己想要的功能,比如RxJavaCallAdapter等。可以通过Retrofit.Builder#addCallAdapterFactory(Factory)来添加CallAdapterFactory。

public interface CallAdapter<T> {

   //返回响应类型,即Call<Repo>的响应类型为Repo
  Type responseType();

  //返回一个代理call的实例。
  <R> T adapt(Call<R> call);
  //..
  //省略了工厂源码,用于获取CallAdapter的实例
 }

Callback

Callback用于回调结果。

public interface Callback<T> {

  //接收HTTP响应(404,500也会调用,所以需要调用isSuccessful来判断)
  void onResponse(Call<T> call, Response<T> response);

  //网络异常或者发生异常时调用
  void onFailure(Call<T> call, Throwable t);
}

Converter

用于类型转换,使用Retrofit.Builder#addConverterFactory(Factory)添加转换工厂,比如添加Gson转换。

public interface Converter<F, T> {
  T convert(F value) throws IOException;
  //..
  //省略了工厂源码
}  

构造方法

Retrofit构造方法采用构建者模式构造,源码如下。

  private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();//key为接口中定义方法,value为转换过后的方法

  public static final class Builder {
    private Platform platform;//平台:安卓、java等
    private okhttp3.Call.Factory callFactory; //okhttp的Call工厂类,自定义newCall将Request转为Call
    private HttpUrl baseUrl;//okhttp中的类,保存解析过的url
    private List<Converter.Factory> converterFactories = new ArrayList<>();//类型转换工厂列表。
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();//CallAdapter工厂列表。
    private Executor callbackExecutor;//回调线程池
    private boolean validateEagerly;//急需验证?作用在于直接将所有方法加入前面的map缓存中。

    Builder(Platform platform) {
      this.platform = platform;
      converterFactories.add(new BuiltInConverters());//添加默认的转换器
    }

    public Builder() {
      this(Platform.get());//通过Platform.get()获取关于当前平台的实现
    }

    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();//默认使用OkHttpClient
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {//默认使用平台默认回调线程池
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      //将平台默认CallAdapter.Factory加入列表中
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

     //将默认Converter.Factory加入列表中
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

可以看出,首选通过Platform.get()来获取平台实现然后添加了默认转换工厂。由于我们是Android平台,毋容置疑,接下来就去看看Andoird类的源码。

  static class Android extends Platform {
    //回调线程池
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();//主线程
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      //ExecutorCallAdapterFactory继承CallAdapter.Factory,内部代理了原来的Call<T>,用于将Callback回调到指定线程中。
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);//采用Hanlder#post回调到主线程
      }
    }
  }

Andoird类的源码主要实现了两个方法,一个是实现了默认的回调线程池,用于在主线程中执行任务,另一个是实现了CallAdapter工厂,通过代理的方式,将执行结果回调到callbackExecutor中去执行。所以,我们只需将callbackExecutor赋值为MainThreadExecutor即可实现主线程间的回调。

BuiltInConverters继承于Converter.Factory,Converter.Factory中有三个方法:

  • public Converter<ResponseBody, ?> responseBodyConverter(xx)用于将ResponseBody转换为指定类型,通常用于对响应结果的类型转换。
  • public Converter<?, RequestBody> requestBodyConverter(xx)用于将指定类型转为RequestBody。一般用于将@Body,@Part,@PartMap转为RequestBody
  • public Converter<?, String> stringConverter用于将指定类型转为String,用于将@Field,@FieldMap,@Path,@Query,@Header等注解的参数类型转为String。

BuiltInConverters源码实现如下:

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
     //根据类型进行选择不同的转换器
    if (type == ResponseBody.class) {//转换类型为ResponseBody
      if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
        return StreamingResponseBodyConverter.INSTANCE;//如果使用了Steaming注解,就不缓冲到内存,直接返回OkHttp的ResponseBody。
      }
      return BufferingResponseBodyConverter.INSTANCE;//缓冲ResponseBody到内存后返回
    }
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;//Void就直接关闭ResponseBody
    }
    return null;
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {//默认只处理RequestBody相关类型
      return RequestBodyConverter.INSTANCE;
    }
    return null;
  }

  @Override public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == String.class) {//默认只处理String类型的转换,说白了就是直接返回
      return StringConverter.INSTANCE;
    }
    return null;
  }
  //..
  //省略了部分源码
  }

BuiltInConverters源码中最重要的在于responseBodyConverter方法,如果转换类型是ResponseBody且有@Streaming注解,那么将不会缓冲到内存。你可能会疑问两者有什么区别?那么我们想象一种情景——下载大文件。如果下载大文件时将ResponseBody缓冲到内存,OOM那是分分钟的事。

Retrofit提供的用于自定义的方法如下:

  • client(OkHttpClient) 用于自定义客户端
  • callFactory(okhttp3.Call.Factory factory)用于自定义Call工厂,重写newCall将Request转为Call。OkHttpClient就是实现了这个接口。
  • addConverterFactory添加类型转换工厂(Gson转换等)
  • addCallAdapterFactory添加CallAdapter代理工厂,用来代理原始的Call(RxJavaCallAdapter等)。
  • callbackExecutor自定义回调线程池,默认为主线程
  • validateEagerly是否继续验证,是就提前将所有方法转为ServiceMethod放入缓存中,而不是调用一个缓存一个
  • baseUrl用于定义基本链接,必须以”/”结尾

假如基本地址为http://example.com/api/,关于baseUrl与注解中路径的拼接问题如下:

注解中的路径 最终Url (baseUrl为http://example.com/api/
foo/bar/ http://example.com/api/foo/bar/
/foo/bar/ http://example.com/foo/bar/
https://github.com/square/retrofit/ https://github.com/square/retrofit/
//github.com/square/retrofit/ http://github.com/square/retrofit/

创建Service接口

介绍完构造方法后,我们再来看看retrofit.create(XX.class);这个方法。

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);//验证接口,接口不能有继承
    if (validateEagerly) {
      eagerlyValidateMethods(service);//将所有方法(跳过默认方法)转为ServiceMethod放入map中
    }
    //使用代理
    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 (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //JAVA8接口可以定义默认方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }

            //直接看这里
            //将普通的Method转为ServiceMethod
            ServiceMethod serviceMethod = loadServiceMethod(method);
            //将ServiceMethod以及传入的参数值传入OkHttpCall。
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //利用callAdapter代理OkHttpCall,默认直接返回。
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

retrofit.create(XX.class);方法中使用了Java代理,核心在于,首先将普通的Method转为ServiceMethod,然后将ServiceMethod以及传入的参数值传入到OkHttpCall中,最终通过代理OkHttpCall来返回代理Call,默认是直接返回。

ServiceMethod的创建

接下来,我们来看看是怎样将Method转为ServiceMethod的,源码在loadServiceMethod方法中:

  ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);//首先从map中取看看是否已经缓存过
      if (result == null) {//否则构造ServiceMethod。
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);//缓存map中
      }
    }
    return result;
  }

loadServiceMethod的方法很简陋,直接传入了Method然后build(),那么这个build()又做了什么?

  public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();//赋值方法注解数组
      this.parameterTypes = method.getGenericParameterTypes();//赋值参数类型数组
      this.parameterAnnotationsArray = method.getParameterAnnotations();//赋值参数注解数组
}

  public ServiceMethod build() {
      callAdapter = createCallAdapter();//创建CallAdapter,用来代理Call
      responseType = callAdapter.responseType();//获取返回类型
      //...
      responseConverter = createResponseConverter();//创建ResponseConverter,用来转换ResponseBody为指定类型

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);//遍历解析方法注解
      }
      //...
      int parameterCount = parameterAnnotationsArray.length;//parameterAnnotationsArray为参数注解数组
      parameterHandlers = new ParameterHandler<?>[parameterCount];//初始化ParameterHandler,用来处理参数相关
      for (int p = 0; p < parameterCount; p++) {//遍历参数注解数组
        Type parameterType = parameterTypes[p];//获取参数类型
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];//获取参数注解数组
         //...
        //通过注解和参数类型,解析并赋值到parameterHandlers中
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

     //...

      return new ServiceMethod<>(this);
    }

createCallAdapter用于创建CallAdapter。最终的源码在Retrofit类中,从源码可以看出,进行遍历源码,如果查询到就返回。

  public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      //遍历Adapter集合,查到就返回
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    //..
    //省略了部分源码
  }

同理,createResponseConverter也是。

  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(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++) {
      //遍历调用查询
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        return (Converter<ResponseBody, T>) converter;
      }
    }

    //..
    //省略了部分源码
  }

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);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } 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 = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        //请求头注解
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);//解析Header
      } else if (annotation instanceof Multipart) {
        //Multipart
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        //FormUrlEncoded
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }

从上面源码可以看出,使用parseHttpMethodAndPath这个方法用于解析请求方法注解和路径参数保存到Set中

    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      int question = value.indexOf(‘?‘);//查询参数开始的符号
      if (question != -1 && question < value.length() - 1) {
       //如果在查询参数中使用了{},则抛出异常。
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }

      //赋值相对链接
      this.relativeUrl = value;
      //解析{}路径参数保存到Set中
      this.relativeUrlParamNames = parsePathParameters(value);
    }

从源码可以发现,不允许在查询参数中使用{}进行占位,否则就会抛出异常,然后将请求方法中的注解值赋值给relativeUrl,通过parsePathParameters将{}路径参数保存到Set中。

通过parseHeaders来解析头部注解。

    private Headers parseHeaders(String[] headers) {
      Headers.Builder builder = new Headers.Builder();
      for (String header : headers) {
        int colon = header.indexOf(‘:‘);
        if (colon == -1 || colon == 0 || colon == header.length() - 1) {
          throw methodError(
              "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
        }
        String headerName = header.substring(0, colon);
        String headerValue = header.substring(colon + 1).trim();
        if ("Content-Type".equalsIgnoreCase(headerName)) {
          MediaType type = MediaType.parse(headerValue);
          if (type == null) {
            throw methodError("Malformed content type: %s", headerValue);
          }
          contentType = type;
        } else {
          builder.add(headerName, headerValue);
        }
      }
      return builder.build();
    }

看完解析方法注解后,现在来看下是如何解析参数相关注解的。源码在parseParameter

    private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
      for (Annotation annotation : annotations) {//遍历参数注解
        ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);//解析参数注解

        if (annotationAction == null) {
          continue;
        }

        if (result != null) {
          //一个参数中只允许使用一个注解,否则抛出异常
          throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
        }

        result = annotationAction;
      }

      if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
      }

      return result;
    }

可以看出,内部调用了parseParameterAnnotation来解析参数注解,此外,还进行了相关判断,虽然参数中可以使用多个注解,但Retrofit强制用户只能使用一个,否则抛出异常。

parseParameterAnnotation中的源码实在太长了,这里就简单介绍下工作流程。首先根据注解来判断来校验使用上有没有错误,比如@Query注解必须在@Path和@Url后使用,使用了@Url注解那么请求方法注解中不允许设置请求路径等等;然后获取相应Converter用于转换类型(String,ResponseBody),最后初始化相应ParameterHandler返回。

ParameterHandler这个抽象类用于处理参数,还内置了不同类型的参数转换类,源码如下:

abstract class ParameterHandler<T> {
abstract void apply(RequestBuilder builder, T value) throws IOException;

//Body注解类型的转换
static final class Body<T> extends ParameterHandler<T> {
    private final Converter<T, RequestBody> converter;

    Body(Converter<T, RequestBody> converter) {
      this.converter = converter;
    }

    @Override void apply(RequestBuilder builder, T value) {
      if (value == null) {
        throw new IllegalArgumentException("Body parameter value must not be null.");
      }
      RequestBody body;
      try {
        body = converter.convert(value);//转换为RequestBody
      } catch (IOException e) {
        throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
      }
      builder.setBody(body);//设置Body
    }
  }
  //..
  //省略了其他注解类型转换代码
}

调用服务接口

以上介绍了retrofit.create(XX.class);用于创建服务方法接口的过程。现在该进行相关调用了。我们知道,创建服务方法会返回一个Call<XX>对象,通过Call<XX>可以进行相关异步同步调用。

默认CallAdapter相关源码实现在OkHttpCall中,源码如下:

  @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;//okhttp中的call对象
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();//创建原始Call,即okhttp中的Call对象
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

    //通过enqueue进行异步调用
    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);
      }
  }

从源码不难看出,首先通过createRawCall()来创建OkHttp中的Call对象,然后通过Call进行异步/同步调用,获取结果后通过parseResponse解析OkHttp中的Response,然后进行相应回调。

createRawCall()用于创建OkHttp中的Call对象,源码如下:

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);//通过 serviceMethod.toRequest进行转换成Request
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);//调用newCall返回Call对象,默认callFactory为OkHttpClient,OkHttpClient也实现了okhttp3.Call.Factory接口。
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

parseResponse用于解析原始Response,源码如下:

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

    //移除响应体
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();//获取状态吗
    //code < 200 || code >= 300
    if (code < 200 || code >= 300) {
      try {
        //将整个rawBody缓冲到内存中回调
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }
   //code == 204 || code == 205
    if (code == 204 || code == 205) {
      //此时没有响应体,只需回调状态
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(catchingBody);//通过`serviceMethod.toResponse`转换为对应对象
      //回调
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

createRawCall()parseResponse的源码中,可以发现,通过 serviceMethod.toRequest进行转换成OkHttp中的Request,通过serviceMethod.toResponse将OkHttp中的Response转换为对应对象。

toRequest相关源码如下:

  Request toRequest(Object... args) throws IOException {
    // RequestBuilder
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    //ParameterHandler数组
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    //...

    for (int p = 0; p < argumentCount; p++) {
      //遍历数组进行循环应用
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();//通过build()返回Request对象
  }

toResponse源码非常简单,如下:

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

默认情况下,会调用BuiltInConverters中的转换器来转换,当然,更多时候我们回去应用Gson转换器来进行转换。

最后

  • GsonConverterFactory是怎么工作的?

    一般情况下,我们会加入addConverterFactory(GsonConverterFactory.create())来增加Gson转换功能。

    public final class GsonConverterFactory extends Converter.Factory {
    //默认Gson
    public static GsonConverterFactory create() {
    return create(new Gson());
    }
    //自己配置Gson
    public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
    }
    
    private final Gson gson;
    
    private GsonConverterFactory(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
    }
    
    //转换ResponseBody
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));//根据Type获取TypeAdapter
    return new GsonResponseBodyConverter<>(gson, adapter);//转换逻辑代码
    }
    
    //转换成RequestBody
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));//根据Type获取TypeAdapter
    return new GsonRequestBodyConverter<>(gson, adapter);//转换逻辑代码
    }

    ResponseBody核心转换代码:

    @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());//创建JsonReader
    try {
      return adapter.read(jsonReader);//通过adapter转换
    } finally {
      value.close();//关闭流
    }
    }

    RequestBody核心转换代码:

    @Override public RequestBody convert(T value) throws IOException {
    Buffer buffer = new Buffer();
    Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
    JsonWriter jsonWriter = gson.newJsonWriter(writer);//获取JsonWriter
    adapter.write(jsonWriter, value);//adapter将value转换为Gson
    jsonWriter.close();
    return Re
  • RxJavaCallAdapterFactory是怎么工作的?

    我们还可以加入addCallAdapterFactory(RxJavaCallAdapterFactory.create())使你的代码可以进行响应式编程。

    //直接创建
    public static RxJavaCallAdapterFactory create() {
    return new RxJavaCallAdapterFactory(null);
    }
    
    //指定工作线程创建
    public static RxJavaCallAdapterFactory createWithScheduler(Scheduler scheduler) {
    if (scheduler == null) throw new NullPointerException("scheduler == null");
    return new RxJavaCallAdapterFactory(scheduler);
    }
    
    @Override
    public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //..
    //省略了部分源码,
    //通过getCallAdapter来获取Adapter
     CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
    
     return callAdapter;
    }
    
    //获取CallAdapter<Observable<?>>
    private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {
    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
     Class<?> rawObservableType = getRawType(observableType);
      //..
     //省略了部分源码,
     //一般会走SimpleCallAdapter
     return new SimpleCallAdapter(observableType, scheduler);
    }
    
    //SimpleCallAdapter的源码如下
    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.create创建被观察者
      Observable<R> observable = Observable.create(new CallOnSubscribe<>(call))
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);//切换到指定线程
      }
      return observable;
    }
    }

    CallOnSubscribe中使用了RequestArbiter中包装了Call<T>对象,然后通过call.execute()同步执行,最后将结果subscriber.onNext(response)回调出去。

     static final class RequestArbiter<T> extends AtomicBoolean implements Subscription, Producer {
    private final Call<T> call;//原始的Call
    private final Subscriber<? super Response<T>> subscriber;
    
    RequestArbiter(Call<T> call, Subscriber<? super Response<T>> subscriber) {
      this.call = call;
      this.subscriber = subscriber;
    }
    
    @Override public void request(long n) {
      //...
      try {
        Response<T> response = call.execute();//call.execute()同步执行
        if (!subscriber.isUnsubscribed()) {
          subscriber.onNext(response);//onNext回调
        }
      } catch (Throwable t) {
        Exceptions.throwIfFatal(t);
        if (!subscriber.isUnsubscribed()) {
          subscriber.onError(t);
        }
        return;
      }
    
      if (!subscriber.isUnsubscribed()) {
        subscriber.onCompleted();
      }
    }
    
    @Override public void unsubscribe() {
      call.cancel();
    }
    
    @Override public boolean isUnsubscribed() {
      return call.isCanceled();
    }
    }
    

本篇解读到此结束。

时间: 2024-10-27 11:07:47

Retrofit2 源码解读的相关文章

Retrofit2源码解读

综述 Retrofit2的用法在Retrofit2.0使用详解这篇文章中已经详细介绍过了.那么在这就来看一下Retrofit2它是如何实现的.Retrofit2中它的内部网络请求是依赖于OKHttp,所以Retrofit2可以看做是对OKHttp的一次封装,那么下面就开看下Retrofit2是如何对OKHttp进行封装的. 回顾Retrofit2的使用 在这里首先来回顾一下Retrofit2的使用.对于Retrofit2的使用可以分为三步. 首先,我们创建一个Java接口GitHubServic

QCustomplot使用分享(二) 源码解读

一.头文件概述 从这篇文章开始,我们将正式的进入到QCustomPlot的实践学习中来,首先我们先来学习下QCustomPlot的类图,如果下载了QCustomPlot源码的同学可以自己去QCustomPlot的目录下documentation/qcustomplot下寻找一个名字叫做index.html的文件,将其在浏览器中打开,也是可以找到这个库的类图.如图1所示,是组成一个QCustomPlot类图的可能组成形式. 一个图表(QCustomPlot):包含一个或者多个图层.一个或多个ite

vue源码解读预热-0

vueJS的源码解读 vue源码总共包含约一万行代码量(包括注释)特别感谢作者Evan You开放的源代码,访问地址为Github 代码整体介绍与函数介绍预览 代码模块分析 代码整体思路 总体的分析 从图片中可以看出的为采用IIFE(Immediately-Invoked Function Expression)立即执行的函数表达式的形式进行的代码的编写 常见的几种插件方式: (function(,){}(,))或(function(,){})(,)或!function(){}()等等,其中必有

SpringMVC源码解读 - RequestMapping注解实现解读 - RequestCondition体系

一般我们开发时,使用最多的还是@RequestMapping注解方式. @RequestMapping(value = "/", param = "role=guest", consumes = "!application/json") public void myHtmlService() { // ... } 台前的是RequestMapping ,正经干活的却是RequestCondition,根据配置的不同条件匹配request. @Re

jdk1.8.0_45源码解读——HashMap的实现

jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对的映射,允许多个null值和一个null键.但此类不保证映射的顺序,特别是它不保证该顺序恒久不变.  除了HashMap是非同步以及允许使用null外,HashMap 类与 Hashtable大致相同. 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能.迭代col

15、Spark Streaming源码解读之No Receivers彻底思考

在前几期文章里讲了带Receiver的Spark Streaming 应用的相关源码解读,但是现在开发Spark Streaming的应用越来越多的采用No Receivers(Direct Approach)的方式,No Receiver的方式的优势: 1. 更强的控制自由度 2. 语义一致性 其实No Receivers的方式更符合我们读取数据,操作数据的思路的.因为Spark 本身是一个计算框架,他底层会有数据来源,如果没有Receivers,我们直接操作数据来源,这其实是一种更自然的方式

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现 一. Set架构 如上图: (01) Set 是继承于Collection的接口.它是一个不允许有重复元素的集合.(02) AbstractSet 是一个抽象类,它继承于AbstractCollection.AbstractCollection实现了Set中的绝大部分函数,为Set的实现类提供了便利.(03) HastSet 和 TreeSet 是Set的两个实现类.        HashSet依赖于HashMa

线程本地变量ThreadLocal源码解读

  一.ThreadLocal基础知识   原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步.但是Spring中的各种模板类并未采用线程同步机制,因为线程同步会影响并发性和系统性能,而且实现难度也不小. ThreadLocal在Spring中发挥着重要的作用.在管理request作用域的bean,事务管理,任务调度,AOP等模块中都出现了它的身影. ThreadLocal介绍: 它不是一个线程,而是线程的一个本地化

hadoop源码解读namenode高可靠:HA;web方式查看namenode下信息;dfs/data决定datanode存储位置

点击browserFilesystem,和命令查看结果一样 当我们查看hadoop源码时,我们看到hdfs下的hdfs-default.xml文件信息 我们查找${hadoop.tmp.dir}这是引用变量,肯定在其他文件有定义,在core-default.xml中查看到,这两个配置文件有个共同点: 就是不要修改此文件,但可以复制信息到core-site.xml和hdfs-site.xml中修改 usr/local/hadoop 是我存放hadoop文件夹的地方 几个关于namenode的重要文