Retrofit 是一个负责网络调度的框架,它负责对 Request 和 Response 的处理,底层的网络层是由 OkHttp 处理。
基本使用示例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")//主机
.build();
GitHubService api = retrofit.create(GitHubService.class);
api.getRepos("valenti").enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
Log.e(TAG, "success!" + gson.toJson(response.body()));
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
Log.e(TAG, "failure!");
}
});
enqueue
为异步函数,excute
为同步函数。
GithubServer 为接口,定义为:
public interface GitHubService {
@GET("users/{user}/repos") //path
Call<List<Repo>> getRepos(@Path("user")String user);
}
Repo 为对应 bean。
源码
Retrofit 的核心为 create
函数,GitHubService.class 这个参数传进经历了什么?一起来看看。
校验过程
内部首先经过 Utils.validateServiceInterface()
方法,这个方法内部逻辑为:
static <T> void validateServiceInterface(Class<T> service) {
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
if (service.getInterfaces().length > 0) {
throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
}
}
首先验证该 service 是否为 Interface,service.getInterfaces().length > 0
用来判定该 Interface 是否继承其他 Interface。
该方法就是对 GitHubService.class 进行简单的结构校验。
接下来为 eagerlyValidateMethods()
函数,该函数的作用是对 GitHubService 的内部声明合法性进行验证,那么为什么需要做一层该配置呢?假如你进行了该配置,那么在你的 Interface(也就是 GitHubService)被创建的第一时间就把所有的验证都做完了,这很利于开发者的调试工作,但是不利于性能。
核心部分
真正的核心部分就是 return
后的代码,完整代码如下:
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
if (method.getDeclaringClass() == Object.class){
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
Proxy.newProxyInstance()
为一个动态代理,这个方法中有三个参数,第一个参数 service.getClassLoader(),表示创建任何类的时候都需要提供一个类加载器用于把类(GitHubService)加载进来,所以我们不需要对这个 ClassLoader 进行任何操作;第二个参数是我们提供的一系列 Interface,然后 newProxyInstance()
方法会把所有的 Interface 全部实现到一个对象里。
但是,它是如何将我们仅仅声明但未曾在任何地方实现的 api 接口给实现了?就是在第三个参数 new InvocationHandler() {...}
中的回调 invoke()
中实现的。
动态代理是为运行时生成的代理类,其模样大概为(借用 GitHubService 的例子):
public class GitHubService extends Proxy implements GitHubService {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
return ...;
}
};
@Override
public Call<List<Repo>> getRepos() {
try {
Method method = GitHubService.class.getMethod("getRepos");
return (Call<List<Repo>>) handler.invoke(this, method, args);
} catch (Throwable e) {
e.printStackTrace();
}
...
return ...;
}
...
}
'...' 部分为其他实现,并且上述代码和动态代理反编译看到的动态代理类结构有偏差,只是大体意思相同。
到这里大概明白了动态代理的工作内容,首先创建一个 GitHubService 接口的实例对象,然后接口中的每一个方法也会实现,然后调用 handler 的 invoke 方法,将 getRepos() 这个方法信息传进去,这体现了 Retrofit 最核心的部分,Interface 中声明的 api 接口就是如此实现的。
也可看出,当调用 api 接口的时候 invoke 方法会被调用一次。
那么 invoke 方法中做了什么?
invoke() 的内部
invoke 方法中第一个判断体 method.getDeclaringClass() == Object.class
首先进行了一次 method(也就是例子中的 getRepo()) 的判断,判断该方法是在哪个类中声明的,假如是 toString()
、hashCode()
... 方法的话不会对它进行任何操作,这并不是 Retrofit 关注的重点。
下面一个判断体 platform.isDefaultMethod(method)
中 platform 为不同的系统平台,不同的系统平台有着不同的行为,isDefaultMethod()
为 Java 8 新特性(众所周知在之前 Java 中的接口是不能直接实现的,必须经由实现该接口的类来实现,Java 8 后允许)。
前两层判断都是为了保证兼容性,不是 Interface(GitHubService)里声明的方法该怎么调用还怎么调用。
接下来 loadServiceMethod()
是 invoke 实际做的内容,getRepo 函数最终的返回为一个我们自己的 Call,那么 Retrofit 中的返回的 Call 是如何转换为我们自定义 Call 的?
源码中 CallAdapter 就是完成这个工作的,追寻下面的足迹得到了 CallAdapter 的对象:
在 nextCallAdapter()
函数中有这样一段代码:
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
callAdapterFactories
的生成为:
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
看到了熟悉的身影 platform
,上面说过不同的平台会有不同的操作行为,其实就是根据不同的平台环境生成对应的 callAdapterFactory,Android 下默认的 callAdapterFactory 为 ExecutorCallAdapterFactory
。
在 ExecutorCallAdapterFactory 中能返回一个 CallAdapter 对象:
@Override public @Nullable 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);
}
};
}
在这里完成了目标 Call 的转换,与其说转换更不如说是适配。 HttpServiceMethod 中的Type responseType
就是适配目标的 Call ,也就是 Call<List<Repo>>。
并且在 ExecutorCallAdapterFactory 中还发现了在使用过程中成功失败的回调:
@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()) {
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);
}
});
}
});
}
在这里有一个代理 callbackExector
是他去执行了请求的成功与失败,在 Android 环境下返回的是 MainThreadExecutor()
,MainThreadExecutor 中的 excute() 做了代理,response 并没有直接在外部的 onResponse 和 onFailure 中处理,而是通过 excute() 交由 handler 去做,在这里将网络代码切换到了前台:
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
在这里整理下思路,callbackExecutor
交给工厂 platform.defaultCallAdapterFactory()
处理,这个工厂创建一个个 CallAdapter
,他会为 Interface(例子中的 GitHubService) 中的每个 api(例子中的 getRepos())创建一个 CallAdapter,也就是如下对应关系:
每个 api 对应一个 Call,每个 Call 对应一个 CallAdapter,CallAdapter 进行了线程的切换,将后台线程切换到前台主线程,当然 CallAdapter 不仅仅有切换线程这一个作用,还能做 RxJava 的适配。
其他细节
Retrofit 结构简单,但是细节处理巧妙又复杂,很值得考究。
HttpServiceMethod 中的 Converter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
这句话的作用是什么?由于 Retrofit 的底层为 OkHttp,最终我们想要的结果是得到 Call<List<Repo>> 而不是 Call<ResponseBody>,转换的过程就是 Converter 来实现。Converter 除了将结果转换为具体的对象,还有有关 Request 相关的转换,假如 GitHubService 中 api 定义如下:
@GET("users/{user}repos")
Call<List<Repo>> getRepos(@Path("user") Integer user_id);
若参数的拼接需要将 Integer 转换成 String 才能进行,那么 Converter 可以完成这项工作。
或者上传图片:
@POST("users/{user}repos")
@Multipart
Call<User> uploadAvatar(@Path("user") Integer user_id, @Part("image")File file);
file 转换成 requestBody 的过程也能通过 Converter 处理。
方法注解的解读
RequestFactory 为一个专门负责 Request 处理的工厂,在调用 HttpServiceMethod 的构造方法的时候,创建 RequestFactory 工厂实例,通过私有函数 parseMethodAnnotation()
对 api 注解进行解析。
在这里有意思的是,在这里会判断方法用的是否规范,比如 api 为 GET 但是却又在里面加了 Body,它会报错提示。在这里可以看出,Retrofit 的使用对 HTTP 规范有更高的要求,同时能让开发者更简便的去使用 Retrofit。
参数注解的解读
同时 Request 的 Parameter 也是在 RequestFactory 中处理,调用路径为:
在这里可以看到解读每一个 parameter 的 annotation,如 Url、Path ... 解析完成后这些参数的 annotation 会被暂存起来,目的是为了创建目标 Call(例子中的 Call<List<Repo>>)用。
所以回到最初的 loadServiceMethod()
它的作用就是对 GitHubService 中定义的 api 进行解读罢。
enqueue() 的解读
其关键部分在于:
call = rawCall = createRawCall();
首先这个 call 是 Retrofit 中的 Call —— OkHttpCall,是外层的一个包装,真正进行网络请求的是 OkHttp,在这里得到的就是 OkHttp 的Call:
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
这也就是说,Retrofit 不做网络请求,它的请求层工作是 OkHttp 来完成,Retrofit 负责包装、解析、转换、适配等一些列操作。
注意:上文说的 OkHttp 的 Call 不是 OkHttpCall。
网络请求的工作为:
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) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
这个 call 为 OkHttp 的 Call,在这里调用 OkHttp Call 的 enqueue (),然后对 response 进行解析,然后交给 callback,这个 callback 可能是开发者传的,也可能是 callAdapter。
parseResponse()
在这个函数中,更加能体现 Retrofit 和 OkHttp 的关系,关于状态码的判断:
if (code < 200 || code >= 300) {
try {
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
除 2xx 全都报错,301、302 等的自动跳转 OkHttp 会处理,Retrofit 不会处理。
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
204、205 会返回一个空对象。
关于解析的操作在:
T body = responseConverter.convert(catchingBody);
如我们想要什么类型,它就解析成什么类型,如示例 GitHubService getRepos() 返回 Call 中的 List<Repo> 类型(是 List<Repo> 不是 Call<List<Repo>>)。
所以 enqueue() 内部的工作流程为:
关于 RxJava 的适配
首先:
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
然后:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("your host")//主机
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
这里添加的 Adapter 并不会影响前面:
@GET("users/{user}repos")
Call<List<Repo>> getRepos(@Path("user") String user);
这段代码的正常适配,若还有:
@GET("users/{user}repos")
Observable<List<Repo>> getReposRx(@Path("user") String user);
能够正确适配到 Observable 中,因为 Retrofit 内部有多个 Adapter,那再来贴一次 nextCallAdapter()
函数中的代码:
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
可发现 callAdapterFactories 为 List 型,即是多个 Adapter 并存的关系,而不是后来者替换的关系。
前面提到 CallAdapter 配合 callbackExecutor 进行了线程的切换,而用了 RxJava 的适配呢,则是另外一套实现,具体的实现逻辑在 RxJava2CallAdapter
的 adapt()
函数当中。