这篇文章主要是对Retrofit的请求过程从源码的角度上做个分析,来了解为什么只要定义一个接口,然后就能完成网络请求,看看Retrofit帮我们做了什么事情。
创建请求接口
public interface NewsService{
@GET("news/latest")
Observable<News> getLatest();
}
这里顶一个接口,不需要再定义一个实现类。待会直接使用Retrofit的create创建一个实例即可。
创建Retrofit实例
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(HOST_NAME)
.client(okHttpClient)
.addConverterFactory(LoganSquareConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
首先介绍一下这里使用了Builder模式,设置baseUrl,client等等。一般请求下我们整个app中保持一个Retrofit实例即可,我们可以自己弄一个单例。
创建服务实例
newsService = RetrofitClient.getInstance().create(NewsService.class);
通过这么一句话就可以创建一个实例,但是我们并没有创建NewsService的实现类,OK,我们看下Retrofit的create方法帮我们做了些什么
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();
@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);
}
});
}
其实这里使用动态代理技术,使用Proxy生成一个实例,在执行实例的方法时会调用InvocationHandler的inovke方法。
首先invoke方法会调用loadServiceMethod()返回一个ServiceMethod,从方法名字我们大概可以猜出来ServiceMethod是一个方法的描述实例。我们直接来看下ServiceMethod.Builder的build方法。
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
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);
}
1、首先调用createCallAdapter根据当前方法的返回类型返回一个CallAdapter,不同的返回类型当然有不同的callAdapter
2、调用createResponseConverter方法返回一个Converter,其实这个也是根据返回类型创建的,我们创建的时候设置的ConverterFactory为LoganSquareConverterFactory所以,会返回LoganSquareResponseBodyConverter
3、调用parseMethodAnnotation处理方法的注解GET、POST等。
4、处理方法参数根据参数的注解生成ParameterHandler,当然不同的参数注解生成的实例类型也不同,具体可参看ParameterHandler源码。
其实这里使用了一部分的反射,这样势必会降低程序运行的效率,尽管使用了缓存策略,只有第一个才会这样。可以考虑假如apt,在编译时期,处理这些事情
有一篇博客对这块写的还是蛮不错的,在这了记录一下
图解 Retrofit - ServiceMethod,作者的图画的太棒了。
获取ServiceMethod实例之后,创建了一个OkHttpCall实例,返回serviceMethod.callAdapter.adapt(okHttpCall)。前面分析了这里的callAdapter其实就是SimpleCallAdapter,我们可以直接看这个类的adapt方法。
@Override public <R> Observable<R> adapt(Call<R> call) {
Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
.flatMap(new Func1<Response<R>, Observable<R>>() {
@Override public Observable<R> call(Response<R> response) {
if (response.isSuccessful()) {
return Observable.just(response.body());
}
return Observable.error(new HttpException(response));
}
});
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
这里使用CallOnSubscribe创建了一个Observable实例。
在分发的时候,会调用CallOnSubsrcibe的call方法,call方法内部又会调用OkHttpCall类的execute方法
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
首先会调用createRawCall方法创建一个Call,这个方法很重要,他会对请求进行组装,之后会调用call.execute()发起请求。
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
通过serviceMethod的toRequest返回一个request,参数就是调用方法的参数,这里基本上就可以确定请求的地址等具体信息了,然后调用callFactory的newCall方法返回一个call,这个CallFactory就是OkHttpClient
/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
// 省略部分代码
.....
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
这里重点是handlers的apply方法,前面我们分析ServiceMethod中知道其实这里是根据方法的参数得到,主要是除去Query、Path这里参数注解的我们简单看下
static final class Query<T> extends ParameterHandler<T> {
private final String name;
private final Converter<T, String> valueConverter;
private final boolean encoded;
Query(String name, Converter<T, String> valueConverter, boolean encoded) {
this.name = checkNotNull(name, "name == null");
this.valueConverter = valueConverter;
this.encoded = encoded;
}
@Override void apply(RequestBuilder builder, T value) throws IOException {
if (value == null) return; // Skip null values.
builder.addQueryParam(name, valueConverter.convert(value), encoded);
}
}
就简单看下Query类,都是继承自ParameterHandler,主要是apply方法,这里又调用了RequestBuilder的addQueryParam方法
/** Encodes the query parameter using UTF-8 and adds it to this URL's query string. */
public Builder addQueryParameter(String name, String value) {
if (name == null) throw new IllegalArgumentException("name == null");
if (encodedQueryNamesAndValues == null) encodedQueryNamesAndValues = new ArrayList<>();
encodedQueryNamesAndValues.add(
canonicalize(name, QUERY_COMPONENT_ENCODE_SET, false, false, true, true));
encodedQueryNamesAndValues.add(value != null
? canonicalize(value, QUERY_COMPONENT_ENCODE_SET, false, false, true, true)
: null);
return this;
}
OK,到最后把参数名参数值放在一个集合里面,成对连续放置。
到此基本上Retrofit的请求前的处理就这么多,下面就是OkHttp发起请求的过程了,我们下篇再分析。