转载请注明出处:http://blog.csdn.net/p892848153/article/details/50890500
整体介绍
最近注解式的框架非常火,注解以其轻量,简洁等特性被人们所喜爱者,关键是它解藕。网络请求的框架非常多,比较受欢迎的当属retrofit和okHttp了。连retrofit都是基于okHttp之上开发的。ok, 言归正传,我们来聊聊retrofit。我们假设读者都对okHttp有一定的了解!
先简单说下retrofit的用法,如果不懂,可以上网查查就知道了,当然最好还是看官方的文档。public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
上面的demo是来自官方文档的一小段,还有最后一步,上面没体现出来,那就是调用Call的enqueue或者execute方法实现真正的发起请求。这里要注意以下Call这个类,它不是okHttp里面的Call类,是Retrofit自己的类。看上面demo可以发现,就是写一个接口,然后接口里面有写标签,再实例化一个retrofit对象,再然后就是关键所在了。 我们看下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);
}
return loadMethodHandler(method).invoke(args);
}
});
}
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadMethodHandler(method);
}
}
}
MethodHandler loadMethodHandler(Method method) {
MethodHandler handler;
synchronized (methodHandlerCache) {
handler = methodHandlerCache.get(method);
if (handler == null) {
handler = MethodHandler.create(this, method);
methodHandlerCache.put(method, handler);
}
}
return handler;
}
可以发现它用到了动态代理,这里我们假设读者都懂java里面的动态代理。现在知道那个接口是干嘛用的了吧,至于上面的标签,无非就是提供一些信息,没什么神奇的。好的,回到create方法里面的代理,发现代理最后会调用MethodHandler的invoke方法,并且这个MenthodHandler还缓存在一个Map中。所以retrofit的网络请求带有缓存功能,如果两次调用同一个网络请求,第二次请求会快一些,少了实例化与解析注解的时间。还有就是你调用接口中的方法其实到最后都是委托给动态代理的Proxy.invoke()方法,该方法又会调用MenthodHandler.invoke()方法。ok,这条线知道到这里就行了,以后再详解,再来看下MethodHandler.create方法是如何创建MethodHandler对象的。
static MethodHandler create(Retrofit retrofit, Method method) {
CallAdapter<?> callAdapter = createCallAdapter(method, retrofit);
Type responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw Utils.methodError(method, "'"
+ Types.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
Converter<ResponseBody, ?> responseConverter =
createResponseConverter(method, retrofit, responseType);
RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
return new MethodHandler(retrofit.callFactory(), requestFactory, callAdapter,
responseConverter);
}
首先该方法会得到一个CallAdapter<?>对象,一般情况下,默认的到的会是由DefaultCallAdapterFactoty对象创建的CallAdapter对象,但是很不巧,Android平台更改了该工厂,返回的是ExecutorCallAdapterFactory对象。对CallAdapter的具体分析,以后再说。我们接着说MethodHandler.create方法。再然后是验证返回类型,再然后得到一个Converter<?, ?>对象,默认的到的是由BuiltinConverters创建的Converter对象,再然后由RequestFactoryParser.parse()方法解析注解标签了。最后就new 了一个MethodHandler对象返回。至于Converter<?, ?> 是什么、有什么用、怎么用,CallAdapter是什么、有什么用、怎么用,RequestFactoryParser如何解析注解标签、所有注解标签的用法,我们会分三个段落来分别分析讲解。
我们知道Converter是由Converter.Factory的工厂方法生成的。我们看下Converter.Factory
abstract class Factory {
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return null;
}
}
可以看到该类中有三个方法,第一个方法responseBodyConverter()的作用是返回一个Converter<ResponseBody, ?>,该Converter的作用是将ResponseBody转换成一个你想要的类,具体要转换成什么,看你的实现了。第二个requestBodyConverter()方法同理,返回一个可以将一个实体类转换成RequestBody的Converter, 最后一个方法的作用很明显是返回一个可以将类转换成String的Converter。在这里要注意一下,RequestBody和ResponseBody类是okhttp里面的类,不是Retrofit里面的。稍后会分析一下GsonConverterFactory是如何实现工作的。
Converter<?, String>和Converter<?, RequestBody>
通过上篇,我们知道Retrofit默认返回的Converter.Factory是BuiltInConverters类,该类根据返回类型会返回各种各样的Converter,不过他们基本实现都很简单。那么问题来了,Converter将一个特定的类转换成RequestBody进行网络传输的方法在什么时候,在哪里会被使用到呢?
回到MethodHandler.create()方法,该方法里面有个RequestFactoryParser.parse()来解析标签,我们进入该方法。好吧,这么说有点啰嗦,以后在说到从哪个方法进入到哪个方法都用'->'表示了。我们跟着方法来:RequestFactoryParser.parse() -> RequestFactoryparser.parseParameters()在该方法中.
private void parseParameters(Retrofit retrofit, Annotation[] methodAnnotations) {
......
} else if (parameterAnnotation instanceof Path) {
......
Converter<?, String> converter =
retrofit.stringConverter(parameterType, parameterAnnotations);
action = new RequestAction.Path<>(name, converter, path.encoded());
}
......
} else if (parameterAnnotation instanceof Part) {
......
Converter<?, RequestBody> converter =
retrofit.requestBodyConverter(iterableType, parameterAnnotations,
methodAnnotations);
action = new RequestAction.Part<>(headers, converter).iterable();
......
会发现在解析Path, Query等标签的时候会得到一个Converter<?, String>对象,然后传递给RequestAction,而在解析Part, PartMap等标签时会得到一个Converter<?, RequestBody>对象,然后传递给RequestAction,我们进RequestAction查看,发现只有一个perform()方法需要实现,还有两个方法,它们的调用最终都是要回到perform()方法。在RequestAction中,有各种各样的实现,比如RequestAction.Path。各个标签的解析都会生成一个RequestAction对象,保存在数组中,然后在RequestFactoryParser.toRequestFactory()方法中将数据都保存在RequestFactory中。然后将CallAdapter, Converter, RequestFactory都传递给MethodHandler中,最后在Retrofit.create中调用了MethodHandler.invoke方法。那问题是perform方法是怎么被调用的呢?
说来话长,我就长话短说。回到MethodHandler.invoke()方法
Object invoke(Object... args) {
return callAdapter.adapt(
new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
}
上篇我们说到MethodHandler.create中的CallAdapter是ExecutorCallAdapterFactory.get()得到的,在该方法中找到默认的CallAdapter对象
@Override
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
知道CallAdapter.adapt()方法其实返回一个ExecutorCallbackCall对象。所以在我们最后真正发起网络请求的时候,比如调用Call.enqueue()其实是调用ExecutorCallbackCall.equeue()方法,该方法又会调用OkHttpCall.enqueue()。ok, 看下OkHttpCall.enqueue()
@Override public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
......
call = rawCall = createRawCall();
......
}
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;
}
看到requestFactory.create()了吧,
Request create(Object... args) throws IOException {
RequestBuilder requestBuilder =
new RequestBuilder(method, baseUrl.url(), relativeUrl, headers, contentType, hasBody,
isFormEncoded, isMultipart);
if (args != null) {
RequestAction[] actions = requestActions;
if (actions.length != args.length) {
throw new IllegalArgumentException("Argument count ("
+ args.length
+ ") doesn't match action count ("
+ actions.length
+ ")");
}
for (int i = 0, count = args.length; i < count; i++) {
actions[i].perform(requestBuilder, args[i]);
}
}
return requestBuilder.build();
}
现在看到RequestAction.perform()方法的调用了吧,而这个args就是你接口中方法的参数,它跟标签是一一对应的。也就是说接口中方法的参数每个都要配上标签。ok,绕了一圈,终于回到了Converter来了,我们看下Path.perform();
@Override void perform(RequestBuilder builder, T value) throws IOException {
if (value == null) {
throw new IllegalArgumentException(
"Path parameter \"" + name + "\" value must not be null.");
}
builder.addPathParam(name, valueConverter.convert(value), encoded);
}
Converter.convert()方法将实体类转换成String,然后交给RequestBuilder.addPathParam()去处理。总之不同的标签最后都大概类似的处理,都交给RequestBuilder的不同方法处理,然后在RequestBuilder.build()方法中根据这些数据生成一个okhttp的Request对象。到此,Converter<?, String>和Converter<?, RequestBody>是什么,怎么用都清楚了吧。
Converter<ResponseBody, ?>
再来看看Converter<ResponseBody, ?>是什么鬼,怎么用?上面说到网络请求实际上是OkHttpCall在发起。比如enqueue()方法。我们再回到OkHttpCall.enqueue()方法
@Override public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
......
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);
}
......
}
可以看到真正发起请求的是okhttp3.Call.enqueue()方法,返回成功时会调用parseResponse()方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
看到Converter<ResponseBody, ?>.convert方法了吧,被转化得到的T body被至于Response<T>中,然后通过回调Callback.onResponse(OkHttpCall, Response<T>)返回。那Converter<ResponseBody, ?>对象是在何时传递进来的呢。我们回到MethodHandler.create()方法,就是在这里传递进去的。Converter<ResponseBody, ?>也是通过Converter.Factory.responseBodyConverter()方法返回的。好了,到这里大家都请求Converter类是怎么工作的了吧。本来想说一下GsonConverterFactory的,不过看了下,没几行,就不聊了。我相信现在大家应该都会自定义Converter.Factory了吧,然后调用Retrofit.Builder.addConverterFactory就可以了。
现在来说说CallAdapter在retrofit中的作用,相信弄懂这些,自定义CallAdapter就不难了。也能对理解RxAndroid配合retrofit使用有帮助。
CallAdapter
好的,言归正传。首先我们看下MethodHandler.create()方法,在该方法中会得到一个CallAdapter对象,跟着方法找MethodHandler.create() -> MethodHandler.createCallAdapter() -> Retrofit.callAdapter() -> Retrofit.nextCallAdapter()
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
......
}
看到这里,我们知道CallAdapter是由CallAdapter.Factory.get()返回的。我们找到android系统默认给我们的Factory:ExecutorCallAdapterFactory类。看下ExecutorCallAdapterFactory.get()是如何返回一个CallAdapter对象的。
@Override
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
ok, 现在知道系统返回一个匿名类给我们, 关键点是它的adapt()方法。从
Retrofit2.0源码分析(二)Converter
我们知道哪里会调用到adapt() 方法,就在MethodHandler.invoke()方法里面,CallAdapter的作用就在这体现出来了。adapt()方法接收一个OkHttpCall对象,返回一个ExecutorCallbackCall对象。到这里要说下这个设计非常好,OkHttpCall跟okhttp3包的耦合性很高。ExecutorCallbackCall跟OkHttpCall一样实现Call<T>,ExcutorCallbackCall还包含OkHttpCall对象,看起来就是就是一个代理模式。
那么系统提供的ExecutorCallbackCall有什么用呢,它的作用就是把你的回调方法弄到主线程中去,保证你可以在回调Callback的方法中更新UI。说到这里还没完,CallAdapter不是还有一个responseType()方法么,看注释的意思是该方法返回的Type在将HTTP response body 转换成一个Java object的时候会用到,而且不是Method.getGenericReturnType()返回的Type。看的我有点晕,总之这个Type = retrofit.Utils.getCallResponseType(Method.getGenericReturnType());希望有高手懂的,帮忙讲清楚这个Type。
现在来对retrofit的所有注解标签做一遍解释。首先是方法上的标签,然后是参数上的标签。
Method Annotation
DELETE,GET,HEAD,PATCH,POST,PUT,OPTIONS,HTTP :代表不同的网络访问,它的值是相对地址(relativeUrl),可以包含'?'字符,'?'字符后面跟key=value&key=value...但是不可以出现“{var}”这种在地址中代表变量的字符。
比如@GET("users/{user}/repos") 对
@POST("users/{user}/repos?password=123456") 对
@HEAD("users/{user}/repos?password={pwd}") 错, '?' 后面不能出现"{var}"类型字符
Headers :它的值是一个字符串数组,然后会将值解析后构造出一个okhttp3.Headers对象。
private okhttp3.Headers parseHeaders(String[] headers) {
okhttp3.Headers.Builder builder = new okhttp3.Headers.Builder();
for (String header : headers) {
int colon = header.indexOf(':');
if (colon == -1 || colon == 0 || colon == header.length() - 1) {
throw methodError(method,
"@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
}
String headerName = header.substring(0, colon);
String headerValue = header.substring(colon + 1).trim();
if ("Content-Type".equalsIgnoreCase(headerName)) {
contentType = MediaType.parse(headerValue);
} else {
builder.add(headerName, headerValue);
}
}
return builder.build();
}
数组中的每个字符串都至少有一个
' : '
字符,且该字符第一次出现的位置不能是首位。
Multipart, FormUrlEncoded: 这两个标签起标记作用,没有值。
引用两句话解释下这两个标签的意思
application/x-www-form-urlencoded :窗体数据被编码为名称/值对。这是标准的编码格式。
multipart/form-data : 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。
Parameter Annotation
@Url: 用来设置relativeUrl的。不能出现多个, 不能跟@Path标签一起用,不能在@Query标签后面出现,如果方法头部标明了relativeUrl,就不能再使用这个了。该标签后面可以跟着String, java.net.URI, Class<android.net.Uri>三种类型的参数。
@Path : 用来给relativeUrl中的"{var}"赋值的,不能赋值null。不能在@Query标签后面出现, 不能跟@Url标签一起用,必须跟relativeUrl一起使用,它的值必须符合正则表达式"[a-zA-Z][a-zA-Z0-9_-]*",它的值必须是relativeUrl中以"{var}"形式出现过的var。
@Query:用来表示传给服务器的key=value。它修饰的参数值可以是Iterable(或者子类), Array或者其他的什么
@Header : 用来给请求头赋值的。值会传给okhttp3.Headers。它修饰的参数值的取值跟@Query一样。
@Field :用来表示传给服务器的key=value。只有在有@FormUrlEncoded标签时才能使用此标签。它修饰的参数值的取值跟@Query一样
@FieldMap: 用来表示传给服务器的多个key=value。只有在有@FormUrlEncoded标签时才能使用此标签。它修饰的参数值的取值跟@QueryMap一样
@Part : 标签的值是key, 标签修饰的参数值是要传递给服务器的数据。只有在有@Multipart标签时才能使用此标签。它修饰的参数值的取值跟@Query一样,使用此标签时会创建一个okhttp3.Headers以供使用
okhttp3.Headers headers = okhttp2.Headers.of("Content-Disposition", "form-data; name=\"") + part.value() + "\"",
"Content-Transfer-Encoding", part.encoding());
@PartMap : 表示使用了多个@Part标签。只有在有@Multipart标签时才能使用此标签。它修饰的参数值的取值跟@QueryMap一样
@Body: 表示传递一个RequestBody过去,将会使用Converter把数据转化成RequestBody。使用此标签时,不能使用@FormUrlEncoded或者@Multipart。只能使用一次