Android学习笔记——Retrofit2源码浅分析

标签: Android

先看看这张图


制图简陋请多包涵

Retrofit

在学习Android的时候,我最熟悉的网络请求框架莫过于OKHttp + Retrofit,反反复复用了很多次,个人感觉Retrofit的兼容和解耦做的太好了,这里想要试着分析一下Retrofit的源码。

RESTful 原则

在我们使用Retrofit这个框架的时候,我们都需要后端的接口遵循RESTful原则,那什么是RESTful原则呢?
RESTful是Representational State Transfer的缩写,翻译过来就是“表现层状态转化”,他是基于HTTP协议的。

  1. 表现层:我们访问服务器是为了“增、删、改、查”服务器上面的资源(数据),资源的对应的是一段文本(text)、一张图片、一首歌曲或者一段音频等,那么这里我们就说这些资源的实体就是表现层
  2. 状态转化:当我们通过用“增、删、改、查”的方式访问服务器的时候,服务器的资源实体可能会发生变化,对应的状态也会相映的变化,这就是状态转化
  3. 客户端怎么进行“增、删、改、查”? 通过HTTP协议,具体就是用HTTP提供的GET、POST、DELETE、PUT的方式

Retrofit分层

我们导入Retrofit的时候,发现我们导入的包分为了三个部分:包装OKHttp的Retrofit部分,兼容RxJava的部分和兼容Gson的部分。

一、包装OKHttp

我们来看看这个部分的目录:


image.png
  1. 首先这里有一个子目录http,进去看我们就可以发现里面定义了若干的关于http协议的注解(包括GET、POST、PUT和DELETE等)。
  2. 接下来就是Retrofit以及它包装的网络请求执行器,请求的构建器,响应、转化器、线程池和工具等等(以上没按照顺序)

二、兼容Gson

我们来看看这个部分的目录:


image.png

这个部分很简单,只包含了三个部分:Gson转换器的工厂、Gson请求体的转换器和Gson响应体的转换器。

三、兼容RxJava

我们再来看看这个部分的目录:


image.png

这个部分我们可以看到很多继承自RxJava的Observable对象,都由RxJava2CallAdapter封装,最后由它的工厂类RxJava2CallAdapterFactory创建出来。

Retrofit 流程源码分析

一般情况下我们使用Retrofit的代码如下:

    mApiService = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .client(getClient())
                .build()
                .create(ApiService.class);

先是通过Retrofit的构建器构建一个Retrofit对象,前面的几个链式调用都是为了支持其他三方库的功能,我们直接去看Retrofit的构建器的build()方法。

Retrofit.Builder.java

    //成员
    //当前的平台
    private final Platform platform;
    //网络请求执行器的工厂
    private @Nullable okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    //可能会用到的转换器工厂的集合
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    //可能会用到的适配器工厂的集合
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    //可能会用到的线程池,默认为null
    private @Nullable Executor callbackExecutor;
    //是否在调用create()方法时,验证传入的接口中的所有方法
    private boolean validateEagerly;
    
    .......
    
    
 public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 制作适配器的防御副本并添加默认的呼叫适配器.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // 制作转换器的防御副本.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

我们可以看见,这里和简单的构建者模式没什么区别,初始化需要用到的成员变量,然后再直接通过Retrofit的构造函数创建一个实例对象,接下来我们去看看create()方法。

public <T> T create(final Class<T> service) {
    //验证服务接口,服务接口必须是一个interface对象,且它没有任何父接口
    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, @Nullable Object[] args)
              throws Throwable {
            // 如果是Object的方法,不做任何处理
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //这个条件判断在Retrofit3.2.0时一直为false,其他版本不知道
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //这里通过服务接口的方法加载了一个ServiceMethod对象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

在create()方法中,首先验证了我们传入的服务接口类,然后通过动态代理拿到方法和参数,通过拿到的方法加载ServiceMethod对象,那么ServiceMethod是干什么的呢?注释是这么说的:将接口的方法调整为Http调用。我们先保留疑问,去看一下loadServiceMethod()方法做了什么:

 ServiceMethod<?, ?> loadServiceMethod(Method method) {
    //通过缓存拿到
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        // 没有缓存就通过构建器构建一个新的ServiceMethod
        result = new ServiceMethod.Builder<>(this, method).build();
        //缓存
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

这个方法的干的事情就是,判断是否有缓存,有就直接返回,没有就通过构建器构建一个新的ServiceMethod对象,这里的缓存是通过CurrentHashMap,键为方法,值为ServiceMethod对象。这里模拟第一次调用,没有缓存,所以我们器看看ServiceMethod的构建器:

   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();
      //通过响应类型
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      //拿到转换器
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }
      return new ServiceMethod<>(this);
    }
  1. 这里先拿到适配器,判断适配器的响应类型是否合法
  2. 然后再拿到转换器
  3. 循环解析——服务接口中被调用的方法上的注解
  4. 检查各项数据,new出ServiceMethod实例

下面我们依次分析这几步:
第一步: 拿到适配器

private CallAdapter<T, R> createCallAdapter() {
      //获取通用的返回类型
      Type returnType = method.getGenericReturnType();
      //返回类型通配符或者变量类型
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      //返回类型不能是无返回类型
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      //拿到传入该方法的的注解
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

这里先是判断了服务接口中,被用户调用的方法的返回类型是否合法,合法之后拿到该方法的注解,转入Retrofit中调用callAdapter()方法,下面我们去看看这个方法:

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

这里直接就调用了nextCallAdapter()方法,我们继续跟进:

 public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    // 判断参数是否为空
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    //找到与传入参数值相等的变量在List中的索引
    int start = adapterFactories.indexOf(skipPast) + 1;
    //循环遍历找到需要的适配器
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    ......
  }

这里进行了参数的非空判断,然后循环遍历List,我们看向第十行这里调用了CallAdapter.Factory的实现类(应该是RxJava2CallAdapterFactory)的get()方法,这个方法会判断调用的服务接口里面的方法的返回类型,是否为Completable、Flowable、Maybe,Single、Obervable,之后会直接new出来需要的适配器(Adapter)。

我们在去看看第二步:拿到转换器

  private Converter<ResponseBody, T> createResponseConverter() {
      //获取方法的注解
      Annotation[] annotations = method.getAnnotations();
      try {
      //转到Retrofit中去拿到转换器
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

我们跟到Retrofit中去看看responseBodyConverter()方法怎么拿到转换器的。

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

这里和拿到适配器的流程很像,也调用了一个nextxxx()方法。

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    //检查参数是否为空
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    //获取参数的值获取在List对应的索引
    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) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }
    ......
  }

这里可以看见,拿到转换器的过程和拿到适配器的过程十分相似,重点还是第12行的responseBodyConverter()。根据我们平时的用法,这个方法直接new出来了一个GsonResponseBodyConverter(Gson响应体转换器)。

我们去看看第三步: 解析注解

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) 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);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }

这里解析注解主要有分为两类:

  • 1 通过parseHttpMethodAndPath()方法解析出网络请求的方法URL相对路径
  • 2 通过parseHeaders()方法解析出——需要添加的请求头

这里具体的解析方式我们就不深究了。
接下来我们看看第四步:检查各项数据,拿到ParameterHandler,new出ServiceMethod实例。这里就不挖这些代码了。
我们回到Retrofit的create()方法:

public <T> T create(final Class<T> service) {
    //验证服务接口,服务接口必须是一个interface对象,且它没有任何父接口
    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, @Nullable Object[] args)
              throws Throwable {
            // 如果是Object的方法,不做任何处理
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //这个条件判断在Retrofit3.2.0时一直为false,其他版本不知道
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //这里通过服务接口的方法加载了一个ServiceMethod对象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

ServiceMethod的loadServiceMethod()加载完了,接下来就是直接new出OkHttpCall(网络执行器),并把OkHttpCall通过RxJava2CallAdapter的adapt()方法传过去,接下里我们看看adapt()方法:

public Object adapt(Call<R> call) {
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }

首先通过服务接口中的方法的返回类型中的泛型,判断被观察者的种类(一般情况下我们都是responseObservable),然后通过服务接口中的方法的返回类型判断是Flowable、Single、Maybe或者就是Observable。

到这里我们的Retrofit一套简单流程就基本就分析完了,当然这里是浅分析,当然还有颇多存疑。

 mApiService = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .client(getClient())
                .build()
                .create(ApiService.class);

前面分析总结:create()中使用了动态代理,在实际调用服务接口中的方法时,把方法和参数传给了ServiceMethod,ServiceMethod通过构建者模式创建,在build()方法中通过Retrofit拿到了适配器和转换器,并且对方法的注解和参数进行了解析,可以说ServiceMethod做了很多事情,最后ServiceMethod通过调用它拿到的适配器,进过判断,返回网络请求中的被观察者。

有些童鞋可能会想,网络请求到底是在什么时候执行的呢?
通过我们拿到的被观察者,订阅我们自己写的观察者时,进行的网络请求。具体的,RxJava的订阅方法subscrib()最终会调用subscribeActual()这个方法,这个方法是个抽象方法,在Retrofit的取得适配器中的adapt()方法(我们上面分析过这个方法)会new出新的被观察者,这个新的观察者会覆写subscribeActual()这个方法,通过传入的网络执行器发起网络请求。

本片文章谢谢童鞋们的观看。

本人Andorid小白,水平有限,如果有不对的地方,希望大家提点一下我。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • 前言 注解式的框架非常火,注解以其轻量,简洁等特性被人们所喜爱者,关键是它解藕。网络请求的框架非常多,比较受欢迎的...
    萨达哈鲁酱阅读 580评论 0 5
  • 本文将顺着构建请求对象->构建请求接口->发起同步/异步请求的流程,分析Retrofit是如何实现的。 开始之前,...
    zhuhf阅读 1,619评论 0 10
  • 安卓开发领域中,很多重要的问题都有很好的开源解决方案,例如Square公司提供网络请求 OkHttp , Retr...
    aaron688阅读 1,918评论 1 20
  • Retrofit 是目前作为网络请求的主流框架,使用起来很方便,仅需在接口中定义方法,打上注解,而且和 Rxjav...
    Kip_Salens阅读 573评论 0 3
  • 今天下午我去上书法课,因此,我非常开心,因为我又能见到我书法老师和同学们了。 到了书法班之后,我迫...
    豪好学习天天向上阅读 104评论 0 0