#.整体介绍
Retrofit也是一个网络请求库,它是对OkHttp的进一步封装。用OkHttp做请求时需要我们自己设置各种请求参数,并且对结果做解析。
Retrofit的
网络请求工作实际上还是由OkHttp来完成,
而
Retrofit针对OkHttp的输入和输出过程做处理
,简化了这两个过程的代码编写,提供了强大的功能支持。
1.Retrofit封装了上层的网络请求接口,帮助开发者简化OkHttp的Request封装。
可以使用Retrofit提供的一堆注解,来编写网络请求接口类,然后Retrofit会解析这个接口类,调用相关接口时,
Retrofit内部会生成对应的Http请求数据体,并最终调用OkHttp来发起网络请求。
2.Retrofit提供了数据转换器和适配器功能,当从OkHttp获取到网络请求结果Response后会做进一步地各种处理
定义网络接口的返回类型时,需要与
数据转换器、适配器要相结合。
#.使用过程简介
1.使用注解编写接口类,示例:
public interface TestHttpService2 {
@GET("weather/xxx/xxx")
Call<String> testGet1(@Query("params1") int arg1, @Query("params2") String arg2);
}
2.创建Retrofit实例,解析接口类获取接口对象。示例:
//创建OkHttpClient实例
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS);
mOkHttpClient = builder.build();
//创建Retrofit实例
Retrofit.Builder builder = new Retrofit.Builder();
//配置URL基地址,Retrofit2 的baseUrl必须以 /(斜线)结束,否则会抛出错误
//这里的baseUrl加上接口类中每个方法注解后面的路径,才是一个接口的完整url地址
builder.baseUrl(“Ten▪Api - Tenapi.cn”)
.client(mOkHttpClient)//配置OkHttp实例
//配置使用的适配器
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
//配置使用的数据转化器
.addConverterFactory(new HttpConverterFactory());
mRetrofit = builder.build();
//解析接口类,获取代理对象
mHttpService = mRetrofit.create(TestHttpService.class);
}
3.调用代理对象的接口,获取到Call对象(或者Call的封装对象,要看选用的是什么适配器);利用Call(或其它对象)触发网络请求,获取结果数据并做需要的处理。
//调用方法获取Call对象或者Call对象的封装对象
Call<String> call = mHttpService.testGet1(3, "xxx");
//触发Retrofit发起网络请求,并获取Retrofit处理后的请求结果
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
//....做相应处理....
}
@Override
public void onFailure(Call<String> call, Throwable t) {
//....做相应处理....
}
});
(整体流程示意图,图片来自网络)
#.一、使用注解编写接口类
使用Retrofit提供的注解来编写接口类,Retrofit能够解析使用注解编写的接口类,从而大大简化了相关功能开发。
##.各种注解
主要分为4类,分别用于标注请求方式、标记请求头、标记请求参数、标记请求和响应格式。
1. 请求方法注解
注解类型
|
说明
|
@GET
|
get请求
|
@POST
|
post请求
|
@PUT
|
put请求
|
@DELETE
|
delete请求
|
@PATCH
|
用于更新局部资源,是PUT请求的补充
|
@HEAD
|
head请求
|
@OPTIONS
|
options请求
|
@HTTP
|
该注解可以替换以上所有的注解,它拥有三个属性:method、path、hasBody
|
2. 请求头注解
请求头注解
|
说明
|
@Headers
|
用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在
|
@Header
|
作为方法的参数传入,用于添加不固定的header,它会更新已有请求头
|
3.请求参数注解
基本上可分为两大类,用于GET请求的和用于POST请求的。
注解类型
|
说明
|
1.Get请求相关
|
|
@Query
|
用于Get请求,对应于一个参数
示例:
@Query(“params1”) int arg1
以上”params1″、int分别指明了参数名(服务端约定的)、参数类型
|
@QueryMap
|
用于Get请求,对应一个参数Map,当传递的参数不确定时,很有用
示例:
@QueryMap Map<String, Object> paramMap
传入的map的key值将是Http请求报文中的参数名
|
2.Post请求相关
|
|
2.1数据字段
|
|
@Filed
|
用于Post请求,对应单个参数
注意:需要配合@FromUrlEncoded使用,标明请求的数据格式是编码表单数据
示例:
@Field(“params1”) int arg1
|
@FiledMap
|
用于Post请求,需要配合
配合@FromUrlEncoded使用,效果与上面@QueryMap类似。 |
2.2复合类型数据上传,一般用于上传文件,当然也能用于上传其它数据
|
|
@Part
|
用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况
|
@PartMap
|
用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传
|
2.3自定义类型
|
|
@Body
|
使用Post请求发送自定义的数据类型对象。会根据转换方式将该对象转化对应的数据,例如添加GsonConverterFactory可将body转化为json字符串进行传递
|
3.其它字段
|
|
@Path
|
用于Url中的占位符
|
@Url
|
表明请求Url通过参数形式传入,此时@Get/@Post后面不能指定Url地址。
|
4.请求报文和响应报文数据体类型注解
@FromUrlCoded、@Multipart最终会影响Http请求报文中的
Content-Type字段。@Streaming不会影响Http报文字段,只是在Retrofit内部用于标注相应数据以字节流形式返回。
注解类型
|
说明
|
@FromUrlCoded
|
表明为编码表单数据,每个键值对需要使用@Filed注解,或者直接使用@FiledMap
|
@Multipart
|
表明为多种类型数据的混合表单,每部分数据都需要使用@Part,或者直接使用@PartMap
|
@Streaming
|
表示响应数据用字节流的形式返回。
注意:下载大文件时,应该使用该注解。
因为未使用该注解时,Retrofit默认会把数据全部加载入内存中,再供后继处理,占用内存过多时会出现OOM问题。
使用@Streaming注解时,响应数据会以字节流返回,不用全部同时加载入内存。可通过ResponseBody.byteStream()获取到数据的读取流,读取数据并进行处理。
|
##.示例
/**
* GET请求示例
*/
@GET("xxx/xxx/xxx")
Call<String> testGet1(@Query("params1") int arg1, @Query("params2") String arg2);
@GET("xxx/xxx/xxx")
Call<String> testGet2(@QueryMap Map<String, Object> paramMap);
@GET("xxx/xxx/xxx")
Flowable<String> testGet3(@Query("params1") int arg1, @Query("params2") String arg2);
/**
* POST请求示例 :@FormUrlEncoded
*/
@FormUrlEncoded
@POST("xxx/xxx/xxx")
Call<String> testPost1(@Field("params1") int arg1, @Field("params2") String arg2);
/**
* POST请求示例 :@Multipart
* 多用于文件上传,这里的RequestBody是OkHttp中的请求数据体封装对象
*/
@Multipart
@POST("xxx/xxx/xxx")
Call<String> testPost2(@Part("file1") RequestBody part1, @Part("file2") RequestBody part2);
/**
* .@Streaming示例,多用于下载比较大的文件
*/
@Streaming
@GET("xxx/xxx/xxx")
Call<ResponseBody> testMethod1(@Query("params1") int arg1, @Query("params2") String arg2);
/**
* .@Url示例
*/
@FormUrlEncoded
@POST
Call<String> testMethod2(@Url String url, @Field("params1") int arg1, @Field("params2") String arg2);
/**
* .@Path @HTTP示例
*/
@HTTP(method = "GET", path = "xxx/{userId}/xxx", hasBody = false)
Call<ResponseBody> testMethod3(@Path("userId") String user);
/**
* .@Body示例
*/
@POST("xxxx/xxxxx")
Call<ResponseBody> testMethod4(@Body TestBody body);
/**
* @Header @Headers示例
*/
@GET("user")
Call<ResponseBody> testMethod5(@Header("Authorization") String authorization);
@Headers({
"Accept: xxxxxxxx",
"User-Agent: xxxxx"
})
@GET("xxx/xxxxxxxx")
Call<ResponseBody> testMethod6(@Query("params1") int arg1, @Query("params2") String arg2);
#.二、流程分析
1.接口类解析与方法调用返回结果
接口类的解析是
通过动态代理模式(内部会使用Java反射)进行的,会生成对应的委托对象返回给上层。
我们外部持有的对象,就是这个委托对象。
通过动态代理模式(内部会使用Java反射)进行的,会生成对应的委托对象返回给上层。
我们外部持有的对象,就是这个委托对象。
//解析接口类,获取代理对象
mHttpService = mRetrofit.create(TestHttpService.class);
对应的源代码:
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();
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);
}
});
}
当委托对象调用相应接口时(例如我们调用
mHttpService.testGet1(
3
,
“xxx”)),会执行动态代理中InvocationHandler()的invoke()回调方法,最终会对这个接口的相关注解和参数做解析。每个接口解析后的结果都是一个
HttpServiceMethod对象,调用委托对象的方法时,最终会执行到HttpServiceMethod的invoke(args)方法。以上每个接口的注解和参数解析结果解析成HttpServiceMethod对象的过程只会执行一次,因为解析完成后就会被Map缓存下来,下次直接通过调用的接口查Map就能查到对应的HttpServiceMethod对象。
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
方法调用最终返回的结果由上面adapt(call, args)来生成的,而这个方法的执行过程取决于Retrofit选用的适配器。但无论用哪种适配器,最终的返回结果都是对OkHttpCall进一步封装后的对象。例如默认情况下的适配器工厂类是DefaultCallAdapterFactory,最终返回的是ExecutorCallbackCall;当使用RxJava,选用RxJava3CallAdapterFactory时,会根据定义类型返回Observable/Flowable等。(注:OkHttpCall、ExecutorCallbackCall都继承自Call接口)
2.触发网络请求时的整体过程总结
OkHttpCall对象对应Retrofit一次网络请求任务,它可视为Retrofit对OkHttp架构中Call的进一步封装,在触发Retrofit网络请求时,通过分析源码,过程可总结如下:
1).使用适配器生成的封装对象(内部封装了OkHttpCall) :ExecutorCallbackCall/Observable/Flowable等,来触发整个过程2). ——> 触发Retrofit 中OkHttpCall的网络请求过程3). ——> 发起OkHttp库的网络请求(使用OkHttp库中的Call对象)4). ——>OkHttpCall对OkHttp库的网络请求结果,使用数据转换器(Converter)转换为定义的目标类型5).——>适配器封装对象得到前面处理后的结果,根据适配器内部逻辑做后继处理:线程切换以及回调等。(例如ExecutorCallbackCall可用回调来返回结果,而Flowable等会按照RxJava中的流程将结果数据以事件流发送给观察者。)
#.三、适配器分析(Adapter)
适配器由适配器工厂生成,转换器由转换器工厂生成,Retrofit实例内部维护着一个适配器工厂和转换器工厂的列表:
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
在用注解定义接口时,接口的返回类型与泛型内的数据类型,会影响最终选用的适配器、解析器。Retrofit会自动解析,根据
接口的返回类型、泛型类型来选用合适的工厂类生成
适配器、转换器,但如果找不到
对应的
适配器、数据转换器
,会出错。所以,定义接口时,需要注意对应的适配器工厂、转换器工厂是否已添加。
//创建Retrofit实例
Retrofit.Builder builder = new Retrofit.Builder()
builder.baseUrl(“Ten▪Api - Tenapi.cn”)
.client(mOkHttpClient)
//配置使用的适配器
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
//配置使用的数据转化器
.addConverterFactory(new HttpConverterFactory());
mRetrofit = builder.build();
适配器可以在获取网络请求结果后,进行线程切换等后继流程处理。适配器由适配器工厂类构造,而适配器工厂类在构建Retrofit实例时指定,可自定义(继承
CallAdapter.Factory
),也可选用已有的适配器工厂类。Android中常用的有两种,默认的适配器工厂类:DefaultCallAdapterFactoryRxJava对应的适配器工厂类:RxJava3CallAdapterFactory(目前最新的rxjava是rxjava3,如果是rxjava2的话,对应的是RxJava2CallAdapterFactory)
1.Android平台默认的适配器工厂类DefaultCallAdapterFactory
例如定义以下接口,若使用默认适配器工厂,则此时返回的Call的具体实现是ExecutorCallbackCall。
@GET("weather/xxx/xxx")
Call<String> testGet1(@Query("params1") int arg1, @Query("params2") String arg2);
ExecutorCallbackCall也可以发起同步请求和异步请求,也是只能执行一次,这些都类似于OkHttp中的Call的特性。
ExecutorCallbackCall发起异步请求和同步请求的源代码如下,最终调用外部传入的回调接口,将结果传递出去:
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
@Override
public Response<T> execute() throws IOException {
return delegate.execute();
}
注意:最终通过callbackExecutor来调用回调,而callbackExecutor默认为MainThreadExecutor类型(源代码如下),所以默认情况下,回调会执行在主线程中。
static final class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
2.RxJava对应的适配器工厂类:RxJava3CallAdapterFactory
这是Retrofit比较常见的使用形式,使用RxJava最大的好处就是简化了线程切换的过程,使复杂的逻辑编写起来比较简单,而且代码也相对简介。
封装接口示例:
@GET("xxx/xxx/xxx")
Flowable<String> testGet3(@Query("params1") int arg1, @Query("params2") String arg2);
@FormUrlEncoded
@POST("xxx/xxx/xxx")
Observable<String> testPost1(@Field("params1") int arg1, @Field("params2") String arg2);
此时返回的适配器类型是
RxJava3CallAdapter,关键的源代码:
@Override
public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable =
isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return RxJavaPlugins.onAssembly(observable);
}
它相关的整个逻辑过程比较复杂,
当返回泛型类型不是Response时,内部还会封装一层
Observable,会有两层被观察者-观察者,内部那层观察者先接收到请求结果,进一步处理后,才会交给外部观察者处理。简单概括,就是会根据很多情况来决定最终封装返回的对象类型,无论如何,都是对OkHttpCall的进一步封装。
以下面返回
Flowable时为例,当为其指定观察者
observer.
时,最终会触发内部用OkHttpCall发起请求,获取请求结果后数据转化器将结果处理成接口定义的数据类型T,最终内部逻辑会会调用到
observer.onNext(T)、
observer.onComplete()等,将
结果T以事件形式发送给观察者。
Flowable<String> flowable = mHttpService.testGet3(2, "aa");
flowable.subscribeOn(Schedulers.io())//设置被观察者的执行线程
.observeOn(AndroidSchedulers.mainThread())//设置观察者处理过程的执行线程
.subscribe(new FlowableSubscriber<String>() {
@Override
public void onSubscribe(@NonNull Subscription s) {
}
//因为接口定义的返回数据泛型是String,
// 所以在网络结果返回时,Retrofit会使用对应转化器把结果转化成String
@Override
public void onNext(String s) {
//...对结果数据做处理...
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
});
#.四、数据转换器(Converter)
Retrofit会根据接口返回值定义的泛型类型来选取对应的数据转换器,在OkHttpCall获取到结果数据后,会使用转化器对其做处理,处理成接口返回处定义的泛型类型。
例如返回类型定义为Flowable<String>,然后存在支持返回泛型类型为String的数据转换器,则会选用该转换器将结果转化为String,最终提供给调用方使用。
1.默认的数据转换器工厂类BuiltInConverters
数据转化器由数据转化器工厂创建,默认的数据转化器工厂为BuiltInConverters。主要能处理泛型类型为ResponseBody、无返回结果(void)等情况,如果需要更复杂的情况处理,需要添加自定义数据转化器工厂,或者添加其它已有工厂类,如GsonConverterFactory等(需引入对应的库)。BuiltInConverters关键源代码:
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == ResponseBody.class) {
return Utils.isAnnotationPresent(annotations, Streaming.class)
? StreamingResponseBodyConverter.INSTANCE
: BufferingResponseBodyConverter.INSTANCE;
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;
}
if (checkForKotlinUnit) {
try {
if (type == Unit.class) {
return UnitResponseBodyConverter.INSTANCE;
}
} catch (NoClassDefFoundError ignored) {
checkForKotlinUnit = false;
}
}
return null;
}
2.参考默认转化器工厂类,根据需求自定义转化器工厂类
public class HttpConverterFactory extends Converter.Factory {
public static HttpConverterFactory create() {
return new HttpConverterFactory();
}
/**
* 获取结果数据的转换器,
* OkHttpCall在获取到结果数据后,会使用转化器对其做处理,处理成接口返回处定义的泛型类型
* @return
*/
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
//根据返回的泛型类型type,来判断选择用哪种转换器
if (type == ResponseBody.class) {
return isAnnotationPresent(annotations, Streaming.class)
? StreamingResponseBodyConverter.INSTANCE
: BufferingResponseBodyConverter.INSTANCE;
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;
}
if (type == String.class) {
return StringResponseConverter.INSTANCE;
}
if (type == HttpResult.class) {
return HttpResultConverter.INSTANCE;
}
return null;
}
/**
* 获取请求数据体的转换器
* 可以对请求体进行处理,在解析接口时,会调用到该方法
* @return
*/
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return RequestBodyConverter.INSTANCE;
}
@Override
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return ToStringConverter.INSTANCE;
}
private static final class VoidResponseBodyConverter implements Converter<ResponseBody, Void> {
static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter();
@Override
public Void convert(ResponseBody value) throws IOException {
value.close();
return null;
}
}
private static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
static final RequestBodyConverter INSTANCE = new RequestBodyConverter();
@Override
public RequestBody convert(RequestBody value) throws IOException {
//这里对请求数据没有做任何处理
return value;
}
}
private static final class StreamingResponseBodyConverter
implements Converter<ResponseBody, ResponseBody> {
static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();
@Override
public ResponseBody convert(ResponseBody value) throws IOException {
return value;
}
}
private static final class BufferingResponseBodyConverter
implements Converter<ResponseBody, ResponseBody> {
static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();
@Override
public ResponseBody convert(ResponseBody value) throws IOException {
try {
// Buffer the entire body to avoid future I/O.
Buffer buffer = new Buffer();
value.source().readAll(buffer);
return ResponseBody.create(value.contentType(), value.contentLength(), buffer);
} finally {
value.close();
}
}
}
private static final class StringResponseConverter implements Converter<ResponseBody, String> {
static final StringResponseConverter INSTANCE = new StringResponseConverter();
@Override
public String convert(ResponseBody value) {
try {
// Buffer the entire body to avoid future I/O.
return value.string();
} catch (IOException e) {
e.printStackTrace();
} finally {
value.close();
}
return null;
}
}
private static final class ToStringConverter implements Converter<Object, String> {
static final ToStringConverter INSTANCE = new ToStringConverter();
@Override
public String convert(Object value) {
return value.toString();
}
}
private boolean isAnnotationPresent(Annotation[] annotations,
Class<? extends Annotation> cls) {
for (Annotation annotation : annotations) {
if (cls.isInstance(annotation)) {
return true;
}
}
return false;
}
}
(声明:部分图片来自网络,侵删!)
版权声明:本文为u013914309原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。