RikkaTheWorld 2019-06-27
我们先来看一下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实例对象的创建很明显是采用了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件事:
封装网络请求方法很容易理解,关于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对象,其实是通过动态代理中 serviceMethod.adapt(okHttpCall)
返回的Call对象,这个其实就是 ExecutorCallbackCall ,这个我们都很清楚了,它是对OkHttp中RealCall的一个包装类,在它的异步方法中通过调用 callbackExecutor.execute
实现了线程的切换,当然本质还是通过 Handler 机制。
其实到此为止,基本的流程已经分析完毕了,我们到这里也会很清楚Retrofit的定位:Retrofit并不是一个网络请求的框架,而是一封装网络请求参数,解析网络返回结果的框架。下面我们来分析一下使用了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 内部通调用 OKHttpCall 的 execute() 方法进行网络访问,并将获取到的数据发送到下一级的观察者observer中。
到此为止,Retrofit的源码分析终于结束了,其实它并不难,但想要完全理解整个网络访问流程,除了要明白Retrofit,还需要了解OKHttp甚至是RxJava,对后两者不太熟悉的小伙伴们可以看我的另外两篇源码分析:OkHttp源码分析 , RxJava2源码分析 ,在熟悉了OKHttp和RxJava2的基础上再回过头来看Retrofit,相信你会有更深的理解。