Retrofit2组件分析

目录

  1. 1. 简介
  2. 2. 关键分析
  3. 3. 一些细节

1. 简介

Retrofit2是由Square公司开源的HTTP网络请求框架的封装,最近阅读v2.4.0源码,在此记录一下。
官方使用的Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
// SYNCHRONOUS
repos.execute()
// ASYNCHRONOUS
repos.enqueue(...)

2. 关键分析

关键代码是Retrofit#create(final Class<T> service),核心就是通过动态代理把客户端定义的interface构建出HttpServiceMethod对象,重要的成员变量有:

  • callFactory : okhttp.Call.Factory 负责底层HTTP网络请求,默认是OkHttp组件
  • requestFactory : RequestFactory 解析各种注解生成HTTP Request的参数
  • callAdapter : CallAdapter<ResponseT, ReturnT> 用于把ResponseT`转化成ReturnT`,CallAdapter.Factory是其工厂类。Retrofit默认实现可查看ExecutorCallAdapterFactory类,如果interface函数返回Call<?>时,默认使用该Adapter,实现了OkHttpCallExecutorCallbackCall的转化,前者表示底层具体HTTP请求,后者用于把回调从非主线程切换到主线程,所以异步请求的回调都是在主线程中。
  • responseConverter : Converter<ResponseBody, ResponseT> 用于HTTP请求返回的ResponseBody转化成ResponseT对象,通常使用gson传输数据,所以提供GsonResponseBodyConverter。系统同样提供工厂类Converter.Factory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
abstract class Factory {

/**
* 用于ResponseBody => type类型(比如使用Gson进行解析),如果返回null则表示Factory不支持该type
*/
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
Annotation[] annotations, Retrofit retrofit) {...}

/**
* HTTP Request生成时,type类型 => RequestBody,如果返回null表示Factory不支持该type
* 用于使用@Body/@Part/@PartMap注解的参数
*/
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {...}
/**
* HTTP Request生成时,type类型 => RequestBody,如果返回null表示Factory不支持该type
* 用于使用@Field/@FieldMap/@Header/@HeaderMap/@Path/@Query/@QueryMap注解的参数
*/
public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {...}

具体参考Retrofit默认实现BuiltInConverters。Demo中Call<List<Repo>>#execute()可根据ExecutorCallbackCall#execute() -> OkHttpCall#execute()的流程查看源码。

3. 一些细节

  • URL拼接
    Retrofit.Builder#baseUrl()可以指定Base URL,其必须以”/“结尾;通过@GET(xxx)/@Url xxx等可以指定Endpoint(@Url优先级高于@Get(xxx)),两者拼接形成请求的URL,共有以下几种情况:
Base URL Endpoint Result
http://example.com/api/ foo/bar(相对路径) http://example.com/api/foo/bar/
http://example.com/api/ /foo/bar(绝对路径) http://example.com/foo/bar/
http://example.com/api/ https://foo.com/bar(完整URL) https://foo.com/bar
http://example.com/api/ //foo.com/bar(无scheme) http://foo.com/bar
  • HTTP注解
Annotation 说明
@GET/@PUT/@POST/@PATCH/@DELETE/@HEAD/@OPTION 作用于函数,表示7种HTTP请求类型
@HTTP 作用于函数,自定义HTTP请求
@FormUrlEncoded 作用于函数,标明request使用url encode,Content-Type为application/x-www-form-urlencoded
@Field/@FieldMap 作用于函数参数,配合@FormUrlEncoded使用,表示Named pair。默认使用Url encode编码,通过encoded=true可关闭。@Field支持变长参数,比如@Field("name") String... names 转化为 name=para0&name=para1...。注意null值会被忽略
@Headers/@Header/@HeaderMap 用于request增加Header参数,其中@Headers用于函数,其他两个用于函数参数。注意相同Header不会override
@Query/@queryMap/@QueryName 作用于函数参数,表示query parameter。默认使用Url encode编码,通过encoded=true可关闭。@QueryName用于没有value的情况,@Query/@QueryName支持变长参数,null值同样会被忽略
@Body 作用于函数参数,用途是PUST/PUT请求时不使用form表单,直接控制request body,需要有对应的converter类配合,默认是支持okhttp RequestBody
@Path URL Path占位符参数,比如
@GET("/image/{id}") Call<ResponseBody> example(@Path("id") int id);
@Url 作用于函数参数,可动态修改base URL,优先级最高
@Streaming 作用于函数,Retofit默认会把reponse body放于内存中,使用@Streaming可以直接返回底层okhttp.ResponseBody对象,注意需要手动控制close
@Multipart 作用于函数,标明request body是multi-part
@Part/@PartMap 作用于函数参数,通过value设置每个part的name,encode设置每个part的Content-Transfer-Encoding。默认支持参数类型MultipartBody.Part和RequestBody,其他类型需要converter
  • 如果不关心网络请求返回的数据,可使用Call<Void>/Call<Unit>

  • 整体Retrofit2可扩展性非常好,各种参数的转化/返回数据的解析通过Converter.Factory/Converter接口实现,参考com.squareup.retrofit2:converter-gson对Gson的支持。如果对返回数据的承载类型有所要求,可实现CallAdapter.Factory/CallAdapter接口,比如网络请求返回一个LiveData对象,从google sample截取如下(可用于自己工程中):

    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
    class LiveDataCallAdapterFactory : Factory() {
    override fun get(
    returnType: Type,
    annotations: Array<Annotation>,
    retrofit: Retrofit
    ): CallAdapter<*, *>? {
    if (Factory.getRawType(returnType) != LiveData::class.java) {
    return null
    }
    val observableType = Factory.getParameterUpperBound(0, returnType as ParameterizedType)
    val rawObservableType = Factory.getRawType(observableType)
    if (rawObservableType != ApiResponse::class.java) { // 网络返回数据解析为ApiResponse
    throw IllegalArgumentException("type must be a resource")
    }
    if (observableType !is ParameterizedType) {
    throw IllegalArgumentException("resource must be parameterized")
    }
    val bodyType = Factory.getParameterUpperBound(0, observableType)
    return LiveDataCallAdapter<Any>(bodyType)
    }
    }

    class LiveDataCallAdapter<R>(private val responseType: Type) :
    CallAdapter<R, LiveData<ApiResponse<R>>> {

    override fun responseType() = responseType

    override fun adapt(call: Call<R>): LiveData<ApiResponse<R>> {
    return object : LiveData<ApiResponse<R>>() {
    private var started = AtomicBoolean(false)
    override fun onActive() {
    super.onActive()
    if (started.compareAndSet(false, true)) {
    call.enqueue(object : Callback<R> {
    override fun onResponse(call: Call<R>, response: Response<R>) {
    postValue(ApiResponse.create(response))
    }

    override fun onFailure(call: Call<R>, throwable: Throwable) {
    postValue(ApiResponse.create(throwable))
    }
    })
    }
    }
    }
    }
    }