Retrofit是针对Android网络请求的框架,底层是基于OkHttp实现的,更多使用运行时注解的方式提供功能。
Retrofit基本用法
首先需要配置build.gradle
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
Retrofit的分类
主要分为三类,请求方法注解,标记类注解,参数类注解。
- 请求方法注解: GET,POST,PUT,DELETE,HEAD,PATCH,OPTIONS和HTTP。
- 标记类注解有三种:FormUrlEncode,Multipart,Streaming。
- 参数类注解有Header,Body,Path,Field等
GET请求访问网络
首先编写请求网络接口:
public interface IpService {
@Headers({
"Accept-Encoding: application/json",
"User-Agent: RetrofitTest"
})
@GET("getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg();
}
注意要在请求头中设置Accept-Encoding: application/json
,在接口中定义了访问地址,getIpMsg方法,这个方法返回的Call<IpModel>
类型的参数。
然后创建Retrofit,并创建接口:
String url = "http://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
IpService ipService = retrofit.create(IpService.class);
Call<IpModel>call = ipService.getIpMsg();
用创建者模式创建Retrofit,然后url是通过拼接baseUrl和接口中的url得到请求URL,接下来用Retrofit动态代理的方式获得接口,然后调用接口的getIpMsg方法得到call对象,然后处理回调。
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Log.d(TAG, "onResponse: country get");
Toast.makeText(getApplicationContext(),country,Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
Log.d(TAG, "onFailure: "+t);
}
});
动态配置URL地址
@GET注解中有包含了{path},对应@Path注解中String path,如下:
public interface IpServiceForPath {
@Headers({
"Accept-Encoding: application/json",
"User-Agent: RetrofitTest"
})
@GET("{path}/getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg(@Path("path") String path);
}
这样就可以在getIpMsg方法中动态指定路径了,比如:
Call<IpModel>call = ipServiceForPath.getIpMsg("service");
动态指定查询条件@Query
使用@Query来动态指定ip的值,接口如下:
public interface IpServiceForQuery{
@GET("getIpInfo.php")
Call<IpModel> getIpMsg(@Query("ip")String ip);
}
也可以将参数集成为一个Map统一传递,如图:
public interface IpServiceForQueryMap {
@GET("getIpInfo.php")
Call<IpModel> getIpMsg(@QueryMap Map<String, String> options);
}
POST请求参数
public interface IpServiceForPost {
@FormUrlEncoded
@POST("getIpInfo.php")
Call<IpModel> getIpMsg(@Field("ip") String first);
}
首先使用@FormUrlEncode注解来表明这是一个表单请求,然后在getIpMsg方法中使用@Field注解来标示所对应的String类型数据的键,从而组成键值对来进行传递。
也可以用POST方式将JSON字符串作为请求体发送到服务器,使用@Body即可,Retroit会将Ip对象转换为字符串。
Retrofit的创建过程
Retrofit是根据建造者模式构建出来的,Buider方法调用了Platform.get(),根据不同的平台调用不同的线程池,接下来看build方法,baseUrl是必须指定的,callFactory默认是this.callFactory。this.callFactory就是我们在构建Retrofit时调用callFactory方法所传进来的。
接下来创建Retrofit实例并调用如下代码生成接口的动态代理对象:
IpService ipService = retrofit.create(IpService.class);
然后看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);
}
});
}
返回了一个动态代理对象,当我们调用IpService的getIpMsg方法时,最终会调用InvocationHandler的invoke方法,有三个参数,第一个参数是代理对象,第二个参数是调用的方法,第三个是方法的参数。然后看loadServiceMethod做了什么。
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
这里会判断serviceMethodCache缓存中是否有方法,如果有方法,那么得到该方法。如果没有方法,那么就创建一个,并加入serviceMethodCache缓存起来。然后看ServiceMethod是如何创建的,片段如下:
callAdapter = createCallAdapter();
首先调用了createCallAdapter方法,最终会得到构建Retrofit调用build方法时adapterFactories添加的对象的get方法。build方法中adapterFactories列表会默认添加defaultCallAdapterFactory,defaultCallAdapterFactory指的是ExecutorCallAdapterFactory,ExecutorCallAdapterFactory的get方法,如下:
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的responseType得到的是返回数据的真实类型,比如传入的是Call<IpModel>
,返回的类型就是IpModel,adapt方法会创建ExecutorCallbackCall,它会将call的回调转发至UI线程。因此在:
responseType = callAdapter.responseType();
返回真实的数据类型。然后调用
responseConverter = createResponseConverter();
方法来遍历convertFactories列表中存储的Convert.Factory.并返回一个合适的Convert来转换对象。此前我们调用了
addConverterFactory将GsonConverterFactory添加到convertFactories列表中,表示返回的数据支持JSON对象。然后
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
遍历parseMethodAnnotation方法来对请求方式和请求地址进行解析。在
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
对方法中的参数注解进行解析。
然后看Retrofit的create方法,在调用了loadServiceMethod方法后会创建OkHttpCall,在构造方法中进行了赋值操作,紧接着调用serviceMethod.callAdapter.adapt(okHttpCall),adapt方法会创建ExecutorCallbackCall并传入OkHttpCall,ExecutorCallbackCall是对Call的封装,通过callbackExecutor将请求回调到UI线程,我们在Call中调用的enqueue方法最终调用的是delegate,delegate是传入的OkHttp。
call的enqueue方法
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);
}
首先调用了call的enqueue方法,然后调用parseResponse方法
response = parseResponse(rawResponse);
然后跟踪parseResponse方法:
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) {
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);//1
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;
}
}
主要就是根据不同的状态码来做不同的操作。如果请求成功那么会调用注释1中的代码。调用toResponse方法,如下:
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
这里的responseConverter就是createResponseConverter方法中返回的converter,之前传入的是GsonConvertFactory,跟踪GsonConvertFactory的代码:
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
可以看见,最终会返回一个GsonResponseBodyConverter,跟踪GsonResponseBodyConverter
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
在这里利用jsonReader将回调数据转换为JSON格式。