ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] # 概述 * Retrofit 对网络请求接口进行了封装,实际执行网络请求的依然是 OkHttp * Retrofit 接口层实际是对 OkHttp 中 Request 的封装,采用注解的形式来描述网络请求参数 * Retrofit采用动态代理模式创建请求接口对象,请求执行调用接口方法时,Retrofit会根据注解创建相应的Call对象,接下来使用OkHttp发起请求 # Retrofit 使用 在学习 Retrofit 使用之前我们先回顾下 OKHttp 的工作方式,一个标准的 OkHttp 请求格式为 `mClient.newCall(request).enqueue(callback);`。可以看到,首先构造出 Request 对象(经过封装的 http 请求信息)和 OkHttpClient 对象,然后调用 OkHttpRequest 的 newCall 方法,传入封装的 request 对象后,就得到了一个 RealCall 对象,然后就可以操作 RealCall 对象执行请求、取消等一系列操作。 而 Retrofit 对于 http 请求信息并不是直接构造成 Request 对象,而是声明为接口的形式,相关请求信息(地址、方法、请求头等)使用注解和参数的形式传入。同样会有一个 Retrofit 客户端对象,调用其 create 方法会帮我们创建出请求接口的实例对象,接着调用请求接口实例对象的方法发起请求。流程如下: * 创建请求接口 * 构建 Retrofit 实例 * 创建网络请求接口实例 * 发起网络请求 可以看到,Retrofit 对于 OkHttp 中的 Request 进行了抽象化为接口,在使用时首先构造出请求对象。一个标准的 Retrofit 请求格式为 `mRetrofit.create(RequestService.class).doGet("params").enqueue(callback)`,其中 doGet 方法返回的是一个 Call 对象。 # Retrofit 注解使用 来源:[Retrofit2.0中注解使用套路](https://blog.csdn.net/stven_king/article/details/52372172)、[Retrofit网络请求参数注解,@Path、@Query、@QueryMap](https://www.jianshu.com/p/7687365aa946) ## 静态 url 请求 ### GET 请求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); public interface GitHubService { //无参数 // https://api.github.com/users/xcy396/repos @GET("users/xcy396/repos") Call<List<Repo>> listRepos(); //少数参数 //https://api.github.com/users/xcy396/repos?time={time} @GET("users/xcy396/repos") Call<List<Repo>> listRepos(@Query("time") long time); //参数较多 //https://api.github.com/users/xcy396/repos?time={time}&author={author}... @GET("users/xcy396/repos") Call<List<Repo>> listRepos(@QueryMap Map<String, String> params); } ``` ### POST 请求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); public interface GitHubService { //无参数 @POST("users/xcy396/repos") Call<List<Repo>> listRepos(); //少数参数(参数拼接在url后面) //https://api.github.com/users/xcy396/repos?time={time} @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@Query("time") long time); //少数参数(使用表单形式提交,参数在请求体) @FormUrlEncoded @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@Field("time") long time); //参数较多(使用表单形式提交,参数在请求体) @FormUrlEncoded @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@FieldMap Map<String, String> params); //参数较多(使用Plain形式提交,参数在请求体) @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@Body Map<String, String> params); } ``` ### DELETE请求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); public interface GitHubService { //无参数 // https://api.github.com/users/xcy396/repos @DELETE("users/xcy396/repos") Call<List<Repo>> listRepos(); //少数参数(参数拼接在url后面) //https://api.github.com/users/xcy396/repos?time={time} @DELETE("users/xcy396/repos") Call<List<Repo>> listRepos(@Query("time") long time); //参数较多(使用Plain形式提交,参数在请求体) //https://api.github.com/users/xcy396/repos @HTTP(method = "DELETE", path = "users/xcy396/repos", hasBody = true) Call<List<Repo>> listRepos(@Body Map<String, String> params); } ``` 注意: * 当@GET或@POST注解的url为全路径时(可能和baseUrl不是一个域),会直接使用注解的url的域 * 如果请求为 Post 实现,那么最好传递参数时使用@Field、@FieldMap和@FormUrlEncoded。因为@Query和@QueryMap都是将参数拼接在url后面的,而@Field或@FieldMap传递的参数时放在请求体的 * 使用@Path时,path对应的路径不能包含”/”,否则会将其转化为%2F。在遇到想动态的拼接多节url时,还是使用@Url ## 半静态 url 请求 https://api.github.com/users/{user}/repos ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build() public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } ``` ## 动态 url 请求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build() public interface GitHubService { @GET Call<List<Repo>> listRepos(@Url String user); } ``` # 注解说明 ## 网络请求方法 网络请求方法注解对应于 Http 请求方式,包括: 注解名称|说明 ---|--- @GET|从服务器取出资源(一项或多项) @POST|在服务器新建一个资源 @PUT|在服务器更新资源(客户端提供改变后的完整资源) @DELETE|从服务器删除资源 @HEAD|获取资源的元数据 @OPTIONS|获取信息,关于资源的哪些属性是客户端可以改变的 ## 标记类 标记类注解,作用于网络请求接口的方法,包括: 注解名称|说明 ---|--- @FormUrlEncoded|表示请求体是一个 Form 表单 @Multipart|表示请求体是一个 Multipart 表单 @Streaming|表示返回的数据以流的形式返回 ### 提交表单 代码示例: ```java public interface Add2GankApi { @POST("add2gank") @FormUrlEncoded Call<ResponseBody> add(@Field("url") String url, @Field("desc") String desc, @Field("who") String who, @Field("type") String type, @Field("debug") boolean debug); } ``` ### 提交 Multipart 代码示例: ```java public interface Add2GankApi { @POST("add2gank") @Multipart Call<ResponseBody> addFile(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file); } ``` 其中 @Part 后面括号内的字符用于生成每个 Part 请求头 Content-Disposition 字段的信息,接收的参数则用于生成请求体。生成的 Content-Disposition 字段示例如下: ```plain Content-Disposition: form-data; name=”name” 或 Content-Disposition: form-data; name=”test”; filename=”test.txt” ``` 调用: ```java RequestBody name = RequestBody.create(mediaType, "Tom"); RequestBody age = RequestBody.create(mediaType, "12"); MultipartBody.Part file= MultipartBody.Part.createFormData("test", "test.txt", RequestBody.create(mediaType, new File("test.txt"))); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .addFile(name, age, file); ``` ## 网络请求参数注解 网络请求参数注解包括: 注解名称|说明 ---|--- @Headers|添加固定的请求头 @Header|添加不固定值的 Header 示例: ```java @GET("user") Call<ResponseBody> getUser(@Header("Authorization") String authorization); @GET("user") @Headers("Authorization: authorization") Call<ResponseBody> getUser(); ``` 注解名称|说明 ---|--- @Body|发送自定义数据类型(非表单数据)给服务器 注解名称|说明 ---|--- @Field|表单字段 @FieldMap|表单字段 示例: ```java @POST("add2gank") @FormUrlEncoded Call<ResponseBody> add(@Field("username") String name, @Field("age") int age); @POST("add2gank") @FormUrlEncoded Call<ResponseBody> add(@FieldMap Map<String, Object> map); ``` 使用: ```java RetrofitUtil.getInstance() .createAdd2GankApiRequest() .add("Tom", 18); Map<String, Object> map = new HashMap<>(); map.put("username", "Tom"); map.put("age", 18); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .add(map); ``` 注解名称|说明 ---|--- @Part|Multipart 表单字段 @PartMap|Multipart 表单字段 示例: ```java public interface Add2GankApi { @POST("add2gank") @Multipart Call<ResponseBody> addFile(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file); @POST("add2gank") @Multipart Call<ResponseBody> addFile(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file); } ``` 使用 ```java // Part 使用 RequestBody name = RequestBody.create(mediaType, "Tom"); RequestBody age = RequestBody.create(mediaType, "12"); MultipartBody.Part file= MultipartBody.Part.createFormData("test", "test.txt", RequestBody.create(mediaType, new File("test.txt"))); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .addFile(name, age, file); // PartMap 使用 Map<String, RequestBody> map = new HashMap<>(); map.put("name", name); map.put("age", age); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .addFile(map, file); ``` 注解名称|说明 ---|--- @Query|作用于 GET 方法的查询参数,作用于 Url @QueryMap|作用于 GET 方法的查询参数,作用于 Url 示例: ```java @GET("user") Call<ResponseBody> getUser(@Query("age") int age, @Query("gender") int gender); ``` 在 baseUrl = "http://gank.io/api/",调用 getUser 方法传参为(18, 0)时,请求 url 为 "http://gank.io/api/?age=18&gender=0" 注解名称|说明 ---|--- @Path|URL 地址的缺省值 @Url|直接设置 Url 变量 示例: ```java @GET("users/{user}/photos") Call<ResponseBody> getUserPhotos(@Path("user") String user); ``` 当传参为 "tom" 时,请求 url 为 "http://gank.io/api/users/tom/photos"。 示例: ```java @GET Call<ResponseBody> getUSer(@Url String url, @Query("age") int age); ``` 当有 @Url 注解时,@GET 传入的 Url 可以忽略。 # 源码分析 ## 具体分析 ### Retrofit 的构造 我们首先来从 Retrofit 对象的构建说起。和 OkHttpClient 一样,Retrofit 对象我们也建议项目中只维护一个。一个标准的 Retrofit 构造代码如下: ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); ``` 可以看到 Retrofit 也使用了建造者模式, ### Request对象的封装与Call的生成 一个标准的 Retrofit 请求格式为 `mRetrofit.create(RequestService.class).doGet("params").enqueue(callback)`。 在OkHttp中,我们手动创建了Request对象,并创建Call对象。而在Retrofit中,则是首先声明请求接口(通过注解声明请求地址、方法、请求体等),Retrofit的create方法会为我们创建一个请求接口对象。先来看看create方法: ```java 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, @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); } // 创建 ServiceMethod ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); // 创建 OkHttpCall OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); // 返回 return serviceMethod.adapt(okHttpCall); } }); } ``` Retrofit使用了动态代理模式(可参考[代理模式]([http://wiki.xuchongyang.com/1091154#2\_\_57](http://wiki.xuchongyang.com/1091154#2__57))),create方法返回了请求接口的代理对象。当我们调用代理对象的方法时,会被拦截,执行最后三行关键代码,下面我们先看看ServiceMethod类,再依次看最后三行代码。 1、ServiceMethod类就是我们定义的请求接口到Call的转换适配器。 ```java public class ServiceMethod { Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); this.parameterTypes = method.getGenericParameterTypes(); this.parameterAnnotationsArray = method.getParameterAnnotations(); } public ServiceMethod build() { //... // 解析方法注解 for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } //... int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; if (Utils.hasUnresolvableType(parameterType)) { throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType); } Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) { throw parameterError(p, "No Retrofit annotation found."); } // 解析参数 parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } // ... return new ServiceMethod<>(this); } 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); if (!Void.class.equals(responseType)) { throw methodError("HEAD method must use Void as response type."); } } 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("@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) { if (isFormEncoded) { throw methodError("Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError("Only one encoding annotation is allowed."); } isFormEncoded = true; } } } ``` 从代码中可以看到,我们使用注解定义的所有信息,在构造ServiceMethod时进行了解析并存储。 2、接下来看看刚刚create方法的倒数第三行代码`ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);`。 其中loadServiceMethod方法源码如下: ```java ServiceMethod<?, ?> loadServiceMethod(Method method) { ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; } ``` 可以看到Retrofit对ServiceMethod实例进行了缓存,来提高请求时的解析效率。 3、看下倒数第二行代码`OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);`。这行代码根据serviceMethod对象构造了一个OkHttpCall对象。 OkHttpCall类源码如下: ```java final class OkHttpCall<T> implements Call<T> { private final ServiceMethod<T, ?> serviceMethod; private final @Nullable Object[] args; OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) { this.serviceMethod = serviceMethod; this.args = args; } private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = serviceMethod.toCall(args); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; } } ``` 可以看到,OkHttpCall实现了Call接口,可以直接作为Call执行一系列操作,并且有一个createRawCall方法,会在调用request、enqueue、execute方法时进行调用。 4、得到OkHttpCall对象后,来看看最后一行代码`return serviceMethod.adapt(okHttpCall);` 来看下ServiceMethod的adapt方法: ```java T adapt(Call<R> call) { return callAdapter.adapt(call); } ``` 下面我们按两条线来走,一是CallAdapter是怎么来的呢,二是CallAdapter的adapt方法做了什么。 a、首先来看看CallAdapter是怎么来的呢? ```java private CallAdapter<T, R> createCallAdapter() { Type returnType = method.getGenericReturnType(); Annotation[] annotations = method.getAnnotations(); //... return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations); } // Retrofit类的callAdapter方法如下: public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); } public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { int start = callAdapterFactories.indexOf(skipPast) + 1; for (int i = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } } ``` 可以看到,从CallAdapterFactories中遍历查询,看是否有返回类型相匹配的CallAdpater。下面再看看CallAdapterFactories是怎么来的。 ```java public Retrofit build() { //... // 添加默认的CallAdapterFactory List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); //... } ``` 在Retrofit的构造方法中,会添加平台的默认CallAdapterFactory。CallAdapterFactory有两个子类:DefaultCallAdapterFactory和ExecutorCallAdapterFactory。默认的CallAdapterFactory是ExecutorCallAdapterFactory: ```java final class ExecutorCallAdapterFactory extends Factory { public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { //... return new CallAdapter<Object, Call<?>>() { public Type responseType() { return responseType; } public Call<Object> adapt(Call<Object> call) { return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call); } }; } static final class ExecutorCallbackCall<T> implements Call<T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } public void enqueue(final Callback<T> callback) { //... } public Response<T> execute() throws IOException { //... } } } ``` b、下面看看第二个问题,CallAdapter的adapt方法到底做了什么呢?由上面的ExecutorCallAdapterFactory可看到,adapt方法返回了一个Call接口的实现类。 c、最后,我们再多来看一下CallAdapter的声明 ```java public interface CallAdapter<R, T> { Type responseType(); T adapt(Call<R> call); abstract class Factory { public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); } } } ``` # 参考 [这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)](http://blog.csdn.net/carson_ho/article/details/73732076) [RESTful API 设计指南](http://www.ruanyifeng.com/blog/2014/05/restful_api.html) [Android:手把手带你 深入读懂 Retrofit 2.0 源码]([https://www.jianshu.com/p/0c055ad46b6c](https://www.jianshu.com/p/0c055ad46b6c))