前面我们已经分析过了OkHttp网络请求框架的源码,今天继续聊聊一个新的框架,Retrofit。如果还没看过OkHttp框架的源码分析,点击这里(OkHttp源码解析)查看,有助于对今天内容的了解。
国际惯例,先了解下框架的功能和特点:Retrofit是Square公司基于OkHttp开发的对网络请求接口进行封装的开源框架。框架中采用大量的注解,很大程度上简化了网络请求的代码,而且解耦性很强,现广泛使用于移动端和Java后端。
那我们就从简单的使用开始把:
首先,就是添加依赖和添加网络权限:
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
接下来需要定义一个接口类:
public interface PixabayAPI {
public static final String BASE_URL = "https://pixabay.com/api/"; //服务器的地址,必须以"/"结束
@GET("?key=16902026-685*************01c&image_type=photo") //请求数据的接口,采用的是get的请求方式,参数直接拼接
public Call<Pixabay> getPhotoItems(@Query("q") String param); //因为参数是q=***直接拼接在请求url中,所以使用@Query注解
}
然后就开始使用retrofit请求了:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(PixabayAPI.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
PixabayAPI api = retrofit.create(PixabayAPI.class);
final Call<Pixabay> photos= api.getPhotoItems("cat");//创建一个Retrofit的网络请求,网络请求返回的值会封装成泛型里面的数据类型
new Thread(new Runnable() {
@Override
public void run() {
try {
Response<Pixabay> response = photos.execute();//同步请求,内部是OkHttpCall调用okhttp对象执行的execute();
Log.e("retrofit_response", response.body().toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
builder模式我们看过很多了吧,前两天刚介绍的OkHttp采用的也是builder模式。创建实例的方法也一样有两种,可以直接通过构造函数new出来,或者通过build方法构建出来,这里使用第二种方式,我们看下build方法里面有那些好料。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required."); //强制要求baseurl不能为空
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient(); //如果没有自定义设置callFactory,就使用默认的OkHttpClient
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();//如果没有手动设置callbackExecutor,使用默认的平台返回的callbackExecutor,在Android 里面就是一个MainThreadExecutor,线程切换最终还是得靠handler来处理
}
……
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);//最后还是通过new一个客户端进行返回
}
所以,从代码中可以看到,build方法中做了一些初始化操作,最后还是通过new的方式创建一个实例返回;
然后看看创建接口实例的代码:
PixabayAPI api = retrofit.create(PixabayAPI.class); //通过retrofit的动态代理,创建请求接口对象
这里开始要进入源码研究了,看着好像就一句代码,创建一个实例,轻轻松松完成,那么内部为我们实现了哪些操作呢?
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T create(final Class<T> service) {
validateServiceInterface(service); //验证传入的类必须是接口类型
//返回一个动态代理类的实例
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();//在Android设备中最终 return new Android(); MainThreadExecutor中用handler切换线程
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {//如果是Object类里的方法,直接使用
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) { //如果是平台封装好的方法,直接返回平台方法
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);//一般我们定义的接口类,都是走的这里,所以需要分析下loadServiceMethod方法
}
});
}
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);//ServiceMethod作用是解析method上的各种注解、参数类型、返回类型,并能根据这些生成相应的CallAdapter、Converter等
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;
}
# HttpServiceMethod.class
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
在create方法中,创建了一个请求接口的动态代理对象,当我们调用该对象中的某个方法时,会代理到invoke方法,然后会通过Method对象创建出ServiceMethod对象(loadServiceMethod),用于解析method上的各种注解、参数类型、返回值类型。然后通过ServiceMethod对象的invoke方法会构建一个OkHttpCall对象,所以真正的网络请求是通过OkHttpCall内部使用OkHttp请求实现,等请求返回结果后,再调用ServiceMethod中,根据方法返回类型生成的CallAdapter的adapt方法,将这个原始的OkHttpCall对象包装成需要的类型返回,看过源码可以知道,异步请求中的线程切换最终也是交给handler来完成,至此完成一次网络请求。
Retrofit的网络请求也一样分为同步和异步两种方式,在这里,都是通过OkHttpCall去安排执行的。
我们前面提到,请求是经过ServiceMethod创建出一个OkHttpCall对象,那具体是怎么实现的呢,我们一起去看看:从loadServiceMethod方法进来 => result = ServiceMethod.parseAnnotations(this, method); =>RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);那么,这里我们就要分析RequestFactory这个类了,
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
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;//是否Multipart文件上传
private final ParameterHandler<?>[] parameterHandlers;//请求的参数
final boolean isKotlinSuspendFunction;//是否kotlin协程中的挂起函数
RequestFactory build() {
for (Annotation annotation : methodAnnotations) { //循环解析各个注解
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
……
}
private void parseMethodAnnotation(Annotation annotation) { // 解析注解,同时设置请求request的一些参数
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if……
}
……
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
……
// Get the relative URL path and existing query string, if present.
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError(method, "URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value); //解析请求参数
}
}
上述步骤执行的内容就是解析各种注解,然后获取到各种请求参数到当前的Factory,然后等网络请求的时候,由工厂生产Request。然后继续看刚才的 result = ServiceMethod.parseAnnotations(this, method);方法执行到最后:
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
直接看关键点
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
……
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
……
}
所以后面执行网络请求,还是走的okhttp 中的Call(也就是RealCall),具体的实现,在前面的okhttp解析中有讲解过。(
PixabayAPI api = retrofit.create(PixabayAPI.class);
final Call<Pixabay> allContributors = api.getPhotoItems("cat"); //走到这里,就是创建了一个Call 对应okhttp中的call
)
然后网络请求成功后,数据回传,就得经过Retrofit的解析了,同样还是OkHttpCall来执行:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
// Buffer the entire body to avoid future I/O.
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); //解析器解析获得最后的结果并返回
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
其实retrofit并没有网络请求功能,一系列的网络请求和数据解析都是通过各种转换器去实现的,现在流行的组合是:
RxJava + okhttp + Retrofit ,有机会分析分析为啥选这个组合,今天的Retrofit源码先这样粗略过一遍,回头结合具体的场景做一些分析吧。