Retrofit解析

一.Retrofit实例的创建

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com")
                .build();

设计模式:

1.建造者模式:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。通俗的说就是:建造者模式就是如何一步步构建一个包含多个组成部件的对象,相同的构建过程可以创建不同的产品

优点:

在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

缺点:

如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大

2.门面模式

门面模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

优点:

-对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入门面模式,客户代码将变得很简单,与之关联的对象也很少。
-实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。

缺点:

-不能很好地限制客户使用子系统类。
-在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

二.Retrofit的使用

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

GitHubService service = retrofit.create(GitHubService.class);

Call<List<Repo>> repos = service.listRepos("octocat");

retrofit.create(GitHubService.class) 先看下 如何创建 Api 实例:

    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 {
              ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });

使用了动态代理,对方法调用的真正执行逻辑在 InvocationHandler.

三.接口如何转为真正的请求

上面我们知道真正的请求是通过InvocationHandler 里面,来执行真正的逻辑

     ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);

ServiceMethod 负责把接口方法注解转换为http表示
OkhttpCall 负责将serviceMethod转换为Okhttp3.Call

1.ServiceMethod
/** Adapts an invocation of an interface method into an HTTP call. */
                将一个接口调用转化为 HTTP 请求

ServiceMethod 的作用是将我们对 Api Service 对接口的调用转化为HTTP call

 ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>)  `loadServiceMethod(method)`;
 
 ServiceMethod<?, ?>  `loadServiceMethod(Method method)` {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
      //省略
    return result;
  }

ServiceMethod 会对每个方法,做缓存处理,ServiceCache 内存在的直接返回结果

ServiceMethod 的构造函数:

  ServiceMethod(Builder<R, T> builder) {
   //网络请求工厂   默认值为 okhttp3.OkHttpClient
    this.callFactory = builder.retrofit.callFactory();
   //网络请求适配器   把Call 请求适配各个平台
   this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
   解析每个参数使用的注解类型
    this.parameterHandlers = builder.parameterHandlers;
  }
1.1callAdapter
/**
 * Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are
 * created by {@linkplain Factory a factory} which is
 * {@linkplain Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit}
 * instance.
 */

public interface CallAdapter<R, T> {
T adapt(Call<R> call);
}
CallAdapter 主要就是将 R 转为 T,

我们看下Retrofit 默认的 CallAdapter

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

        CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
           if (callbackExecutor != null) {
             return new ExecutorCallAdapterFactory(callbackExecutor);
          }
           return DefaultCallAdapterFactory.INSTANCE;
         }

默认的 ExecutorCallAdapterFactory 主要做的事 就是将 调用 enqueue 时,将结果回调至 传入的 callbackExecutor (默认值 为 MainThreadExecutor),
所以我们在调用 enqueue 时返回的 callback 内的数据 是在 主线程,就是在这里处理的

有一个疑问是,我们传入了多个 CallAdapter Retrofit 是如何处理 ?

Retrofit 会遍历CallAdapters 根据 returnType 来自动选择合适的 CallAdapter

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

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    // 省略
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      //调用 callAdapter 的 get 直至 不为 null
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    // 省略
  }

//DefaultCallAdapterFactory 内的实现

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
   // 如果返回值类型 不是 Call.class 直接返回 null, 寻找下一个
  if (getRawType(returnType) != Call.class) {
      return null;
    }
}


1.2responseConverter

public interface Converter<F, T> {
T convert(F value) throws IOException;
}

Converter 主要作用是将 F 转 为 T

我们看下convert 函数被调用的地方
ServiceMethoid类内的

/** Builds a method return value from an HTTP response body. */
  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }
F 就是 服务器返回的 body , T是什么呢, 通过 CallAdapter的获取, 初步判断与 return type 有关

Retrofit类内

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

 public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
     // 省略参数判断 
        
    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;
      }
    }
      //省略
  }

在 Retrofit build方法内
会先添加一个 converter BuiltInConverters

final class BuiltInConverters extends Converter.Factory {
  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == ResponseBody.class) { 
    // 判断注解内 是否有 Streaming.class , 有返回  StreamingResponseBodyConverter, 其他返回 BufferingResponseBodyConverter
      return Utils.isAnnotationPresent(annotations, Streaming.class)
          ? StreamingResponseBodyConverter.INSTANCE
          : BufferingResponseBodyConverter.INSTANCE;
    }
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;
    }
    return null;
  }
}
2.OkhttpCall

OkhttpCall implements 了 retrofit2.Call

平时我们调用

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com")
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        Api service = retrofit.create(Api.class);
          // enqueue 的真正执行逻辑就是 在 OkhttpCall 里面实现的
         service.getUser().enqueue();   //异步
         
         service.getUser().execute()     // 同步
  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
      
       call = rawCall;
      if (call == null) {
        try {
          //1.创建 call 对象 
         call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }
   //  2.执行请求  3.  解析response
    return parseResponse(call.execute());
  }
OkhttpCall excute() 主要做了三件事
  1. 创建 okhttp3.Call 对象
  2. 执行请求
  3. 解析response

我们看下 okhttp3.Call对象的创建, 调用 ServiceMethod 的 toCall 创建, 这时候 Retrofit 去解析我们写在 接口上的各种注解,
解析成功 我们平时 Request 对象的各种参数

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
  //创建 HTTP 请求 通过方法参数
  /** Builds an HTTP request from method arguments. */

   okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);
    
    //通过 ParameterHandler 解析注解
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
      // callFactory  默认值为   new OkHttpClient()
      return callFactory.newCall(requestBuilder.build());
  }

解析Response

  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();

    int code = rawResponse.code();
    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();
      }
    }

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

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      // 将 Response 转化为 T 
      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;
    }
  }

调用 ServiceMethod 将 response 转为 T, 根据 我们Api 接口的 Return Type 决定 responseConverter 对象

  /** Builds a method return value from an HTTP response body. */
  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }
异步 enqueue(Callback<T> callback)

调用 okhttp3.Call#enqueue(Callback responseCallback) 来实现, 将返回的数据callback ,其他步骤与 excute 一样

  1. callAdapter.adapt(call)
  T adapt(Call<R> call) {
    return callAdapter.adapt(call);
  }
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
      // 默认的 CallAdapter ,    T adapt(Call<R> call)  , T 就是 Call<Object>, R就是 Object
     return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return call;
      }
    };
  }
}

至此 整个 网络请求结束 :

1.通过 ServiceMethod 的 ParameterHandler 解析参数 ,通过 ApiService 的 方法 return type 选择合适的 callAdapter 和 converter
   ServiceMethod<Object, Object> serviceMethod =  (ServiceMethod<Object, Object>) loadServiceMethod(method);
2. 包装 Okhttp#call ,执行网络请求, 解析 response
    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
3. 将reponse 通过 convert 转为 method return type
   serviceMethod.adapt(okHttpCall);

借用 Retrofit分析-漂亮的解耦套路

图片名称

参考文章链接 :

https://www.jianshu.com/p/45cb536be2f4
https://juejin.im/post/5a7ff9b3f265da4e9a49567a
https://blog.piasy.com/2016/06/25/Understand-Retrofit/

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

推荐阅读更多精彩内容