Retrofit 2
使用方法
retrofit java interface 配置:
使用注解描述http请求:
替代URL的参数路径, 查询参数支持;
对象转变成请求体;
Multipart请求体和文件上传;
eg:
public interface MyApi {
@GET("users/{user}/repos")
Call<User> getUser()(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create()))
.build();
MyApi api = retrofit.create(MyApi.class);
Response<User> user = api.getUser().execute();
常用Api解析
请求方法
每个方法必须有Http注解,由请求方法和相关URL提供;有5个内置注解:
GET
,POST
,PUT
,DELETE
,HEAD
;注解中指定相关的URL资源;url上也可指定查询参数 eg:
@GET("users/list?sort=desc")
URL操作
请求的URl可以动态的使用替代块和方法参数去更新,{}中即是替代块,对应于
@Path
中的参数,参数可用@Query,复杂参数可使用@QueryMap; eg:
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") sort, [或者]@QueryMap Map<String, String> options);
请求体
使用@Body注解 可以指定一个对象使用哪种请求体;
在Retrofit
对象中,也可指定一个对象使用converter转换;没有converter,只有RequestBody被使用;
@POST("users/new")
Call<User> createUser(@Body User user);
Encoded编码 & Multipart
使用form-encoded 和multipart数据 可以声明方法;
@FormUrlEncoded
出现在方法上,代表form表单需要被encoded编码,每个key-value对需要被@Field
注解;
@Multipart
使用在方法上,代表multipart 文件请求,使用@Part
注解声明Parts 文件;
multipart parts 使用Retrofit Converters
转换器或者implement RequestBody 处理序列化;
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
请求头处理
@Headers
注解方法设置静态的头信息;key-value;
请求头可以被@Header
动态的更新,相关的参数可以被@Header
提供.value是null时,header将会被忽略;不为null时,value就是toString方法的值;
可以使用@HeaderMap
处理复杂的头信息组成;需要对每个请求添加头信息,使用拦截器;
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization, [或者]@HeaderMap Map<String, String> headers)
异步 & 同步
Call
对象代表http请求,可以被同步执行,也可以被异步执行;每个对象仅仅只会被使用一次,使用过的对象可以调clone()
方法复制一个新的可用对象;
Retrofit 常见配置
通过Retrofit
类可以将你的Api接口转换成回调对象;
Converters转换器
默认情况下,Retrofit仅仅将http 体 反序列化成
ResponseBody
类型(call的通用参数为response body类型),也只能接受@Body的RequestBody类型;可以使用
Converter.Factory
实例转换响应体,如果不关心内容可以使用Void
类型;Converter 支持其他类型:
Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
//使用GsonConverterFactory 转换器进行反序列化;
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
//生成retrofit接口的实现类;
GitHubService service = retrofit.create(GitHubService.class);
自定义converter
建造一个继承
Converter.Factory
类,通过对象构造自定的适配器;
@注解
- GET
- PUT
- POST
- PATCH
- HEAD
- DELETE
- OPTIONS
- HTTP 自定义
@Url
:方法参数,可以操作动态的Url,忽略注解中的路径并且在第一个参数中使用Url注解;
@POST() Call<BaseBean<LogBean>> getLogBeanDynamic( @Url() String url, @Query("mobile") String mobile, @Query("password") String password);
@Path
:方法参数,替换Url的一部分;被替换的部分使用{foo}
花括号所表示;
@Query
:方法参数,添加到Url的查询参数;
@Body
:方法参数,表示请求体,被Converter.Factory
的对象所转换;RequestBody
可被用于raw repersentation;更改请求体格式:
@FormUrlEncoded
:所有被@Field
注解的参数,都会被encode编码成 Form-encoded 的键值对;
@MultiPart
:所有被@Part
注解的参数,都会被指定为 兼容的multipart data >parts;
Retrofit2 源码浅析
Retrofit
该类为了
动态代理 by create
(改建)一个用于http调用的java 接口,为了使用声明方法上的注解去定义如何请求; 使用create (Builder)方法生成一个动态代理实现类;
//有关属性
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
//网络工厂,一般实现是OkHttpClient;
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
//转换ResponseBody数据,如GsonConverter;
final List<Converter.Factory> converterFactories;
//返回值的再次封装处理,原始Call对象,可以扩展为RxJavaAdapter;
final List<CallAdapter.Factory> adapterFactories;
//线程池
final @Nullable Executor callbackExecutor;
//是否迫切更新;
final boolean validateEagerly;
//动态代理自定义的http调用接口类
public <T> T create(final Class<T> service) {
//判断是否符合条件,必须是接口,并且接口方法的数量>0
Utils.validateServiceInterface(service);
//是否需要迫切更新配置中所有方法;
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//返回retrofit 定义接口的动态代理对象(AOP)
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, @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);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//缓存封装方法及请求参数的对象 ServiceMethods;
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
//执行掉用请求的Call对象;
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//使用转换Call对象的适配器,如retrofit 接口方法返回值为Observable,即 使用的是Rxjava的适配器;
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
Builder:
Retrofit的构造类可指定
OkHttpClient(Call.Factory ),
BaseUrl,
Converter.Factory,
Executor;
设置BaseUrl注意:
:baseurl 应该必须以 / 结尾;
eg:
1. 端点的值不包含 / 代表相对路径,将把有path components的路径拼接到BaseUrl上;
Base URL: http://example.com/api/<br>
Endpoint: foo/bar/<br>
Result: http://example.com/api/foo/bar/
<b>Incorrect:</b><br>不以/结尾会强制找到/结尾;
Base URL: http://example.com/api<br>
Endpoint: foo/bar/<br>
Result: http://example.com/foo/bar/
2. 端点的值包含 / 代表是绝对路径,结果仅保留baseUrl的Host ,忽略特殊的路径组件;
Base URL: http://example.com/api/<br>
Endpoint: /foo/bar/<br>
Result: http://example.com/foo/bar/
Base URL: http://example.com/<br>
Endpoint: /foo/bar/<br>
Result: http://example.com/foo/bar/
3. 端点值是一个Url全路径,结果是端点的host代替baseUrl的host,并且值的约束也代替BaseUrl的约束;
Base URL: http://example.com/<br>
Endpoint: https://github.com/square/retrofit/<br>
Result: https://github.com/square/retrofit/
Base URL: http://example.com<br>
Endpoint: //github.com/square/retrofit/<br>
Result: http://github.com/square/retrofit/ (note the scheme stays 'http')
ServiceMethod
可以在一个http调用中进行invocation(即实现对接口的动态代理);
缓存的 retrofit接口类 方法及方法注解及需要的请求参数等;
对注解进行解析,对每个接口中的方法进行处理,即进行动态代理的方法增强;返回值的处理扩展:
内部通过方法
addCallAdapterFactory
添加的工厂创建CallAdapter.Factory
,没有设置则使用默认的工厂,默认由PlatForm提供;可以添加扩展RxJavaAdapter;
数据的转换处理:
通过方法
addConverterFactory
添加工厂创建Converter.Factory
,默认添加了BuiltInConverters
可以添加扩展GsonConverter;
OkHttpCall
retrofit的Call请求具体实现;
本身继承于retrofit2.Call
,内部通过okhttp3.Call
调取网络;
okhttp3.Call
的对象由createRawCall方法构建;okhttp3的联网处理:
newCall方法由
okhttp3.Call.Factory
工厂构建okhttp3.Call
对象,实现类callFactory 即构建Retrofit传入的OkHttpClient
工厂对象,newCall的具体实现在OkHttpClient
中;OkHttpClient工厂具体构建的类是
RealCall
类;
RealCall implements okhttp3.Call
是真正调取接口的实现;
//OkhttpCall中 private okhttp3.Call createRawCall() throws IOException { //将retrofit接口中方法的参数转换成Request对象; Request request = serviceMethod.toRequest(args); //通过传入的`callFactory`(一般即OkHttpClient对象)构建 //`okhttp3.Call`对象(一般为RealCall对象) okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; } //Retrofit中 public Builder client(OkHttpClient client) { return callFactory(checkNotNull(client, "client == null")); } public Builder callFactory(okhttp3.Call.Factory factory) { this.callFactory = checkNotNull(factory, "factory == null"); return this; } /** OkHttpClient中 * Prepares the {@code request} to be executed at some point in the future. */ @Override public Call newCall(Request request) { return new RealCall(this, request, false /* for web socket */); }
Platform
判断anroid平台还是java8平台,动态代理中没有使用,构造链中设置默认值使用,设置
Executor
和CallAdapter.Factory
默认值;
Retrofit2:converter-gson Gson格式转换器
GsonConverterFactory
,自定义的Gson Converter.Factory
重写工厂方法responseBodyConverter
&&requestBodyConverter
;
addConverterFactory(Converter.Factory)
添加至Retrofit的对象中;
GsonRequestBodyConverter (implement Converter< T, RequestBody>)
使用Gson的
TypeAdapters(type adapter for basic types)
处理,找到处理目标Type的TypeAdapter;重写抽象方法convert
@Override public RequestBody convert(T value) throws IOException { //创建流,将value写入到typeAdapter Buffer buffer = new Buffer(); Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); JsonWriter jsonWriter = gson.newJsonWriter(writer); adapter.write(jsonWriter, value); jsonWriter.close(); return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); }
GsonResponseBodyConverter
同上,处理响应数据,
OkHttpCall
将rawResponse - > Response ,回调出去;
@Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { return adapter.read(jsonReader); } finally { value.close(); } }
TypeAdapter
gson中用于Java objects 和Json的相互转换;
可继承该类实现自定义的转换;
read(JsonReader)必须精确读一个值,常用方法 nextBoolean(),nextDouble(),nextInt(),nextLong(),nextString(),nextNull();
write(JsonWrite,Object)
必须精确的写一个值,常用方法
value(),nullValue();对于数组,类型适配器先开始调用
beginArray()
转化所有的元素,结束时调用endArray()
;对于对象,开始时调用beginObject
,转换对象,结束时使用endObject()
;使用nullSafe()方法注册类型适配器,如果Gson instance已经配置了 GsonBuilder serializeNulls(),空值将被写于最终的文档中;否则 value 将被自动忽略(这种情况下,typeadapter 必须处理空值);
eg: Gson 转换 Point <-> JSON "5","8" public class PointAdapter extends TypeAdapter<Point> { * public Point read(JsonReader reader) throws IOException { * if (reader.peek() == JsonToken.NULL) { * reader.nextNull(); * return null; * } * String xy = reader.nextString(); * String[] parts = xy.split(","); * int x = Integer.parseInt(parts[0]); * int y = Integer.parseInt(parts[1]); * return new Point(x, y); * } * public void write(JsonWriter writer, Point value) throws IOException { * if (value == null) { * writer.nullValue(); * return; * } * String xy = value.getX() + "," + value.getY(); * writer.value(xy); * } * }}</pre> * 使用自定义的type adapter 必须使用GsonBuilder注册 GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Point.class, new PointAdapter()); // if PointAdapter didn't check for nulls in its read/write methods, you should instead use // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe()); ... Gson gson = builder.create();
Retrofit2:adatpter-rxjava Rxjava适配器;
RxJava2CallAdapterFactory
,自定义的CallAdapter.Factory
工厂,对返回值Call对象再次处理;
addCallAdapterFactory(CallAdapter.Factory)
添加至Retrofit的对象中;使用RxJava2创建Observable,添加当前工厂,允许从service 方法返回
Observable
,Flowable
,
Single
,Completable
,
Maybe
对象,代替Call对象;
Observable< User> :
onNext
-> 反序列化数据返回 响应码2XX响应;
onError
->HttpException 返回 非2XX的响应,网络错误返回IoException;Observable< Response< User>> :
onNext
-> 所有Http responses的Response对象;onError
-> 网络错误IoExceptionObservable< Result< User>> :
onNext
->所有的Http responses和error 的Result对象;
retrofit2.CallAdapter $ Factory
[动态代理]改造返回值
Call
的响应类型;1.抽象方法 Type responseType();
此adapter使用 转换Http response body 成java对象的返回类型;eg:
Call -> Repo ;2.抽象方法 T adapt(Call call);
返回call的代理对象;
CallAdapter.Factory
工厂类的抽象方法
//返回一个接口方法返回类型的call adapter public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); //提取通用类型的上限, Map<String, ? extends Runnable> //index 1 -> Runnable protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } //提起raw class type,List<? extends Runnable> //->List.class protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); }
RxJava2CallAdapter
Rxjava的包,
CallAdapter.Factory
get抽象方法返回的CallAdapter;responseType() 获取的返回类型的上限;
adapt()代理call对象
(分为返回值< R>,< Response< R>>,< Result< R>>):1.
<Result<R>>
:
封装< Response< R>>
ResultObservable
(extends Observable< Result< T>>)
使用RxJava特点->
ResultObservable.ResultObserver
(implements Observer< Response< R>>)->
onNext
中使用Result
的方法将Response转成Result;2.
<R>
:
封装< Response< R>>
BodyObservable
(extends Observable< T>)
使用RxJava特点->
BodyObservable.BodyObserver
(implements Observer< Response< R>>) ->onNext
response.body()3.
<Response<R>>
:异步
CallEnqueueObservable
(extends Observable< Response< T>>);同步
CallExecuteObservable
(extends Observable< Response< T>>)最后使用RxJava的订阅-发布;
详细见RxJava章节