【Retrofit2进阶】---从源码角度看Retrofit2实现原理

前言

本文是Retrofit2进阶系列的第一篇---从源码角度看Retrofit2实现原理,我会尽可能用简洁的代码帮大家理解Retrofit实现一次网络请求的核心流程是怎样的,这个流程整体上就4个步骤,是不复杂的,希望大家带着信心读下去,在文章末尾,我也为大家做出了简单的总结,希望能帮大家更好的理解这个框架,希望在读文章的你能有一些收获,能有更大的进步。备注:基于Retrofit2.4.0分析

系列文章:
Retrofit2进阶】---启示、思想

先来看一个最基础的网络请求案例:

//1、创建Retrofit接口
public interface ApiService {
    @GET("/")
    Observable<String> getData();
    @GET("/")
    Call<String> getData2();
}

//2、retrofit相关配置 
Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://xxxx/")
                    .client(client)
                    //json转换
                    .addConverterFactory(GsonConverterFactory.create())
                    //RxJava2转换器
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
//创建接口对象
ApiService apiService = retrofit.create(ApiService.class);

//3、接口方法调用
3.1 call调用
apiService.getData2().enqueue();

3.2结合rxjava调用
apiService.getData().subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe();

下面就来分析一下Retrofit2是如何完成一次完整的网络请求的。

先来看一下Call调用

public interface ApiService {
    @GET("/")
    Call<String> getData2();
}

通过接口调用会返回一个Call对象,如果你看过OkHttp源码的话,应该会对这个Call很熟悉,它封装了一个网络请求的任务,但是它却不做网络请求,而是通过调用enqueue()或者execute()来发起真正的网络请求。

那这个Call对象如何生成的?其实可以在retrofit.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, @Nullable 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<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

create()内部的核心就一件事情,通过动态代理生成一个代理类对象,具体来说就是通过Proxy.newProxyInstance()来创建的。然后在回调方法invoke()内部通过一系列解析,包装,最终返回用于发起网络请求的Call对象或者用于结合rxjava使用的Observable对象。

这里的关键点与难点在于,你要懂代理模式和动态代理,如果你还不会的话,建议先简单学习一下再接着往下看,不然很多逻辑的连贯性你是不懂的。这里推荐一下我之前的一篇文章Java动态代理那些你容易忽略的细节

先说一下Retrofit2动态代理的逻辑

//retrofit的API接口对象
ApiService apiService;
//创建代理对象
 apiService = retrofit.create(ApiService.class);
//调用代理类中的方法
apiService.xxx();

首先生成一个代理类对象,当调用代理类的方法时,会回调执行InvocationHandlerinvoke()方法

@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            //省略,不会执行的代码

            //生成Call关键在这里
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  • ServiceMethod是对接口中的方法进行解析,包括注解、参数,生成Http Request,具体来说是toRequest(),将response转换为String或者实体类,具体说是toResponse()
  • OkHttpCall是对okhttp3.call的封装,以及okhttp3的其他方法的调用;
  • serviceMethod.callAdapter.adapt(okHttpCall);通过calladapter转换器,将 okhttp3.call转换成可以用于发起网络请求的retrofit2.Call或者说用于rxjava的Observable

下面对ServiceMethodOkHttpCallCallAdapter做一个简单的解释,注意不是详解,这样可以让你避免陷入一叶障目的困境。我们先搞清楚主流程,那些细枝末节后面再看呗。

1、ServiceMethod
它是通过建造者模式来创建的,核心内容如下:
这部分内容先不用详细理解,可以先看个大概,后面再单独来看

 public ServiceMethod build() {
        //获取具体的网络适配器对象CallAdapter,如果retrofit配置了rxjava2转换器,那这里就是RxJava2CallAdapter
       callAdapter = createCallAdapter();
        
       responseType = callAdapter.responseType();
      //获取响应转换器
     responseConverter = createResponseConverter();

      //解析方法注解,比如@GET、@POST、@FormUrlEncoded等等
      //这里解析完毕之后可以获取到Http请求方式,请求体,url等信息
      for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);
      }

      //解析当前方法的参数
int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

}

2、OkHttpCall
它继承于Retrofit2.Call,内部就是对okhttp3一系列操作的封装,甚至说代码都是直接copy的。这里就不展开详细解释了。注意:不要被OkHttpCall的名字所迷惑,它不是okhttp3中的call,而是Retrofit2.Call

final class OkHttpCall<T> implements Call<T> {
  private final ServiceMethod<T, ?> serviceMethod;
  private final @Nullable Object[] args;

  private volatile boolean canceled;

  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;
  @GuardedBy("this")
  private @Nullable Throwable creationFailure; // Either a RuntimeException or IOException.
  @GuardedBy("this")
  private boolean executed;

  OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

3、callAdapter
上面invoke()方法中,最后一步是return serviceMethod.callAdapter.adapt(okHttpCall);
上面有提到过,这一步的作用是返回一个Retrofit2.Call或者Observable

这里的CallAdapter对象是一个接口对象,所以需要找到他的实现类。先回顾一下在Retrofit配置的时候,有这么一行代码.addCallAdapterFactory(RxJava2CallAdapterFactory.create()),它可以将接口方法返回Observable,如果不加这个配置,会如何呢?答案是会返回Retrofit2.Call类型,具体是如何产生的,请看如下流程:

retrofit.build()---->platform.defaultCallAdapterFactory()--->Platform.ExecutorCallAdapterFactory--->ExecutorCallAdapterFactory.ExecutorCallbackCall

3.1 Retrofit没有配置callAdapter时
serviceMethod.callAdapter就是ExecutorCallAdapterFactory.get()返回的CallAdapter实例,然后调用它的adapt会返回一个ExecutorCallbackCall对象。简单看一下这个对象,它是继承于Call的。

static final class ExecutorCallbackCall<T> implements Call<T> {

3.2 Retrofit配置了RxJava2CallAdapterFactory时
serviceMethod.callAdapter就是RxJava2CallAdapter,调用它的adapt会返回一个Observable对象。

到这里就搞清楚了生成Retrofit2.Call或者Observable的整个流程,最后就只剩下一步了,发起网络请求和响应是如何实现的。

4、网络请求的实现
4.1 Call实现网络请求
上面提到过Call对象是通过enqueue()或者execute()发起网络请求的,而这个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) {
      checkNotNull(callback, "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);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

所以,当我们外部调用enqueue()时,实际调用的就是ExecutorCallbackCall.enqueue(),而这里的delegateOkHttpCall,所以最终会由OkHttpCall内部的okhttp3去执行。

这里还有一个关键点callbackExecutor,它的来源如下:

//Platform.MainThreadExecutor.java
static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }

可以看到enqueue的回调结果是在主线程中。

4.2 Observable实现网络请求
从之前的分析中,我们可以知道Observable是在RxJava2CallAdapter内部的adapt()方法返回的:

@Override public Object adapt(Call<R> call) {
    //创建一个发射网络返回数据的Observable
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    //这里为null
    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    //可以转换成Flowable
    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }

关键点在第一行代码,isAsync返回true,所以看一下CallEnqueueObservable的实现


final class CallEnqueueObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallEnqueueObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    CallCallback<T> callback = new CallCallback<>(call, observer);
    observer.onSubscribe(callback);
    call.enqueue(callback);
  }

如果你看过RxJava2的源码的话,应该对这段代码非常熟悉,每个Observable都会去调用subscribeActual()方法。这里通过call.enqueue()去发起网络请求,然后通过CallCallback回调

private static final class CallCallback<T> implements Disposable, Callback<T> {
    private final Call<?> call;
    private final Observer<? super Response<T>> observer;
    boolean terminated = false;

    CallCallback(Call<?> call, Observer<? super Response<T>> observer) {
      this.call = call;
      this.observer = observer;
    }

    @Override public void onResponse(Call<T> call, Response<T> response) {
      if (call.isCanceled()) return;

      try {
        observer.onNext(response);

        if (!call.isCanceled()) {
          terminated = true;
          observer.onComplete();
        }
      } catch (Throwable t) {
        if (terminated) {
          RxJavaPlugins.onError(t);
        } else if (!call.isCanceled()) {
          try {
            observer.onError(t);
          } catch (Throwable inner) {
            Exceptions.throwIfFatal(inner);
            RxJavaPlugins.onError(new CompositeException(t, inner));
          }
        }
      }
    }

    @Override public void onFailure(Call<T> call, Throwable t) {
      if (call.isCanceled()) return;

      try {
        observer.onError(t);
      } catch (Throwable inner) {
        Exceptions.throwIfFatal(inner);
        RxJavaPlugins.onError(new CompositeException(t, inner));
      }
    }

    @Override public void dispose() {
      call.cancel();
    }

    @Override public boolean isDisposed() {
      return call.isCanceled();
    }
  }
}

这里的逻辑也很简单,就是把网络请求的结果发射出去。

到这里,Retrofit2网络执行流程的源码分析就算完了。其实这里的类不算多,逻辑上也比较清晰,但是需要掌握的知识点比较杂,比如入口处的动态代理,中间的OkHttpRxJava2源码。所以,这里建议基础薄弱的同学,看完这些内容之后再回头来看Retrofit2,理解会更加深刻。


总结

流程
1、通过建造者模式创建一个Retrofit实例,也就是做Retrofit相关配置;
2、通过Retrofit对象的creat()方法创建一个代理对象,当调用接口方法时,都会调用动态代理的invoke()方法;
3、invoke()内部做了3件事情:

  • 3.1 会对method进行解析,最后生成ServiceMethod对象,并缓存起来,下次调用就不需要解析了;
  • 3.2 将原始的okhttp3.call封装成OkHttpCall
  • 3.3 通过CallAdapter转换成Call对象或者Observable对象

4、如果返回Call对象,调用execute或者enqueue方法去做网络请求;如果返回Observable对象,则结合rxjava2做后续的操作。

结论:

  • Retrofit是一个封装了网络请求的框架,它本身是不做网络请求的,而是通过Okhttp实现的;

  • Retrofit提供了一种更简洁的发起网络请求的方式,具体来说是通过接口和注解的方式来简化网络请求相关的一系列配置;

  • 可扩展性。比如说Client、数据转换Gson、RxJava适配等。

推荐文章:
Retrofit分析-经典设计模式案例

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