个人主页:https://chengang.plus/
文章将会同步到个人微信公众号:Android部落格
1 基本使用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_retrofit);
new Thread(new Runnable() {
@Override
public void run() {
try {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GithubService service = retrofit.create(GithubService.class);
Call repos = service.listRepos("cg229836277");
Response response = repos.execute();
ResponseBody responseBody = response.body();
String message = responseBody.string();
MyLog.d(TAG, "message:" + message);
Call userRepos = service.getUserInfo("cg229836277");
Response userResponse = userRepos.execute();
UserInfoBean userInfoBean = userResponse.body();
String userName = userInfoBean.getName();
MyLog.d(TAG, "userName:" + userName);
} catch (Exception | Error e) {
e.printStackTrace();
}
}
}).start();
}
run方法中包裹的代码以空格为界,分为两部分,上半部分是返回的结果不解析,直接返回ResponseBody,拿到body string之后做其他处理;下半部分获取用户相关信息,由Gson解析序列化成UserInfoBean对象。
所有的接口定义在GithubService
类中,如下:
public interface GithubService {
@GET("users/{user}/repos")
Call listRepos(@Path("user") String user);
@GET("users/{user}")
Call getUserInfo(@Path("user") String user);
}
2 源码查看
2.1 Retrofit.Builder
首先需要构建Retrofit,通过Retrofit.Builder类构建。
看看构建方法里面的几个方法。
2.1.1 client
public Builder client(OkHttpClient client) {
return callFactory(Objects.requireNonNull(client, "client == null"));
}
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = Objects.requireNonNull(factory, "factory == null");
return this;
}
这里将OkHttpClient
设置为callFactory
,因为OkHttpClient集成自Call.Factory,所以可以直接传递:
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
2.1.2 baseUrl
用于设置请求网址,比如https://api.github.com/
是请求的基础url,后面还可以加很多请求query参数。
这个方法最终调用到:
public Builder baseUrl(HttpUrl baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
List pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
在调用之前还调用了HttpUrl的get方法,将传递进来的URL或String地址,转成HttpUrl类型:
private HttpUrl(Builder builder) {
this.scheme = builder.scheme;
this.username = percentDecode(builder.encodedUsername, false);
this.password = percentDecode(builder.encodedPassword, false);
this.host = builder.host;
this.port = builder.effectivePort();
this.pathSegments = percentDecode(builder.encodedPathSegments, false);
this.queryNamesAndValues = builder.encodedQueryNamesAndValues != null
? percentDecode(builder.encodedQueryNamesAndValues, true)
: null;
this.fragment = builder.encodedFragment != null
? percentDecode(builder.encodedFragment, false)
: null;
this.url = builder.toString();
}
get方法将你传入的url先做规范化处理,比如url开始和结束位置有空格等其他非法字符等,处理完毕之后,接着获取请求方式,http或https,请求host,端口等信息。
2.1.3 addConverterFactory
这个方法中,可以添加请求数据或返回数据的序列化类。比如开头的实例中,需要将返回的用户信息序列化成UserInfoBean类,可以通过这个方法添加GsonConverterFactory对象。
2.1.4 addCallAdapterFactory
添加请求处理类,比如RxJavaCallAdapterFactory,可以通过将他的对象传递到这个方法,引入对RxJava的支持。
2.1.5 build
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Make a defensive copy of the converters.
List converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
- baseUrl,上层不设置baseUrl,会直接报错;
- callFactory,上层不设置,默认设置OkHttpClient,如果自己自定义的话,可以添加一些自定的interceptor,这个是okhttp的一大特色;
- callAdapterFactories,将上层添加的请求工厂类添加进来,另外还有平台自己默认的一个请求工厂类,在Platform类中定义,如下:
List extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
这里hasJava8Types 在sdk版本大于等于24时,为true,这里将CompletableFutureCallAdapterFactory和DefaultCallAdapterFactory一起放到一个List中然后返回,这里DefaultCallAdapterFactory持有传递过来的callbackExecutor对象。
- converterFactories
用于存放请求参数或结果参数的序列化工具,初始化如下:
// Make a defensive copy of the converters.
List converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
//retrofit\src\main\java\retrofit2\Platform.java
List extends Converter.Factory> defaultConverterFactories() {
return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList();
}
OptionalConverterFactory
只有在Android sdk 版本大于等于24才添加。
BuiltInConverters
为默认添加的转换类。
总结
最终build方法返回一个Retrofit对象,封装了上述参数。
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
2.2 Retrofit.create
还是贴代码,然后分步骤分析:
public T create(final Class service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(...);
}
上边create方法的参数里面,Class对象service对应文章开头的GithubService类的对象。
2.2.1 validateServiceInterface
用于检查传递过来的形参service是否合法,第一,必须是interface类型;第二,interface类型的类,不能限定类型,比如GithubService
private void validateServiceInterface(Class> service) {
if (!service.isInterface()) {
throw new IllegalArgumentException("API declarations must be interfaces.");
}
if (service.getTypeParameters().length != 0) {
throw new IllegalArgumentException(message.toString());
}
}
这个方法还涉及一个validateEagerly参数,在Retrofit.Builder中通过validateEagerly方法设置,如果设置为true之后,在这个方法中,就会接着解析interface里面的方法:
if (validateEagerly) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
loadServiceMethod(method);
}
}
}
isDefaultMethod
表示当前方法是public且非abstract修饰的实例方法,也就是一个非static方法,有方法体,在interface里面定义。其实说的是java 8的新特性,在interface中,可以添加default修饰符到一个方法,这个方法可以有实现,可以避免当在interface中添加一个方法,所有实现都必须改动的问题。
A default method is a public non-abstract instance method, that is, a non-static method with a body, declared in an interface type.
validateEagerly为true时,会获取到service interface下所有定义的方法,并判断所有方法的类型,如果满足非static并且是default方法,就会调用loadServiceMethod方法,这个方法下一节分析。
2.3 调用interface中的方法
以文章开头demo中GithubService中的方法为例,通过GithubService service = retrofit.create(GithubService.class);
获取了GithubService对象,接下来调用里面定义的API方法,具体调用在Retrofit.create方法中定义的内部类中实现:
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 @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) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
在InvocationHandler类中,调用最后在invoke方法里面:
- proxy,Retrofit的代理对象;
- method,GithubService中定义的方法;
- args,方法中定义的参数,比如
Call
的参数就是user。listRepos(@Path("user") String user);
2.4 ServiceMethod和HttpServiceMethod
接下来判断是否是default修饰的方法,如果是,就直接调用,否则通过loadServiceMethod方法调用。
ServiceMethod
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
通过这个方法可以拆分出很多细节,包括注解解析,返回类型解析等。最终这些属性被封装到ServiceMethod类中。
//Retrofit
ServiceMethod> loadServiceMethod(Method method) {
ServiceMethod result = ServiceMethod.parseAnnotations(this, method);
}
//ServiceMethod
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
//RequestFactory
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
return new RequestFactory(this);
}
上边的代码引申出了RequestFactory,这个类主要处理请求相关的参数。
2.4.1 RequestFactory
- parseAnnotations
方法中通过建造者模式,构建了请求相关的诸多参数,这里只分析其中比较核心的参数。
- parseMethodAnnotation
通过方法可以知道,这个方法主要解析方法的注解:
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
请求方法判断
if-else判断,从DELETE到OPTIONS都属于http的请求方法,参见:https://www.runoob.com/http/http-methods.html。
请求方法注解参数
@GET("users/{user}/repos")
以上述GET请求方法为例,parseHttpMethodAndPath方法解析出了users/{user}/repos
这个地址,以及地址中的user参数。
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
this.httpMethod = httpMethod;
this.hasBody = hasBody;
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
relativeUrl对应地址,relativeUrlParamNames对应地址中的动态参数。
注解以HTTP开头
表示可以自定义请求,示例如下:
@HTTP(method = "GET", path = "users/{user}", hasBody = false)
Call getUserInfo1(@Path("user") String user);
注解以Headers开头
在http请求中添加请求头,如果一个项目中多个请求API都有相同的header字段,比如每个请求头中都有设备型号字段,设备的Android版本字段等,可以统一在OKHttpClient中添加Interceptor,而Interceptor中通过添加Request.Builder设置。这个OKHttpClient对象通过Retrofit.Builder
中的client方法设置:
public Builder client(OkHttpClient client) {}
注解包含Multipart
常见的 POST 数据提交的方式。使用表单上传文件时,必须让