Retrofit源码分析三 源码分析

Retrofit源码分析三 源码分析

使用方法

我们先来看一下Retrofit的常见使用方法:

//创建网络请求接口类
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

//创建Retrofit实例对象
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

//通过动态代理创建网络接口代理对象
GitHubService service = retrofit.create(GitHubService.class);

//获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

//执行同步请求或异步请求
repos.execute();
repos.enqueue(callback)

上面是Retrofit的最基本使用方法,当然现在使用最多的还是RxJava2+Retrofit搭配使用,关于RxJava2,大家可以看我的另一篇 RxJava2源码分析 ,当然RxJava2与Retrofit搭配使用的解析我会在稍后分析,这里我们先关注最基本的使用方法。

创建网络接口类

这一步的目的就是封装我们网络请求相关的一些参数,没什么好多说的。

创建Retrofit实例对象

Retrofit实例对象的创建很明显是采用了Builder模式,Builder模式在 Java语言中 创建一个 有很多可选配置参数 的对象的时候是很好的一种设计模式。Builder模式有两个重点,一个是在 Java语言中 中,另一个是 有很多可选配置参数,其实在现在的Android开发中,使用Kotlin开发Android已经很普遍了,熟悉Kotlin语法的小伙伴可能很熟悉了,由于Kotlin中 默认参数 的存在,所以在Kotlin中使用Builder模式的意义不大。但由于Java语法的限制,在创建一个 有很多可选配置参数 的时候,Builder模式还是首要选择。

我们来看一下Retrofit中的成员变量:

public final class Retrofit {
  //缓存封装好的ServiceMethod
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
  //OKHttp中的网络请求工厂
  final okhttp3.Call.Factory callFactory;
  //BaseUrl
  final HttpUrl baseUrl;
  //数据转换器工厂集合
  final List<Converter.Factory> converterFactories;
  //网络请求适配器工厂集合
  final List<CallAdapter.Factory> callAdapterFactories;
  //处理线程切换
  final @Nullable Executor callbackExecutor;
  //不需要关注,默认为false
  final boolean validateEagerly;
  
  //忽略无关代码......
  }

serviceMethodCache 是一个HashMap,它的Key是Method,代表我们定义的网络请求接口类中的方法,它的Value是ServiceMethod,代表对网络请求接口类中的方法的一个封装,简单看一下它就明白了:

final class ServiceMethod<R, T> {

  private final okhttp3.Call.Factory callFactory;
  private final CallAdapter<R, T> callAdapter;

  private final HttpUrl baseUrl;
  private final Converter<ResponseBody, R> responseConverter;
  private final String httpMethod;
  private final String relativeUrl;
  private final Headers headers;
  private final MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  private final ParameterHandler<?>[] parameterHandlers;
  
  //忽略无关代码.......
  }

很明显了,ServiceMethod就是对网络请求参数的封装类,包含请求头、相对url、GET请求或者是POST请求等等对请求的配置信息。serviceMethodCache 就是一个对ServiceMethod的缓存,可以提高一定的效率。

在Retrofit中,默认的数据转换器工厂就是 GsonConverterFactory ,所以其实如果我们是使用 Gson 来做数据转换的,其实没有必要去配置。在Android平台,Retrofit中默认的网络请求适配器工厂是 ExecutorCallAdapterFactory ,我们简单瞅一眼它是如何被创建的:

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      //注意这里创建了一个位于主线程的Handler
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        //通过handler.post(runnable)实现线程切换
        handler.post(r);
      }
    }
  }

可以看到,在创建 ExecutorCallAdapterFactory 的同时传入了一个 callbackExecutor ,这个 callbackExecutor 也是Retrofit中默认的 callbackExecutor ,在Android平台中它是 MainThreadExecutor类型 ,可以看到,在它的内部创建了一个位于主线程的Handler。我们知道,使用Retrofit的时候不同于直接使用OKHttp,在使用Retrofit的异步网络请求时,网络结果的回调是位于主线程中的,那么Retrofit是如何切换的线程,看过上面的代码应该会心里有个数了,它是通过 handler.post(r) 来实现由子线程到主线程的切换。

通过动态代理创建网络接口代理对象

看过我的Retrofit源码分析第二篇:代理模式的小伙伴们应该都比较清楚了,在Retrofit的 create 方法中其实是使用了动态代理生成了一个代理对象,现在我们就来看一下 create 的源码:

  public <T> T create(final Class<T> service) {
    //忽略无关代码......
    
    //下面就是JDK给我们提供的动态代理了
    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 {
            //忽略无关代码......
            
            //获取method的网络请求参数的封装类ServiceMethod对象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
                
            //获取对OKHttp中的RealCall的一个包装类OkHttpCall对象
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //通过网络请求适配器将原始的Call对象转换成需要的对象,比如RxJavaCallAdapter会将Call对象转换成Observable。
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

create 方法中做了3件事:

  • 封装网络请求方法
  • 封装原始Call对象
  • 通过CallAdapter转换Call对象
    封装网络请求方法很容易理解,关于ServiceMethod上面已经说过,不再赘述。封装原始Call对象,对OKHttp源码熟悉的小伙伴应该知道,原始的Call其实就是OkHttp中的ReallCall。如果不熟悉OkHttp的同学可以看我之前的一篇 OkHttp源码分析 。我们可以简单看一下这个包装类 OkHttpCall :
final class OkHttpCall<T> implements Call<T> {

    //忽略无关代码......    
    
    @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    //熟悉OkHttp源码的同学应该很熟悉了,完全照抄OkHttp中的代码,确保每个Call只会被执行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      //忽略无关代码......
    
      call = rawCall;
      if (call == null) {
        try {
          //创建OkHttp中的RealCall对象
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }
    //解析OkHttp中的RealCall执行同步方法后返回的网络数据
    return parseResponse(call.execute());
  }
  
  
  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    //确保一个Call对象只被执行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          //创建OkHttp中的RealCall对象
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
    
    //调用OkHttp中的RealCall的异步请求网络方法
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }
}

可以看到,Retrofit中的这个 OKHttpCall 完全是对OkHttp中的 RealCall 的一个包装。在 OKHttpCall 中的同步网络方法 execute 和异步网络方法 enQueue 中其实就做了三件事:获取 RealCall 对象,调用 RealCall 中的 execute 或者 enQueue 方法,然后去解析OkHttp返回的原始数据并转换成我们需要的类型,就这么简单。

接下来就 create 方法中就只剩下 通过CallAdapter转换Call对象 了,我们上面说过,在没有特别配置CallAdapter的时候,默认的CallAdapterFactory是 ExecutorCallAdapterFactory ,很明显,CallAdapter是由Factory创建的,那我们看一下这个默认的CallAdapterFactory:

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
    
  //本质是传入的MainThreadExecutor
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    
    //直接创建并返回一个CallAdapter的匿名对象
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        //返回默认的CallAdapter转换后的Call对象
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  static final class ExecutorCallbackCall<T> implements Call<T> {
    //本质是传入的MainThreadExecutor
    final Executor callbackExecutor;
    //就是OKHttpCall,从名字也能理解,代理Call对象
    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");

       //调用OkHttp中RealCall的异步网络请求
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
            //注意,OkHttp的异步回调是在子线程的,Retrofit这里实现了线程的切换,本质是调用了handler.post(runnable)
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
            //注意,OkHttp的异步回调是在子线程的,Retrofit这里实现了线程的切换,本质是调用了handler.post(runnable)
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

    @Override public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override public Response<T> execute() throws IOException {
       //由于同步方法不需要切线程,所以直接执行并返回OKHttpCall的同步网络方法
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

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

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }

    @Override public Request request() {
      return delegate.request();
    }
  }
}

上面的代码其实也很清晰了 ExecutorCallAdapterFactory 这个CallAdapter工厂类直接创建并返回一个CallAdapter的匿名对象,这个其实就是我们Retrofit的默认CallAdapter,关键是这个CallAdapter的adapt方法,它返回了 ExecutorCallbackCall 这个对OkHttpCall的包装类,我们关注这个包装类的 enqueue 方法,在这个方法中,它通过调用 callbackExecutor.execute 来实现了子线程到主线程的线程切换。这个 callbackExecutor 就是 MainThreadExecutor ,这个类我们上面提到过, MainThreadExecutor 中的 execute 方法内部就是 handler.post(r); ,这个 handler 其实就是主线程的,因此实现了线程切换。

获取Call对象并执行同步或异步方法

其实经过上一步的分析,我们已经知道了,我们获取的Call对象,其实是通过动态代理中 serviceMethod.adapt(okHttpCall) 返回的Call对象,这个其实就是 ExecutorCallbackCall ,这个我们都很清楚了,它是对OkHttp中RealCall的一个包装类,在它的异步方法中通过调用 callbackExecutor.execute 实现了线程的切换,当然本质还是通过 Handler 机制。

其实到此为止,基本的流程已经分析完毕了,我们到这里也会很清楚Retrofit的定位:Retrofit并不是一个网络请求的框架,而是一封装网络请求参数,解析网络返回结果的框架。下面我们来分析一下使用了RxJava2CallAdapter的情况。

RxJava2CallAdapter的原理

CallAdapter的重点是它的adapt方法,我们来看一下RxJava2CallAdapter中的adapt方法:

@Override public <R> Object adapt(Call<R> call) {
    //创建一个发射网络返回结果的Observable
    Observable<Response<R>> responseObservable = new CallObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }
    
    //默认scheduler为空
    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }

这段代码本质是创建一个发射网络返回结果的Observable,我们看一下 CallObservable 的内部实现,看过RxJava2源码的同学应该都知道,Observable的关键方法是 subscribeActual,我们就看一下这个方法内部都做了什么:

@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();
    observer.onSubscribe(new CallDisposable(call));

    boolean terminated = false;
    try {
      //调用了OKHttpCall的同步网络访问方法,并获取网络数据
      Response<T> response = call.execute();
      if (!call.isCanceled()) {
        //将获取到的网络数据发送到观察者observer
        observer.onNext(response);
      }
      if (!call.isCanceled()) {
        terminated = true;
        //发送结束事件
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(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));
        }
      }
    }
  }

熟悉RxJava2的同学看完就应该懂了:RxJava2CallAdapter通过adapt方法生成并返回一个 CallObservable 对象,在这个 CallObservable 内部通调用 OKHttpCallexecute() 方法进行网络访问,并将获取到的数据发送到下一级的观察者observer中。

到此为止,Retrofit的源码分析终于结束了,其实它并不难,但想要完全理解整个网络访问流程,除了要明白Retrofit,还需要了解OKHttp甚至是RxJava,对后两者不太熟悉的小伙伴们可以看我的另外两篇源码分析:OkHttp源码分析RxJava2源码分析 ,在熟悉了OKHttp和RxJava2的基础上再回过头来看Retrofit,相信你会有更深的理解。

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

推荐阅读更多精彩内容