Retrofit2流程分析

Retrofit是Square公司出品的Http请求框架,底层网络模块基于Okhttp,是构造REST风格的HTTP客户端的利器,同时具备非常强大的解耦性,可以自定义多种数据格式(xml、json、protocol buffers等),支持Rxjava调用方式。
最近一直在看Retrofit2的源码,对于它具体的流程也是在多次调试后,才略微一二。下面主要分析下Rxjava接口形式的接口生成和调用流程。

Retrofit

Retrofit类是整个流程的入口通过它的Builder类设置一些基本的参数,如下:

 new Builder().baseUrl(baseUrl).addConverterFactory(factory).addCallAdapterFactory(RxJavaCallAdapterFactory.create());

ConverterFactory是Request和Response的转换类,继承Factory类,实现responseBodyConverter和requestBodyConverter即可。guava、gson、java8、protobuf等各种格式的converter可以参考官方文档

Retrofit#create

通过create(final Class<T> service)方法,将注解定义的接口生成一个具体的接口实现。接口定义方式如下:

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

如果使用Rxjava的话,可以定义如下:

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

具体的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);
          }
        });
  }

它通过Proxy.newProxyInstance方法来一个实现了我们定义好的API接口的动态代理类,之后调用任何接口里面的方法,都会通过调用invoke(Object proxy, Method method, Object... args)方法来实现。

ServiceMethod

invoke方法最重要的是最后三行代码。loadServiceMethod方法先会判断缓存里面有没有该method对应的ServiceMethod,如果有就直接用,没有就会生成一个新的。

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

下面看下ServiceMethod的生成过程。

Adapts an invocation of an interface method into an HTTP call.

ServiceMethod 的作用就是把一个 API 方法转换为一个 HTTP 调用。

    public Builder(Retrofit retrofit, Method method) {
          this.retrofit = retrofit;
          this.method = method;
          this.methodAnnotations = method.getAnnotations();
          this.parameterTypes = method.getGenericParameterTypes();
          this.parameterAnnotationsArray = method.getParameterAnnotations();
        }

以上面的GitHubService中listRepos接口为例。

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

@GET("users/{user}/repos"是methodAnnotations,String是parameterTypes,@Path("user")是parameterAnnotationsArray的第一个,参数可以有多个,所有它是一个列表。

ServiceMathod#Builder.build()

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      responseConverter = createResponseConverter();

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

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      return new ServiceMethod<>(this);
    }

上面是build方法里面去除了异常处理之后的逻辑。

createCallAdapter

private CallAdapter<?> createCallAdapter() {
    Type returnType = method.getGenericReturnType();
    
    Annotation[] annotations = method.getAnnotations();
      
    return retrofit.callAdapter(returnType, annotations);
    }

retrofit.callAdapter里面主要代码:

for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

它根据returnType和annotations遍历adapterFactories,获取合适的CallAdapter。

/** * Returns an instance of {@code T} which delegates to {@code
call}. */ <R> T adapt(Call<R> call);

它的作用就是把 retrofit2.Call<R> 转换成我们需要的 T,目前retrofit2.Call的实现只有OkHttpCall,所以现在Retrofit网络框架只能用OkHttp。CallAdapters

createCallAdapter

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

它最后也是遍历converterFactories,获取合适的ResponseConverter,它一般是自定义的,通过Retrofit的Builder类addConverterFactory(factory)方法添加的。

parseMethodAnnotation

DELETE、GET、HEAD、PATCH、POST、PUT、OPTIONS这些注解会调用parseMethodAnnotation方法,生成httpMethod、hasBody、relativeUrl、relativeUrlParamNames4个ServiceMethod的参数,当注解是retrofit2.http.Headers类型时,会调用parseHeaders方法,它会生成OkHttp的Headers。

parseParameter

parseParameter方法通过参数的注解和类型,生成对应的ParameterHandler。

ParameterAnnotation ? extends ParameterHandler
Url RelativeUrl
Path Path
Query Query
QueryMap QueryMap
Header Header
HeaderMap HeaderMap
Field Field
FieldMap FieldMap
Part Part
PartMap PartMap
Body Body

所有类型的ParameterHandler都继承自ParameterHandler,实现了apply方法,通过Converter方法,把参数都转换成相应的类型,然后设置到RequestBuilder中,其中Part、PartMap、Body转成RequestBody,其他都转成String。

这样ServiceMethod基本构造完成。

OkHttpCall

之前我们说了,它是Retrofit中Call的唯一实现。

public interface Call<T>

它负责发起请求和回复,每个Call只负责一对请求和回复,如果要复用的话,调用Call.clone方法。Call主要有execute和enqueue方法,前者是同步的,后者是异步的。OkHttpCall实现了Call接口,里面使用okhttp3.Call来完成网络服务交互。

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

createRawCall方法生成okhttp3.Call,request参数是通过serviceMethod的toRequest方法生成的。

execute和enqueue方法里面实际上就是调用okhttp3.Call来实现了同步和异步请求,但是对Response做了进一度处理,通过parseResponse方法,把OkHttp3.Response转成Retrofit2.Response

T body = serviceMethod.toResponse(catchingBody); return
Response.success(body, rawResponse);

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

还是调用Retrofit里面的responseConverter实现了返回结果的转换。

serviceMethod.callAdapter.adapt(okHttpCall)

这一步是把Call转成我们所需的T。

CallAdapters ? extends CallAdapter.Factory T
guava retrofit2.adapter.guava.GuavaCallAdapterFactory com.google.common.util.concurrent.ListenableFuture
java8 retrofit2.adapter.java8.Java8CallAdapterFactory com.google.common.util.concurrent.ListenableFuture
rxjava retrofit2.adapter.rxjava.RxJavaCallAdapterFactory rx.Observable
default retrofit2.ExecutorCallAdapterFactory retrofit2.Call<R>
default retrofit2.DefaultCallAdapterFactory retrofit2.Call<R>

如果是retrofit2.Call类型的,可以调用execute或者enqueue完成请求。

如果rx.Observable类型,会在subscribe()时候调用下面代码:

@Override public void call(final Subscriber<? super Response<T>> subscriber) {
      // Since Call is a one-shot type, clone it for each new subscriber.
      Call<T> call = originalCall.clone();

      // Wrap the call in a helper which handles both unsubscription and backpressure.
      RequestArbiter<T> requestArbiter = new RequestArbiter<>(call, subscriber);
      subscriber.add(requestArbiter);
      subscriber.setProducer(requestArbiter);
    }

它其实是Observable.create(new CallOnSubscribe<>(call))里面CallOnSubscribe的实现。

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

推荐阅读更多精彩内容