Retrofit2实现源码分析

本文详细解析了Retrofit2结合RxJava实现网络请求的原理,深入探讨了Retrofit2源码,解释了如何通过OkHttp3发起网络请求,以及不同适配器的作用。

最近研究Retrofit2+RxJava实现网络请求及数据集处理,一部分的知识点就在Retrofit2,为了更好的理解代码,本人决定分析下Retrofit2源码,既然是分析源码就得带着目的去分析,那么说说本文要解决的问题吧,先看代码来说明

Retrofit retrofit = new Retrofit.Builder()
        .client(new OkHttpClient())
        .baseUrl(API_URL)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
        .addConverterFactory(GsonConverterFactory.create())
        .build();
ZhuanLanApi api = retrofit.create(ZhuanLanApi.class);
Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");
call.enqueue(new Callback<ZhuanLanAuthor>() {
    @Override
    public void onResponse(Call<ZhuanLanAuthor> call, Response<ZhuanLanAuthor> response) {
        System.out.println(response.body().getName());
    }

    @Override
    public void onFailure(Call<ZhuanLanAuthor> call, Throwable t) {

    }
});

以上代码就是单独使用Retrofit2的基本方式,在写该代码时,我有几个疑惑,先列举吧。

疑惑1call.enqueue是怎么发起网络请求的,和Okhttp3发起的网络请求为什么这么相似

疑惑2: 创建retrofit对象时client函数传的OkHttpClient对象有何作用

疑惑3:  addCallAdapterFactory函数传参有起到什么作用

疑惑4: addConverterFactory函数传参起到什么作用

下面就来解决这些疑惑

1、Retrofit2和OkHttp3的关系

实际上Retrofit2是用来解决Okhttp3用法复杂的问题的,先看看用OkHttp3实现一个异步网络请求的代码

String url = "https://www.baidu.com/";
OkHttpClient okHttpClient = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json;charset=utf-8");
RequestBody body = RequestBody.create(JSON, "{'name':'yjing','age':'12'}");
Request request = new Request.Builder()
        .post(body)
        .url(url)
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        System.out.println("我是异步线程,线程Id为:" + Thread.currentThread().getId());
    }
});

注意,这里只是举个例子,传的参数是没有意义的,从代码可以看出OkHttp3使用太复杂,每次调用都得配置手动配置参数,而Retrofit2就是对OkHttp3进行了封装,使得能够通过调用一个Java方法来发起网络请求,网络请求实际上还是由OkHttp3完成的,Retrofit2只是将Java方法反射成为对应的网络请求。

由OkHttp3的使用代码可知,使用OkHttp3发起网路请求使通过Call对象来完成的,而创建Call实例对象,需要OkHttpClient对象和Request对象(这里需要注意)。

2、Retrofit2框架结构说明

在对Retrofit2源码进行解析之前,先说说其框架结构

这里写图片描述

这里的接口和类并不多

Converter:
负责转换网络请求返回数据为对应格式数据的接口,其中GsonConverterFactory共厂类生产的GsonRequestBodyConverter类就是该接口的实现

CallAdapter:
负者决定retrofit.create()返回参数的接口,DefaultCallAdapterFactory以及RxJavaCallAdapterFactory连个工厂类生产的类都是该接口的实现

ServiceMethod:
这个类比较核心,几乎保存了一个API请求所有需要的数据,OkHttpCall需要从ServiceMethod中获得一个Request对象,然后得到Response后,还需要传入ServiceMethod用对应的Converter对象转将Response数据换成数据格式

Retrofit:
生成Retrofit对象,并初始化上面的几个接口实现类或ServiceMethod类对象

3、Retrofit类介绍

上面有代码说明Retrofit对象的初始化操作,这里为了方便说明再贴一次

Retrofit retrofit = new Retrofit.Builder()
        .client(new OkHttpClient())
        .baseUrl(API_URL)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
        .addConverterFactory(GsonConverterFactory.create())
        .build();
ZhuanLanApi api = retrofit.create(ZhuanLanApi.class);

通过build的一系列方法,完成了在Retrofit对象中实例化Convertor对应工厂类、CallAdapter对应工厂类以及OkHttpClient对象的初始化。

然后调用create方法

public <T> T create(final Class<T> service) {
  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, 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);
          }
          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);
        }
      });
}

这里的create方法中使用了动态代理,作用是当调用对应service方法时,会执行到动态代理代码中来,并且传入service对应方法反射对象以及方法参数。而动态代理方法核心是最后三行代码,其中loadServiceMethod方法会实例化一个ServiceMethod对象,并实例化ServiceMethod对象中的一下几个关键变量

下面先在三小节说说ServiceMethod方法的关键点

3、ServiceMethod类的关键点

ServiceMethod中有callAdapter变量、responseConverter变量、toRequest方法、toResponse方法这几个关键点。

callAdapter变量

根据是否有调用addCallAdapterFactory方法来实例化,例如如果调用了addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))则根据该工厂类创建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<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }
  }

如果没有调用addCallAdapterFactory方法则使用一下默认工厂类生成的CallAdapter对象。

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  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<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

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

这里一定熬注意两种CallAdapter对象的adapt方法的差异,不同的CallAdapterFactory生产的CallAdapter类,其adapt方法的返回参数不一样,DefaultCallAdapterFactory 对应的返回参数是Call,而SimpleCallAdapter 对应的返回参数是Observable。

responseConverter变量

该变量负责根据对应规则对Response数据进行格式转化,在Retrofit类的nextResponseBodyConverter方法中可以看到,当有调用addConverterFactory(GsonConverterFactory.create())使用GsonConverterFactory或其它的工厂类生产的转换器,从代码中可以看出当添加多个工厂类时,使用第一个工厂类生产的转换器。

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

然后我们来看看GsonConverterFactory工厂类生产的对象

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

GsonResponseBodyConverter的convert方法会将ResponseBody 值转化为对应Java方法的返回参数T类型,如下面的方法则转化为ZhuanLanAuthor类型(解决疑惑4)。

Call<ZhuanLanAuthor> getAuthor(@Path("user") String user);

toResponse

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

这个方法将body数据转化为对应参数数据。

toRequest

/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
  RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
      contentType, hasBody, isFormEncoded, isMultipart);

  @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
  ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

  int argumentCount = args != null ? args.length : 0;
  if (argumentCount != handlers.length) {
    throw new IllegalArgumentException("Argument count (" + argumentCount
        + ") doesn't match expected count (" + handlers.length + ")");
  }

  for (int p = 0; p < argumentCount; p++) {
    handlers[p].apply(requestBuilder, args[p]);
  }

  return requestBuilder.build();
}

这个方法,提供OkHttp3发起的网络请求需要的Request对象。

4、再谈Retrofit类的create方法

大致介绍完Retrofit类和ServiceMethod后,接着看Retrofit的create方法,前面说了其最后三行是关键代码

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

其中第一行代码讲了,是实例化ServiceMehod对象,第二行是实例化OkHttpCall对象,该OkHttpCall是Call接口的实现,最后调用了callAdapter.adapt方法。前面备注了要注意的就是这个adapt方法,不同的CallAdapter其对应的adapter方法不一样,如前面所说DefaultCallAdapterFactory 工厂类生产的CallAdapter adapt方法对应的返回参数是Call,而SimpleCallAdapter 对应adapt方法的返回参数是Observable,这就为之后RxJava结合Retrofit2使用留下了铺垫(解决疑惑3)。

我们看到当不添加.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))方法时,默认的CallAdater adapt方法直接来什么参数返回什么,也就是返回OKHttpCall对象。

到这里应该知道在本文最开始在addCallAdapterFactory之后还使用了

 Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");

这种方式来返回Call在运行时,肯定是会报错的,应为实际上api.getAuthor方法返回的是Observable变量,这地方只是为了方便后面讲解,才那么写的。

5、call.enqueue发起网络请求

这里的call.enqueue放弃网络请求的时候,实际上是OKHttpCall对象发起的网络请求,看起enqueue的部分代码

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

  call = rawCall;
  failure = creationFailure;
  if (call == null && failure == null) {
    try {
      call = rawCall = createRawCall();
    } catch (Throwable t) {
      failure = creationFailure = t;
    }
  }
}

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

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

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中通过获取ServiceMethod对象toRequest方法生成call对象,儿toRequest方法实际上就是获取的我们传入的.client(new OkHttpClient()) OkHttpClient对象(解决疑惑1、疑惑2)。

private okhttp3.Call createRawCall() throws IOException {
  Request request = serviceMethod.toRequest(args);
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

然后通过call对象调用call.enqueue方法发起网络请求,并执行回调。

到这里对Retrofit2的源码分析就算完成了,在分析的过程中也算是解决了文章开始提出的四个疑问。

6、参考文献

1、Retrofit2 源码解析
http://www.jianshu.com/p/c1a3a881a144

2、OkHttp3的基本用法
http://www.jianshu.com/p/1873287eed87

7、Demo地址

Android Demo:https://github.com/Yoryky/AndroidDemo.git
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值