Retrofit源码解析

简单使用

Retrofit turns your HTTP API into a Java interface.

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

The Retrofit class generates an implementation of the GitHubService interface.

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

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

Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.

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

创建Retrofit对象

可以看到这里是通过一个builder模式来构建的Retrofit对象。
主要就是传入和初始化各类参数。

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

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

    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
        // 这里可以理解成就是Handler
        callbackExecutor = platform.defaultCallbackExecutor();
    }

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

    // Make a defensive copy of the converters.
    List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

    // 返回retrofit对象
    return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
    }
}

获取API实例

这里在以前的一篇文章中分析过,主要是运用了动态代理的技术。

简而言之,就是动态生成接口的实现类(当然生成实现类有缓存机制),并创建其实例(称之为代理),代理把对接口的调用转发给 InvocationHandler 实例,而在 InvocationHandler 的实现中,除了执行真正的逻辑(例如再次转发给真正的实现类对象),我们还可以进行一些有用的操作,例如统计执行时间、进行初始化和清理、对接口调用进行检查等。

为什么要用动态代理?因为对接口的所有方法的调用都会集中转发到 InvocationHandler#invoke 函数中,我们可以集中进行处理,更方便了。你可能会想,我也可以手写这样的代理类,把所有接口的调用都转发到 InvocationHandler#invoke 呀,当然可以,但是可靠地自动生成岂不更方便?

retrofit拆解 - 动态代理
公共技术点之 Java 动态代理

方法调用

在我们获取到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, Object... args)
          throws Throwable {
        // If the method is a method from Object then defer to normal invocation.
        // 如果是object的方法,就直接调用
        if (method.getDeclaringClass() == Object.class) {
          return method.invoke(this, args);
        }
        // 如果是default方法,就直接调用default方法
        if (platform.isDefaultMethod(method)) {
          return platform.invokeDefaultMethod(method, service, proxy, args);
        }
        // 这里才是重点
        ServiceMethod serviceMethod = loadServiceMethod(method);
        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
        return serviceMethod.callAdapter.adapt(okHttpCall);
      }
    });

ServiceMethod

这里我们先了解一下ServiceMethod是有什么作用的:

Adapts an invocation of an interface method into an HTTP call. 把对接口方法的调用转为一次 HTTP 调用。

我们可以理解成一个ServiceMethod对象对应于一个API interface的一个方法。
至于这个ServiceMethod是怎么来的,就需要看看具体的方法了:

// 处理Retrofit.java中
ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
        // 这里是有一个cache的。同一个API的同一个方法就只用解析一次
        result = serviceMethodCache.get(method);
        if (result == null) {
            // 这里也是通过builder模式得到ServiceMethod对象
          result = new ServiceMethod.Builder(this, method).build();
          serviceMethodCache.put(method, result);
        }
    }
    return result;
}

我们看看ServiceMethod的构造函数:

// 处于ServiceMethod.java
ServiceMethod(Builder<T> builder) {
    // 这里就是retrofit的callFactory, 其实就是okhttpClient
    this.callFactory = builder.retrofit.callFactory();
    // 这个就联想RxJavaCallAdapterFactory,它是把
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    // 负责把服务器返回的数据(JSON、XML、二进制或者其他格式,由 ResponseBody 封装)转化为 T 类型的对象
    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;
    // 则负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数;
    this.parameterHandlers = builder.parameterHandlers;
}

callFactory

callFactory 实际上由 Retrofit 类提供,而我们在构造 Retrofit 对象时,可以指定 callFactory,如果不指定,将默认设置为一个 okhttp3.OkHttpClient。

callAdapter

// 处于ServiceMethod.java
private CallAdapter<?> createCallAdapter() {
  // 省略检查性代码
  Annotation[] annotations = method.getAnnotations();
  try {
    return 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.java
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}

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++) {
        // 这里进入到具体的CallAdapter中去获取类型(java,rxjava,guava)
        CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
        if (adapter != null) {
            return adapter;
        }
    }
    // 抛出异常
    ...
    throw new IllegalArgumentException(builder.toString());
}

responseConverter

private Converter<ResponseBody, T> createResponseConverter() {
  Annotation[] annotations = method.getAnnotations();
  try {
    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);
  }
}

同样,responseConverter 还是由 Retrofit 类提供,而在其内部,逻辑和创建 callAdapter 基本一致,通过遍历 Converter.Factory 列表,看看有没有工厂能够提供需要的 responseBodyConverter。工厂列表同样可以在构造 Retrofit 对象时进行添加。

parameterHandlers

每个参数都会有一个 ParameterHandler,由 ServiceMethod#parseParameter 方法负责创建,其主要内容就是解析每个参数使用的注解类型(诸如 Path,Query,Field 等),对每种类型进行单独的处理。构造 HTTP 请求时,我们传递的参数都是字符串,那 Retrofit 是如何把我们传递的各种参数都转化为 String 的呢?还是由 Retrofit 类提供 converter!

Converter.Factory 除了提供上一小节提到的 responseBodyConverter,还提供 requestBodyConverter 和 stringConverter,API 方法中除了 @Body 和 @Part 类型的参数,都利用 stringConverter 进行转换,而 @Body 和 @Part 类型的参数则利用 requestBodyConverter 进行转换。

OkHttpCall

final class OkHttpCall<T> implements Call<T> {
    OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
        this.serviceMethod = serviceMethod;
        this.args = args;
    }
}

OkHttpCall 实现了 retrofit2.Call,我们通常会使用它的 execute() 和 enqueue(Callback<T> callback) 接口。前者用于同步执行 HTTP 请求,后者用于异步执行。

serviceMethod.callAdapter.adapt

CallAdapter<T>#adapt(Call<R> call) 函数负责把 retrofit2.Call<R> 转为 T。这里 T 当然可以就是 retrofit2.Call<R>,这时我们直接返回参数就可以了,实际上这正是 DefaultCallAdapterFactory 创建的 CallAdapter 的行为。

其实就是返回接口中定义的方法的返回值,要么是Call<T>,要么是Observable<T>

这里我们就以默认的ExecutorCallAdapterFactory来进行分析:

// 这里是在ExecutorCallAdapterFactory.java
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<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      // 在这里进行的adapt方法
      // 参数call其实就是OkHttpCall
      @Override public Call<Object> adapt(Call<Object> call) {
          // 其实这里返回的就是一个CallbackCall
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
}

enqueue

// 这里是在ExecutorCallAdapterFactory.java
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 其实就是OkHttpCall, OkHttpCall就是对okhttp的包装
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
            // 这里的callbackExecutor 其实就是线程调度器,在Android平台下默认就是Handler
            // execute其实就是handler.post(r)
          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.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

这里其实就是对应了用户调用的enqueue方法,
内部先是通过OkHttpCall调用enqueue,
然后OkHttpCall内部还是由okhttp的enqueue方法

// 这里是在OkHttpCall中执行的
@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    okhttp3.Call call;
    Throwable failure;

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

      call = rawCall;
      failure = creationFailure;
      // 创建真正的OKHttp
      if (call == null && failure == null) {
        try {
            // 在这里用convert对请求进行了封装
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
    // okhttp执行真正的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);
      }

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

Converter

retrofit 模块内置了 BuiltInConverters,只能处理 ResponseBody, RequestBody 和 String 类型的转化(实际上不需要转)。而 retrofit-converters 中的子模块则提供了 JSON,XML,ProtoBuf 等类型数据的转换功能,而且还有多种转换方式可以选择。这里我主要关注 GsonConverterFactory。

根据上面的enqueue里的代码,里面会有一个解析数据的方法,我们先看看那个方法:

// 这里是在OkHttpCall.java中
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 {
        // 重点关注这个!!!
      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.java
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
    // 所以这里就可以直接关注convert.convert方法了
    return responseConverter.convert(body);
}

小结

整体流程图

相关链接

拆轮子系列:拆 Retrofit

Retrofit分析-漂亮的解耦套路

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

推荐阅读更多精彩内容