目录
前言
怎样用Retrofit请求网络
Retorfit是如何创建的
请求接口的实例是如何创建出来的
Call到底是什么
5.1 ServiceMethod的创建
5.2 Call的创建
5.3 总结
Call的enqueue方法到底干了什么
遗留
1.前言
近些天回想起工作这几年,感觉一事无成。俗话说,就算是咸鱼,也要做一条有理想的咸鱼。因此,订了一个长期的计划:博客。
为何选择Retrofit作为人生中的第一篇博客?
- Retrofit太火了,在最新的2017Android框架中Retrofit排名第一
- Retrofit是对OkHttp的封装。作为一个封装库,它用了大量的设计模式,有很多值得借鉴的地方
- 代码量少,相对简单
在读这篇文章前,假设你已经掌握了
- Retrofit如何使用
分析源码一般都有一根主线,本文打算从Retrofit最基本的用法开始,一步一步深入了解Retrofit全貌。
2.怎样用Retrofit请求网络
一般我们使用Retrofit需要以下五步:
- 定义请求接口
- 创建Retrofit实例
- 获取请求接口的实例
- 获取Call对象
- 同步或异步请求
//1.定义请求接口
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
//2.创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
//3.通过Retrofit实例获取请求接口实例(动态代理)
GitHubService service = retrofit.create(GitHubService.class);
//4.获取Call对象
Call<List<Repo>> call = service.listRepos("octocat");
//5.异步请求
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
//doSometionSuccess
...
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
//doSometionError
...
}
});
我们将会以上述代码为导火索来分析源码。针对上述代码我们先提出以下几个问题,分别对应代码注释中的2 3 4 5对应的代码:
- Retrofit是如何创建的
- 请求接口的实例是如何创建出来的
- Call到底是什么
- Call的enqueue方法到底干了什么
我会在每节的开始处附上每个问题对应的代码
3.Retorfit是如何创建的
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
只要稍微了解Build模式的童鞋都能一眼看出来,Retrofit是通过Build模式构建的。关于Retrofit中的那些设计模式,我打算另起篇幅介绍,这里就不叙述了。
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
private final Platform platform;
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
这里我先简单介绍一下各个参数的作用:
- callFactory,前面介绍了Retrofit是对OkHttp的封装,callFactory是Okhttp3提供的类,用来自定义OkHttpClient()。
- callbackExecutor,请求完成后,用来对回调过程进行线程控制。默认是在主线程中回调。
- adapterFactories,它是CallAdapter.Factory的集合。CallAdapter.Factory是一个工厂,用来生产callAdapter,callAdapter是对OkHttpCall的适配。至于OkHttpCall是什么,我们会在下面的篇幅中会介绍。随便提一句,Retrofit集成RxJava就是通过CallAdapter.Factory实现的。
- converterFactories,它与adapterFactories类似。不同的是,它生产的Converter可以控制请求结果的解析。例如,进行JSON还是XML解析。
- validateEagerly默认为false,validateEagerly参数如果我们在配置的时候设置为true的时候,程序是在做一个预处理的操作,会提前解析请求接口中定义的方法,生产MethodHandler并缓存。
- Platform,Retrofit希望是一个跨平台的网络请求库,因此它定义了Platform接口。每个平台都提供了默认的callbackExecutor与CallAdapter.Factory实现。
platform源码
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 {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
4.请求接口的实例是如何创建出来的
GitHubService service = retrofit.create(GitHubService.class);
我们看一下Retrofit的create方法。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//动态代理,返回的是T类型的对象。
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);
}
});
}
我们先暂时不去分析eagerlyValidateMethods(service)方法,因为它看起来即使validateEagerly为false,我们的程序仍能正常运行。
Proxy.newProxyInstance()生成了一个动态代理对象,而InvocationHandler可以拦截这个对象的所有方法。这么说可能有些抽象,你可以简单的认为以下这两段代码等价。
GitHubService service = retrofit.create(GitHubService.class)
GitHubService service = new GitHubService() {
//创建InvocationHandler对象
InvocationHandler invocationHandler = 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);
}
};
//调用listRepos方法其实是调用InvocationHandler的invoke方法,参数分别为:当前GitHubService对象,listRepos()方法对象,listRepos()传入的参数
@Override
public Call<List<String>> listRepos(@Path("user") String user) {
return invocationHandler.invoke(this, GitHubService.class.getMethod("listRepos"), user);
}
}
5.Call到底是什么
Call<List<Repo>> call = service.listRepos("octocat");
service.listRepos()方法其实调用了invocationHandler的invoke()方法。其核心代码仅有三行。
//ServiceMethod的创建
ServiceMethod serviceMethod = loadServiceMethod(method);
//Call的创建
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
这部分将分为两部分介绍,ServiceMethod的创建和Call的创建。
5.1 ServiceMethod的创建
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
loadServiceMethod是为了创建ServiceMethod,然后将其放入的缓存中,缓存的Key为Method对象。ServiceMethod同样是通过Builder模式创建的。上述方法中的this指的是当前Retrofit对象。因此可以推测ServiceMethod.Builder持有了Retrofit对象和Method对象的引用。事实也的确如此,如下:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
这段代码主要是保存了Retrofit的实例对象,method对象,method上的注解,method参数注解,method参数类型。
public ServiceMethod build () {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
responseConverter = createResponseConverter();
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);
}
//...
return new ServiceMethod<>(this);
}
createCallAdapter主要是通过Builder中保存的retrofit对象,找到CallAdapterFactory集合,遍历集合,根据Method的返回值类型和Method上的注解,找到合适的callAdapter。callAdapter是一个接口,定义了两个方法:Type responseType(),返回值类型;<R> T adapt(Call<R> call),用来返回代理对象。我们看一下Platform中默认提供的CallAdapterFactory。
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
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);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
}
这样ServiceMethod内部就创建了一个匿名的CallAdapter对象。
createResponseConverter与createCallAdapter过程类似,不再赘述。build()方法中其余的代码主要是通过解析注解和拼接参数来生成Http请求数据,如:get、post、delete等方法、请求Url、参数。至此,ServiceMethod中拥有了callAdapter、responseConverter以及和网络请求有关的url、parameter、method等。loadServiceMethod介绍完毕。
5.2 Call的创建
我们看一下这两段代码的意思
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
OkHttpCall是Call的实现类,它有一个ServiceMethod对象和一个请求参数的数组。如下:
final class OkHttpCall<T> implements Call<T> {
//构造方法
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
}
我们在 ServiceMethod的创建 中介绍了serviceMethod.callAdapter是一个匿名类。它的adapt方法返回了一个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;
}
这是典型的装饰器模式,ExecutorCallbackCall是对OkHttpCall对象的装饰。它内部的delegate对象就是上面分析的OkHttpCall对象。callbackExecutor是我们在** Retorfit是如何创建的 **中介绍的callbackExecutor对象。Platform提供了默认实现MainThreadExecutor。MainThreadExecutor很简单,它将接收到的Runnable对象交给主线程的Handler去处理。
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
5.3 总结
我们返回的call对象其实是一个ExecutorCallbackCall对象,它实现了Call接口,是对OkHttpCall的装饰,并且内部持有了callbackExecutor对象。OkHttpCall也实现了Call接口,它有持有了一个ServiceMethod对象和一个请求参数的数组的引用。ServiceMethod封装了请求的细节,如get、post、delete等方法、请求Url、参数等,同时callAdapter和responseConverter也是在ServiceMethod中创建的。
6.Call的enqueue方法到底干了什么
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
//doSometionSuccess
...
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
//doSometionError
...
}
});
在上节中,我们知道了返回的Call实例是ExecutorCallbackCall类型的对象。那我们看一下ExecutorCallbackCall的enqueue()方法。
public void enqueue(final Callback<T> callback) {
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(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, t);
}
});
}
});
}
我们先来介绍一下这段代码中出现的变量,然后再来分析整段代码的含义。
- delegate是上一节介绍的OkHttpCall对象。
- callbackExecutor是上一节介绍的MainThreadExecutor对象。
这段代码更能说明ExecutorCallbackCall采用了装饰模式,它生成了一个新的Callback对象。这个新Callback有两个作用。一,将回调切换到callbackExecutor中执行。二,增加了取消操作。最终,这个新的Callback对象交给OkHttpCall对象的enqueue方法去处理。
public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
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);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
OkHttpCall是对Okhttp3.Call的包装,OkHttpCall内部有一个类型为Okhttp3.Call的rawCall对象。rawCall的创建,是通过Retrofit初始化时,传入的OkHttpClient创建的(若为空,系统提供了默认的)。在enqueue方法中利用rawCall对象发起请求。请求成功后,会通过parseResponse(rawResponse)方法对原始数据进行解析。
而parseResponse()的核心代码只有一句话。
T body = serviceMethod.toResponse(catchingBody);
不错,它调用了serviceMethod的toResponse方法。而serviceMethod的toResponse方法也只有一句话。
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
还记得我们在 ServiceMethod的创建 提到的,和callAdapter创建过程类似的responseConverter吗?没错,就是它。
7.遗留
我们在** 请求接口的实例是如何创建出来的 ** 这节中遗留下一段代码
if (validateEagerly) {
eagerlyValidateMethods(service);
}
我们现在回过头来看一下这段代码
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
loadServiceMethod是不是很熟悉。不要告诉我你已经忘了!!! 好吧,那我来告诉你,ServiceMethod的创建 中我们最先介绍的就是这个方法。eagerlyValidateMethods是一个对service中的方法提前解析,将其缓存的过程。所以我们可以通过设置validateEagerly=true来提高请求效率。
哇,终于完了,写博客是真的累啊
真的累啊
真累啊
累啊
累!!!
网友评论