Retrofit 源码解析的初次尝试

enter image description here

目录

  • Retrofit 简介
  • Retrofit 的具体使用
  • 重点分析

Retrofit 简介

Retrofit 是 Square 推出的 HTTP 框架,主要用于 Android 和 Java。Retrofit 将网络请求变成方法的调用,使用起来非常简洁方便。

  • Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to
  • define how requests are made. Create instances using {@linkplain Builder
  • the builder} and pass your interface to {@link #create} to generate an implementation.

这是源码开头的解释,大致意思是 Retrofit 利用方法上的注解接口转化成一个 HTTP 请求。


Retrofit 的使用
  1. 首先建立API接口类:
public interface ZhihuApi {
    String HOST = "http://news-at.zhihu.com/api/";
    @GET("4/news/latest")
    Call<ZhihuList> getZhihuListNews(int page);
}
  1. 具体使用过程:
 // 创建 Retrofit 实例
 Retrofit retrofit = new Retrofit.Builder()
     .baseUrl(ZhihuApi.HOST)
     .addConverterFactory(GsonConverterFactory.create())
     .build();

 // 生成接口实现类
 ZhihuApi zhihuApi = retrofit.create(ZhihuApi.class);

 // 调用接口定义的请求方法,并且返回 Call 对象
 Call<ZhihuList> call = zhihuApi .getZhihuListNews(1);

 // 调用 Call 对象的异步执行方法
 call.enqueue(Callback callback)

以上就是 Retrofit 的基础用法,注意这里的 addConverterFactory() 方法,它是用于吧返回的 http response 转换成 Java 对象,对应方法的返回值就是 ZhihuList 这个自定义的类。大致流程就是获得 retrofit 对象之后,接着调用 create()方法创建 ZhihuApi 的实例,然后调用 ZhihuApi 的方法请求网络,返回的是一个 Call对象,然后就可以使用 Call 方法的 enqueue 或者 execute 来执行发起请求,enqueue 是是异步执行,而 execute是同步执行。


Retrofit 的重点解析
  1. Retrofit 的创建( Builder 模式)
 // 用于缓存解析出来的方法
  private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
  // 请求网络的 OKHttp 的工厂,默认是 OkHttpClient
  private final okhttp3.Call.Factory callFactory;
  // baseurl
  private final HttpUrl baseUrl;
  // 请求网络得到的 response 的转换器的集合 默认会加入 BuiltInConverters    
  private final List<Converter.Factory> converterFactories;
  // 把 Call 对象转换成其它类型,如 Call 以及 Observer
  private final List<CallAdapter.Factory> adapterFactories;
  // 用于执行回调 Android 中默认是 MainThreadExecutor
  private final Executor callbackExecutor;
  // 是否需要立即解析接口中的方法
  private final boolean validateEagerly;

以上是几个 Retrofit 中比较关键的变量,每个都有注释,一看便知。接下来看一看 内部类 Builderbuild()方法:

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

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
      // 默认创建一个 OkHttpClient
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
      // Android 中返回的是 MainThreadExecutor
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      //adapterFactories 把 Call 对象转换成其他对象,如 Observer
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      // 请求网络得到的 response 的转换器的集合
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

以上代码可以看出:callAdapter 是请求返回的对象,Converter 是转换器,转换请求的响应到对应的实体对象,OkHttpClient 是具体的 OkHttp 的请求客户端,在创建 Retrofit 的时候,如果没有指定 OkHttpClient,会创建一个默认的。如果没有指定 callbackExecutor,会返回平台默认的,在 Android 中是 MainThreadExecutor,并利用这个构建一个 CallAdapter 加入 adapterFactories

  1. Create() 方法
    有了 Retrofit 对象后,便可以通过 create() 方法创建网络请求接口类的对象。直接上代码:
public <T> T create(final Class<T> service) {
    // 检查传入的类是否为接口并且无继承
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      // 提前解析方法
      eagerlyValidateMethods(service);
    }
    // 重点是这里
    // 首先会返回一个利用代理实现的 ZhihuApi 对象
    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 the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // 为了兼容 Java8 平台,Android 中不会执行
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }

            // 解析方法 这里用到了注解(Runtime)
            ServiceMethod serviceMethod = loadServiceMethod(method);
            // 将刚刚解析完毕包装后的具体方法封装成 OkHttpCall ,你可以在该实现类找到 okhttp 请求所需要的参数
            // 所以它是用来跟 okhttp 对接的。
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
             // 将以上我们封装好的 call 返回给上层,这个时候我们就可以执行 call 的同步方法或者异步进行请求。
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

这里使用的是 动态代理模式 ,简单的描述就是Proxy.newProxyInstance 根据传进来的 Class 对象生成了代理类,当这个代理类执行某个方法时总是会调用 InvocationHandler(Proxy.newProxyInstance 中的第三个参数) 的invoke方法,在这个方法中可以执行一些操作(这里是解析方法的注解参数等),通过这个方法真正的执行我们编写的接口中的网络请求。我们再来理一下思路:
Create() -->
return (T) Proxy.newProxyInstance(...){...}-->
Call<ZhihuList> call = zhihuApi .getZhihuListNews(1);-->
public Object invoke(...){...}调用代理类的invoke()
记住动态代理,是在方法调用时才会真正的触发过程。

  1. ServiceMethod
    下面看一下在 invoke 中解析网络请求方法的几行,ServiceMethod serviceMethod = loadServiceMethod(method);代码如下:
 ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      // 从缓存中获取该方法
      result = serviceMethodCache.get(method);
      if (result == null) {
        // 为空就创建并且加入缓存
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

Retrofit 有一个双链表用来缓存方法
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();避免一个网络请求在多次调用的时候频繁的解析注解,毕竟注解解析过程消耗比较大。这个部分重点在ServiceMethod.Builder(this, method).build();
build()源码很长,只截取重点:

// 用来发送请求的 client
callAdapter = createCallAdapter();
...
// 结果的转换器(Gson,FastJson …)之类
responseConverter = createResponseConverter();

// 遍历解析注解
for (Annotation annotation : methodAnnotations) {
   parseMethodAnnotation(annotation);
}
...省略代码
  1. OkHttpCall

在得到 ServiceMethod 对象后,把它连同方法调用的相关参数传给了 OkHttpCall 对象,就是前面的这行代码:
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

这个OkHttpCall才是真正OkHttp请求的回调,但是针对我们使用的不同的回调,比如:RxJava的Observable、Call,所以有一层转换的关系,把OkHttpCall转成对应的Observable和Call。就是serviceMethod.callAdapter.adapt(okHttpCall)的工作。

请求方法参数,请求客户端,返回值转换,我们都定义好了之后,便完成最后一步,构建好适合请求客户端的请求方法,Retrofit 默认的是 okhttpCall 。接下来把 OkHttpCall 传给 serviceMethod.callAdapter 对象

那么CallAdapter是干嘛的呢?上面调用了adapt方法,它是为了把一个Call转换成另一种类型,比如当 Retrofit 和 RxJava 结合使用的时候,接口中方法可以返回 Observable<T>,这里相当于适配器模式。默认情况下得到的是一个 Call对象,它是ExecutorCallbackCall,代码如下:

@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;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

以上代码是在ExecutorCallAdapterFactory类中,这个类继承自CallAdapter.Factory,可以看到adapt()方法接受一个Call对象,看下ExecutorCallbackCall的部分源码:

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;
    }

    @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.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);
              }
            }
          });
        }
        ...省略
  }

enqueue 方法中,调用了 OkHttpCallenqueue,所以这里相当于静态的代理模式。OkHttpCall 中的 enqueue其实又调用了原生的OkHttp中的 enqueue,这里才真正发出了网络请求,部分代码如下:

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");
    //真正请求网络的 call
    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      //省略了部分发代码
      ...
      call = rawCall;
      //enqueue 异步执行
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
        //解析数据 会用到 conveterFactory,把 response 转换为对应 Java 类型
          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();
    }
  }
 });
}

OkHttp 获取数据后,解析数据并回调callback响应的方法,一次网络请求便完成了。

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

推荐阅读更多精彩内容