版本号:2.5.0
一.基本使用
1.定义请求接口
interface GithubService {
//通过注解定义请求的方法以及路径,“{}”里面的表示:该内容是可变的,通过下面方法的参数赋值
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<Any>
}
2.创建Retrofit对象,通过Call对象发送网络请求
//创建Retrofit对象
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())//设置数据解析器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//设置支持Rxjava平台
.build()
//创建网络请求接口实例
val githubService = retrofit.create(GithubService::class.java)
//调用相应的接口获取对应的call对象
val call = githubService.listRepos("user")
//通过call对象执行异步请求
call.enqueue(object : Callback<Any> {
override fun onFailure(call: Call<Any>, t: Throwable) { }
override fun onResponse(call: Call<Any>, response: Response<Any>) { }
})
通过上面的代码可以看出,真正发送网络请求的是Call对象,也就是Okhttp,Retrofit只是对网络请求参数的封装,真正的请求是通过Okhttp完成的。
- 应用通过Retrofit发送网络请求,实际上是使用Retrofit接口层封装请求参数,之后由Okhttp完成后续的请求操作,在服务端返回数据之后,Okhttp将原始的结果交给Retrofit,Retrofit根据用户的需求对结果进行解析。
二.源码分析
1.创建对象
网络请求参数的封装是通过Retrofit完成的,所以先看一下给对象的创建代码:
Retrofit.Builder()
public static final class Builder {
//Retrofit支持平台
private final Platform platform;
//网路请求的okhttp的工厂,默认就是OkhttpClient
private @Nullable okhttp3.Call.Factory callFactory;
//请求的基地址
private @Nullable HttpUrl baseUrl;
//数据转换器工厂集合;数据转换器就是将从网络获取的数据转成java对象
private final List<Converter.Factory> converterFactories = new ArrayList<>();
//适配器工厂集合;适配器工厂:将我们Call对象转换成其他类型能用的请求,比如:Rxjava
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
//用于执行异步回调的,在Android是默认在主线程中回调
private @Nullable Executor callbackExecutor;
//标志位,用于后面是否立即解析方法参数
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//其他的成员变量判断
......
//创建Retrofit对象,该对象中也有对应的成员变量,除了platform
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
}
重点关注一下成员变量,如注释所示。在上述代码中
Builder()
会调用Builder(Platform platform)
传入一个Platform对象,下面看一个该对象创建的代码:Platform.get()
class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
try {
//加载指定的类
Class.forName("android.os.Build");
//如果Build.VERSION.SDK_INT != 0,说明是Android平台
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} ......
try {
Class.forName("java.util.Optional");
return new Java8();
}......
return new Platform();
}
从上述代码可以看出,Retrofit支持Android和Java8平台。这里我们只关注Android平台,下面看一下Android中做了什么。
static class Android extends Platform {
......
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
......
@Override List<? extends Converter.Factory> defaultConverterFactories() {
return Build.VERSION.SDK_INT >= 24
? singletonList(OptionalConverterFactory.INSTANCE)
: Collections.<Converter.Factory>emptyList();
}
......
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
Android中提供了默认的Executor、ConverterFactor和CallAdapterFactory,其中默认的异步异步回调处理是在AndroidUI线程处理的
new Handler(Looper.getMainLooper());
,这也就是为什么Retrofit网络请求回调是在主线程中执行的(Okhttp的回调是在子线程中执行的)
2.参数设置
baseUrl()
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
//HttpUrl对baseUrl进行解析分段,如获取:scheme、host、port和PathSegments
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
//baseUrl的必须以“/”结尾
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
注意:baseUrl的必须以“/”结尾,否则抛出异常IllegalArgumentException(baseUrl must end in /:)
addConverterFactory()
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
该方法很简单,就是把设置的工厂添加到对应的集合中,重点看一下Converter.Factory对象的创建:
GsonConverterFactory.create()
:
public static GsonConverterFactory create() {
return create(new Gson());
}
public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
private GsonConverterFactory(Gson gson) {
this.gson = gson;
}
创建的过程就是创建一个Gson对象,然后给GsonConverterFactory的属性
gson
赋值。
addCallAdapterFactory()
该方法同
addConverterFactory()
类似,将Factory对象添加到相应的集合中。RxJava2CallAdapterFactory.create()
方法也类似,创建一个RxJava的Scheduler对象并赋值。
3.获取请求接口实例
retrofit.create(GithubService::class.java)
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();
private final Object[] emptyArgs = new Object[0];
//proxy:代理对象
//method:调用的方法信息
//args:调用方法的参数
//当代理对象方法调用的时候就会调用该方法
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// 如果调用的是Object类中的方法,比如:toString()方法等
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//如果是平台默认的方法,Android平台该方法返回false,所以if条件不成立。
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//最终会调用该方法
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
在Android平台下调用请求方法时,是通过动态代理来实现的,在这个过程中最终会调用
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
我们先看一下loadServiceMethod(method)
方法
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
loadServiceMethod
方法就是返回了一个ServiceMethod对象,该对象是对请求方法method的封装,如果缓存中存在就从缓存中获取,否则直接创建一个:ServiceMethod.parseAnnotations(this, method);
。ServiceMethod是一个抽象类,调用的是它子类HttpServiceMethod的parseAnnotations
方法,在该方法中直接new HttpServiceMethod对象,下面就看一下该类中的相关属性和方法:
final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
......
//请求工厂,里面封装着请求方法先关的信息
private final RequestFactory requestFactory;
//请求工厂:其实就是OkhttpClient
private final okhttp3.Call.Factory callFactory;
private final CallAdapter<ResponseT, ReturnT> callAdapter;
private final Converter<ResponseBody, ResponseT> responseConverter;
......
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
}
下面重点看一下RequestFactory:
final class RequestFactory {
......
private final Method method;
private final HttpUrl baseUrl;
final String httpMethod;
private final @Nullable String relativeUrl;
private final @Nullable Headers headers;
private final @Nullable MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
//参数解析器
private final ParameterHandler<?>[] parameterHandlers;
......
}
从RequestFactory的成员变量可以看出该类中包括请求相关的所有信息。
创建好ServiceMethod对象以后,调用该对象的invoke
方法:
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
该方法就会返回一个Call对象,
allAdapter.adapt()
方法就是将OkHttpCall对象转换成其他平台能用的Call对象,比如:RxJava。这里返回的是OkHttpCall的对象,该类是Retrofit中定义的,是对Okhttp中的Call对象的封装。下面看一下该类中的相关属性和方法:
final class OkHttpCall<T> implements Call<T> {
private final RequestFactory requestFactory;
private final Object[] args;
private final okhttp3.Call.Factory callFactory;
private final Converter<ResponseBody, T> responseConverter;
private volatile boolean canceled;
@GuardedBy("this")
private @Nullable okhttp3.Call rawCall;
......
//异步请求方法
@Override public void enqueue(final Callback<T> callback) {
......
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();
}......
}
}
......
//调用Okhttp-》call的enqueue方法,完成网络请求
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//解析响应数据
response = parseResponse(rawResponse);
}......
}
}
......
//创建Call对象:okHttpClient.newCall(request)
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
......
return call;
}
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
//code判断
if (code < 200 || code >= 300) {
try {
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
//转换成实体类
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
}......
}
}
OkHttpCall对Okhttp的Call封装了一下,它里面的
enqueue
方法其实调用的就Call.enqueue,在它的回调成功方法中,通过配置的Converter.Factory将响应数据转换成对应的实体类(Java对象)。还有一个需要关注的地方就是,Requst的创建,是通过requestFactory.create(args)
创建的,下面看一下该方法:
final class RequestFactory {
......
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
//方法参数解析器,解析接口中定义的方法
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
//调用方法传入的参数
int argumentCount = args.length;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
//通过Builder模式创建Request对象
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
headers, contentType, hasBody, isFormEncoded, isMultipart);
List<Object> argumentList = new ArrayList<>(argumentCount);
//设置请求参数
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.get()
.tag(Invocation.class, new Invocation(method, argumentList))
.build();
}
......
}
该方法就是根据ParameterHandler解析的参数以及传入的参数值args创建Request对象。
总结
在调用Retrofit的create
方法获取接口请求实例Call对象,内部使用的是动态代理的方式,在调用相应的网络请求方法的时候,会回调invoke
方法,在该方法中会调用ServiceMethod()的invoke
方法。
ServiceMethod是一个抽象类,实际使用的是HttpServiceMethod,该类是对请求方法信息的封装,里面有几个比较重要的成员变量:
- 1.RequestFactory :包含请求相关的信息
private final Method method;
private final HttpUrl baseUrl;
final String httpMethod;
private final @Nullable String relativeUrl;
private final @Nullable Headers headers;
private final @Nullable MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
//参数解析器
private final ParameterHandler<?>[] parameterHandlers;
- 2.okhttp3.Call.Factory
- 3.callAdapter
- 4.responseConverter
ServiceMethod.invoke的方法会返回一个OkHttpCall对象,该对象是对OkHttp中Call的封装。真正的网络请求是通过OkHttp中Call来实现的。
三 总结
Retrofit底层是基于Okhttp实现的,使用运行时注解的方式
1、 原理
通过java接口以及注解来描述网络请求,并用动态代理的方式生成网络请求的request,然后通过client调用相应的网络框架(默认okhttp)去发起网络请求,并将返回的response通过converterFactorty转换成相应的数据model,最后通过call adapter转换成其他数据方式(如rxjava Observable)
2、 Retrofit流程
(1)通过解析 网络请求接口的注解 配置 网络请求参数
(2)通过 动态代理 生成 网络请求对象
(3)通过 网络请求适配器 将 网络请求对象 进行平台适配
(4)通过 网络请求执行器 发送网络请求
(5)通过 数据转换器 解析服务器返回的数据
(6)通过 回调执行器 切换线程(子线程 ->>主线程)
(7)用户在主线程处理返回结果
3、 Retrofit优点
1.可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;
2.请求的方法参数注解都可以定制;
3.支持同步、异步和RxJava;
4.超级解耦;
5.可以配置不同的反序列化工具来解析数据,如json、xml等
6.框架使用了很多设计模式
网友评论