Retrofit学习

what

网络请求框架,底层是使用OkHttp请求实现的。通过动态代理来创键创键解耦的请求,具体的请求交给OkHttp。同时支持Rxjava,以及各种格式的转换。官方文档地址:Retrofit

how

定义请求API

创键接口,以及请求定义方法

1
2
3
4
5
6
7
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
public interface TestApi {
@GET("article/list/0/json")
Call<ResponseBody> getArticle();
}

请求

1
2
3
4
5
6
7
8
9
10
11
Retrofit retrofit = new Retrofit.Builder().baseUrl(url).build();
TestApi testApi = retrofit.create(TestApi.class);
Response<ResponseBody> response = testApi.getArticle().execute();
System.out.println(response.code());
System.out.println(response.isSuccessful());
System.out.println(response.body().string());

//ouput
200
true
数据

请求方法声明

@GET

get请求

1
2
@GET("article/list/0/json")
Call<ResponseBody> getArticle();

@POST

1
2
@POST("login")
Call<ResponseBody> login();

其它的请求方式

PUT, DELETE, and HEAD

Url构建

@Path

当我们需要对路径进行动态参数,比如`/page/1/json,这种带参路径,好像和springMVC的挺像,这样就会替换了

如果路径种出现了多个参数,映射形参一定要按照顺序写,不然会出问题

1
2
3
@GET("article/list/{pageNum}/json")
Call<ResponseBody> getArticle(@Path("pageNum") int pageNum);
//article/list/0/json

@Query

url查询参数

1
2
3
4
@GET("article/list/0/json")
Observable<BaseResponse<Articles>> getArticlesByCid(@Query("cid") int cid);

//article/list/0/json?cid=1

@Path和@Query共同出现时,映射形参一定要先path再query的写,不然真的会出问题呃呃

@QueryMap

这个和@Query类似,传入map表示查询参数

1
2
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

请求体构建

@Field和@FormUrlEncoded

post提交表单得这样

1
2
3
4
5
6
@POST("user/login")
@FormUrlEncoded
Observable<BaseResponse<Login>> login(
@Field("username") String userName,
@Field("password") String password
);

@Body

标注请求体

自定义RequestBody,参看OkHttp,这里不需要FormUrlEncoded

1
2
@POST("xxxxxxx")   
Call<Object> login( @Body RequestBody body);

如果没有加入转化器的话就只能修饰RequestBody

请求体为json对象

1
2
@POST("xxxxxxx")   
Call<Object> login( @Body JSONObject parmas );

@Multipart 和 @Part

其实也是完全对应OkHttp的

1
2
3
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

请求头构建

@Headers 和 @Headers @HeaderMap

添加单个Header头

1
2
3
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

添加多个

1
2
3
4
5
6
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

动态赋值Header

1
2
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

动态添加多个

1
2
@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)

基本上一一对应着OkHttp,真好用啊

转换器

默认Retrofit是响应构建是发出RequestBody和响应ResponseBody,如果需要进一步对响应进行转换处理就需要用到转换器了。

大概有这么几种把:(官网拷的)

  • 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

Gson

1
2
3
4
5
6
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();

GitHubService service = retrofit.create(GitHubService.class);

call适配器

联动Rxjava

导入依赖

1
2
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'

添加

1
2
3
4
5
6
7
8
9
10
@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient client) {
return new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())//Gson
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//Rxjava
.build();
}

Gson和Rxajva结合后,接着我们请求API就可以这样写了

定义和响应对应的实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class BaseResponse<T> {
private int errorCode;
private T data;
private String errorMsg;

public int getErrorCode() {
return errorCode;
}

public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}

public String getErrorMsg() {
return errorMsg;
}

public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}

@Override
public String toString() {
return "BaseResponse{" + "errorCode=" + errorCode + ", data=" + data + ", errorMsg='" + errorMsg + '\'' + '}';
}
}

定义API

1
2
3
4
public interface HomeApis {
@GET("article/list/{pageNum}/json")
Observable<BaseResponse<Articles>> getArticles(@Path("pageNum") int pageNum);
}

发送请求处理

1
2
3
4
5
6
7
8
9
10
addRxSubscribe( mModel.getArticles(pageNum)
.compose(RxUtil.rxSchedulerhelper())
.compose(RxUtil.handleResult())
.subscribeWith(new BaseObserver<Articles>(getView()) {
@Override
public void onNext(Articles articles) {
super.onNext(articles);
getView().showArticles(articles.getDatas());
}
}));

顺带着我们可以将线程变换,以及初步的响应处理进行复用,使用Rxjava进行流处理就行,甚至我们可以再重写一个Observer等等(我也是看别人代码这样学的写的哈哈)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class RxUtil {
public static <T> ObservableTransformer<T, T> rxSchedulerhelper(){
return observable -> observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}

public static <T> ObservableTransformer<BaseResponse<T>,T> handleResult() {
return upstream ->
upstream.flatMap(baseResponse -> {
if (baseResponse.getErrorCode() == 0) {
return createObservable(baseResponse.getData());
}
return Observable.error(new ApiException(baseResponse.getErrorCode(), baseResponse.getErrorMsg()));
});
}

private static <T> Observable<T> createObservable(T data) {
return Observable.create(emitter -> {
try {
emitter.onNext(data);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
}
}

why

这个大概的原理就是通过动态代理取生成api,读取注解,组装request,构建成真正的OkHttp的Call,继而调用。

创键API

1
2
3
4
5
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);

上面的Builder模式就先不看了,直接从create开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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();
private final Object[] emptyArgs = new Object[0];
@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)) {//默认就返回fasle
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);//真正调用这里
}
});
}

动态代理返回的对象,实际调用的调用是根据具体的methdo去构建的ServiceMethod.invoke方法。

先看下loadServiceMethod(method)吧,首先去map查询,没查询到就解析构建

1
2
3
4
5
6
7
8
9
10
11
12
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);//从缓存map拿去
if (result != null) return result;//拿到返回
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);//继续拿
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);//解析注解构建
serviceMethodCache.put(method, result);//入缓存map
}
}
return result;//返回
}

接着到ServiceMethod#parseAnnotations

1
2
3
4
5
6
7
8
9
10
11
12
13
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//解析注解并获得Request工厂
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

接着走向RequestFactory#parseAnnotations去解析注解

1
2
3
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}

直接看build方法

1
2
3
4
5
6
7
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);//遍历每个注解,解析
}
//。。。省略
return new RequestFactory(this);
}

调用parseMethodAnnotation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
}
}

在这里就很明显,根据不同的注解进行解析。反正都是去获得注解的值啊生成,最终在invoke组建真正的call尽调用。

后面都是直接装配之前解析的东西了

最后看看HttpServiceMethod#invoke

1
2
3
4
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}

这里的话就会去返回OkHttpCall里面调用产生的原生的OkHttpCall,外面包裹着callAdapter,这里是用于转换Call的,即又进行了一次适配,不过这次的对象是call,这个为啥要适配呢,默认的话我们返回的是Call<>这种样子的,但是我们可以给定Adapteri进行对Call进一步处理,比如RxJava适配器

1
2
3
4
5
6
7
8
Retrofit provideRetrofit(OkHttpClient client) {
return new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//这个
.build();
}

然后我们就能产生Observale<>的东西了。

继续看OkHttpCall是个啥东西。它实现了Retrofit的Call,里面包裹着真正的OkHttp的Call。所以形式很明了了。

通过调Retorfit的Call的execute或者enqueue,去组建真正的Call然后调用到OkHttp了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;

synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;

if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}

call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();//组建真正的OkHttp的Call
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}

if (canceled) {
call.cancel();
}

return parseResponse(call.execute());//调用
}

原理呢就大概是这样。适配器和转换器也很明了

Gson相应体转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;

GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}

@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
}

RxJava Call适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
private final Type responseType;
private final @Nullable Scheduler scheduler;
private final boolean isAsync;
private final boolean isResult;
private final boolean isBody;
private final boolean isFlowable;
private final boolean isSingle;
private final boolean isMaybe;
private final boolean isCompletable;

RxJava2CallAdapter(Type responseType, @Nullable Scheduler scheduler, boolean isAsync,
boolean isResult, boolean isBody, boolean isFlowable, boolean isSingle, boolean isMaybe,
boolean isCompletable) {
this.responseType = responseType;
this.scheduler = scheduler;
this.isAsync = isAsync;
this.isResult = isResult;
this.isBody = isBody;
this.isFlowable = isFlowable;
this.isSingle = isSingle;
this.isMaybe = isMaybe;
this.isCompletable = isCompletable;
}

@Override public Type responseType() {
return responseType;
}

@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);
}
}