昨天晚上正准备睡觉,女闺蜜打电话来想问关于Retrofit的网络框架;于是找出这篇全网最详细的Retrofit资料,给她讲解;究竟是什么Retrofit解析让她感动的如此献殷勤?
一、什么是RetrofitRetrofit 是一个用于 Android 和 Java 平台的类型安全的网络请求框架。Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。 网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装,可以结合Rxjava、协程、以及LiveData使用。返回的序列化数据可以是 Gson、Jackson、Moshi、Protobuf等等
二、Retrofit的使用方法与示例 2.1 使用前准备(1)加入网络权限 在 AndroidManifest.xml 文件中加入如下:
复制代码
(2)添加 Retrofit 库的依赖 因为需要将服务器返回的 ResponseBody 转换成实体类,所以需要添加 Gson 库的依赖作为数据解析器。 最终在当前使用的 module 下的 build.gradle 中加入如下依赖:
// Retrofit implementation 'com.squareup.retrofit2:retrofit:2.5.0' // Gson implementation 'com.squareup.retrofit2:converter-gson:2.5.0' 复制代码2.2 简单的 GET 请求
这里使用 postman 提供的 GET 接口进行演示。(Postman Echo)
(1)创建一个实体类,用于接收服务器返回的数据:
public class PostmanGetBean {
private String url;
// 其余字段省略,具体看 demo。
}
复制代码
(2)创建一个接口,用于定义网络请求:
public interface PostmanService {
@GET("get")
Call testGet();
}
复制代码
可以看到,这里有一个 testGet() 方法,方法上面的注解 @GET 表示 GET 请求,注解里面的 “get” 会与后面的 baseUrl 拼接成完整的路径。例如 baseUrl 为 https://postman-echo.com/,则完整的路径为 https://postman-echo.com/get。这里建议 baseUrl 以 /(斜线)结尾,注解中的 path 统一不要以 /(斜线)开头,因为这种方式看起来比较直观。
(3)创建 Retrofit 的实例:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")// baseUrl
.addConverterFactory(GsonConverterFactory.create())// 解析json数据
.build();
复制代码
(4)创建网络请求接口的实例,并调用接口中的方法获取 Call 对象:
PostmanService service = retrofit.create(PostmanService.class); Callcall = service.testGet(); 复制代码
(5)进行网络请求
call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { System.out.println(response.body().getUrl()); } @Override public void onFailure(Call call, Throwable t) { } }); 复制代码
打印结果:
https://postman-echo.com/get 复制代码
示例源码:RetrofitActivity-testGetRequest
三、Retrofit 注解说明Retrofit 中使用了大量的注解,这里将这些注解分成 3 类。
3.1 第一类:网络请求方法分别是 @GET、@POST、@PUT、@DELETe、@PATH、@HEAD、@OPTIONS 和 @HTTP,前 7 个分别对应 HTTP 中的网络请求方法,都接收一个字符串与 baseUrl 组成完整的 URL,也可以不指定,通过 @HTTP 注解设置。最后一个 @HTTP 注解可以用来替换前面 7 个注解,以及其他扩展功能。 这里主要讲下 @HTTP 注解,其他注解与 @GET 注解类似。
@HTTP 注解示例:
@HTTP 注解有 3 个属性:method、path 与 hasBody,上面说了这个注解可以用来替换前面 7 个注解,所以就替换一下前面讲到 GET 请求中的 @GET 注解吧。
这里只需要修改接口即可,其他不变:
public interface PostmanService {
@HTTP(method = "GET", path = "get", hasBody = false)
Call testHTTP();
}
复制代码
运行结果:
与 @GET 注解示例一样。
示例源码:RetrofitActivity-testHTTP
简介: 表示请求体是一个 Form 表单。
示例:
这里使用 postman 提供的 POST 接口进行演示。
单个键值对传:
(1)创建实体类:
public class PostmanPostBean {
// 字段与重写 toString() 方法省略,具体看 demo
}
复制代码
(2)创建接口:
public interface PostmanService {
@POST("post")
@FormUrlEncoded
Call testFormUrlEncoded1(@Field("username") String name, @Field("password") String password);
}
复制代码
可以看到,这里使用了 @Field 注解,它属于第三类注解,用来向 Post 表单传入键值对,其中 username 表示键,name 表示值。
(3)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call call = service.testFormUrlEncoded1("wildma", "123456");
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
System.out.println(response.body().getForm().toString());
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
复制代码
运行结果:
FormEntity{username='wildma', password='123456'}
复制代码
示例源码:RetrofitActivity-testFormUrlEncoded1
传入一个 Map 集合:
向 Post 表单传入键值对除了上面一个个传,还可以使用注解 @FieldMap 传一个 Map 集合,如下:
(1)创建接口:
public interface PostmanService {
@POST("post")
@FormUrlEncoded
Call testFormUrlEncoded2(@FieldMap Map map);
}
复制代码
(3)发起请求:
// 省略创建 Retrofit 的实例代码 Mapmap = new HashMap<>(); map.put("username", "wildma"); map.put("password", "123456"); Call call = service.testFormUrlEncoded2(map); // 省略网络请求代码 复制代码
示例源码:RetrofitActivity-testFormUrlEncoded2
3.2.2 @Multipart 注解简介: 表示请求体是一个支持文件上传的 Form 表单。
示例:
这里使用 YESAPI 提供的图片上传接口进行演示。
单文件上传:
(1)创建实体类:
public class UploadImgBean {
// 字段与重写 toString() 方法省略,具体看 demo
}
复制代码
(2)创建接口:
public interface FileUploadService {
@POST("?service=App.CDN.UploadImg")
@Multipart
Call testFileUpload1(@Part MultipartBody.Part file, @Part("app_key") RequestBody appKey);
}
复制代码
可以看到,这里使用了 @Part 注解,它属于第三类注解,用于表单字段,适用于有文件上传的情况。这里使用了@Part 的两种类型,MultipartBody.Part 表示上传一个文件,RequestBody 表示传一个键值对,其中 app_key 表示键,appKey 表示值。
(3)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://hn216.api.yesapi.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestBody appKey = RequestBody.create(null, "替换成你在 YESAPI 上获取的 appKey");
// test.png 为 SD 卡跟目录下的文件,需要提前放好
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
// 构建 MultipartBody.Part,其中 file 为服务器约定好的 key,test.png 为文件名称
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.png", requestBody);
FileUploadService service = retrofit.create(FileUploadService.class);
Call call = service.testFileUpload1(filePart, appKey);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
System.out.println(response.body().toString());
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
复制代码
运行结果:
UploadImgBean{ret=200, data=DataEntity{err_code=0, err_msg='', url='http://cd7.yesapi.net/xxx.png'}, msg='当前小白接口:App.CDN.UploadImg'}
复制代码
示例源码:RetrofitActivity-testFileUpload1
多文件上传:
如果想上传多个文件,则可以使用注解 @PartMap 传一个键值对为
(1)创建接口:
public interface FileUploadService {
@POST("?service=App.CDN.UploadImg")
@Multipart
Call testFileUpload2(@PartMap Map map);
}
复制代码
(1)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://hn216.api.yesapi.cn/")
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestBody appKey = RequestBody.create(null, "替换成你在 YESAPI 上获取的 appKey");
// test.png 为 SD 卡跟目录下的文件,需要提前放好
File file = new File(Environment.getExternalStorageDirectory(), "test.png");
RequestBody requestBody = RequestBody.create(MediaType.parse("image/png"), file);
Map requestBodyMap = new HashMap<>();
requestBodyMap.put("app_key", appKey);
// 加入一个文件,其中 file 为服务器约定好的 key,test.png 为文件名称
requestBodyMap.put("file"; filename="test.png", requestBody);
// 有更多文件,则继续 put()...
FileUploadService service = retrofit.create(FileUploadService.class);
Call call = service.testFileUpload2(requestBodyMap);
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
System.out.println(response.body().toString());
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
复制代码
示例源码:RetrofitActivity-testFileUpload2
3.2.3 @Streaming 注解简介: 表示响应体的数据用流的形式返回,如果没有使用该注解,默认会把数据全部载入内存,之后获取数据就从内存中读取,所以该注解一般用在返回数据比较大的时候,例如下载大文件。
示例:
这里使用下载我的博客头像( wildma.github.io/medias/avat… ) 进行演示。
(1)下载文件不需要创建一个实体类,直接用 ResponseBody 来接收服务器返回的数据。(后面的示例为了方便演示,也不再解析成实体类,直接用 ResponseBody 来接收服务器返回的原始数据)
(2)创建接口:
public interface FileDownloadService {
@Streaming
@GET("medias/avatars/avatar.jpg")
Call testFileDownload();
}
复制代码
这里使用了 @Streaming 注解用来表示响应体的数据用流的形式返回。
(3)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://wildma.github.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
FileDownloadService service = retrofit.create(FileDownloadService.class);
Call call = service.testFileDownload();
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
InputStream is = response.body().byteStream();
// 保存文件...
}
@Override
public void onFailure(Call call, Throwable t) {
}
});
复制代码
示例源码:RetrofitActivity-testFileDownload
3.3 第三类:网络请求参数 3.3.1 @Header、@Headers 与 @HeaderMap 注解简介: @Header 与 @HeaderMap 用于添加不固定值的请求头,@Headers 用于添加固定值的请求头。@Header 与 @HeaderMap 是作为请求方法的参数传入,@Headers 则直接添加到请求方法上。
示例:
// @Header
@GET("headers")
Call testHeader(@Header("token") String token);
// @Headers
@Headers("token: 123")
@GET("headers")
Call testHeaders();
// @Headers 多个请求头
@Headers({"token: 123", "sign: 456"})
@GET("headers")
Call testHeaders2();
// @HeaderMap
@GET("headers")
Call testHeaderMap(@HeaderMap Map map);
复制代码
示例源码:RetrofitActivity-testHeader()、testHeaders()、testHeaders2()
3.3.2 @Body 注解简介: @Body 用于非表单请求体。很多时候后台要求前端传一个 json 字符串的请求体,这时候我们可以使用 @Body 注解来轻松实现,因为该注解可以直接传一个实体类,发起请求的过程中会把该实体类转换成 json 字符串的请求体传给后台。
示例:
(1)创建接口:
public interface PostmanService {
@POST("post")
Call testBody(@Body TestBodyBean testBodyBean);
}
复制代码
(2)发起请求:
// 省略创建 Retrofit 的实例代码
TestBodyBean bean = new TestBodyBean();
bean.setUsername("wildma");
bean.setPassword("123456");
PostmanService service = retrofit.create(PostmanService.class);
Call call = service.testBody(bean);
// 省略网络请求代码
复制代码
示例源码:RetrofitActivity-testBody()
3.3.3 @Field 与 @FieldMap 注解简介: 用于向 Post 表单传入键值对。
示例:
具体使用前面讲 @FormUrlEncoded 注解的时候已经讲过了。
示例源码:RetrofitActivity-testFormUrlEncoded1()、testFormUrlEncoded2()
简介: 用于表单字段,适用于有文件上传的情况。
示例:
具体使用前面讲 @Multipart 注解的时候已经讲过了。
示例源码:RetrofitActivity-testFileUpload1()、testFileUpload2()
简介: 用于表单字段,功能与 @Field、@FiledMap 一样,区别在于 @Query、@QueryMap 的数据体现在 URL 上,而 @Field、@FiledMap 的数据体现在请求体上,但生成的数据是一样的。
示例:
(1)创建接口:
public interface PostmanService {
@GET("get")
Call testQuery(@Query("username") String username);
}
复制代码
(2)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call call = service.testQuery("wildma");
// 省略网络请求代码
复制代码
上面的 baseUrl 为 https://postman-echo.com/,@GET 注解中的部分 URL 为 “get”,最终完整 URL 如果没用 @Query 注解应该是 https://postman-echo.com/get,用了注解就变成 https://postman-echo.com/get?username=wildma 了。
@QueryMap 注解则对应 Map 集合,接口如下:
public interface PostmanService {
@GET("get")
Call testQueryMap(@QueryMap Map params);
}
复制代码
发起请求的代码就不贴出来了,传一个对应的 Map 集合进来即可。
示例源码:RetrofitActivity-testQuery()、testQueryMap()
3.3.6 @QueryName 注解简介: 用于没有值的查询参数,该注解实际项目中很少用到,功能与 @Query、@QueryMap 类似,参数都拼接在 URL 上,但是 @Query、@QueryMap 在 URL 上是以键值对拼接的,而 @QueryName 只是拼接键,没有值。
示例:
(1)创建接口:
public interface PostmanService {
@GET("get")
Call testQueryName(@QueryName String... filters);
}
复制代码
注解后面可跟 String filter,也可跟 String… filters,其中后者是可变长参数,可以传多个参数也可不传参数。
(2)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call call = service.testQueryName("wildma","tom");
// 省略网络请求代码
复制代码
上面最终拼接的 URL 为 https://postman-echo.com/get?wildma&tom。
示例源码:RetrofitActivity-testQueryName()
3.3.7 @Path 注解简介: @Path 用于设置 URL 地址的缺省值。
示例:
这里使用官方提供的 API,即获取指定用户的仓库列表进行演示。
(1)创建接口:
public interface GitHubService {
@GET("users/{user}/repos")
Call> listRepos(@Path("user") String user);
}
复制代码
(2)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call call = service.testPath("wildma");
// 省略网络请求代码
复制代码
可以看到,@GET 注解里面的 “users/{user}/repos” 中有一个 “{user}”,这个就是 URL 地址的缺省值, listRepos() 方法中的 @Path(“user”) String user 表示传入的 urse 就是用来替换上面的 {user} 的。所以最终完整的 URL 为 https://api.github.com/users/wildma/repos。
示例源码:RetrofitActivity-testPath()
3.3.8 @Url 注解简介: @Url 用于动态设置一个完整的 URL。
示例:
(1)创建接口:
public interface PostmanService {
@GET()
Call testUrl(@Url String url);
}
复制代码
(2)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
PostmanService service = retrofit.create(PostmanService.class);
Call call = service.testUrl("https://postman-echo.com/get");
// 省略网络请求代码
复制代码
可以看到,baseUrl() 与 testUrl() 都设置了一个 URL,但由于 @Url 注解标识的 URL 是动态设置的,所以最终以 testUrl() 中设置的为准,也就是最终使用的是 https://postman-echo.com/get。
示例源码:RetrofitActivity-testUrl()
四、设置自定义的 OkHttpClient在创建 Retrofit 的实例的时候可以通过 client() 方法设置自定义的 OkHttpClient,自定义 OkHttpClient 可以设置统一的 header,添加 log 拦截器、Cookie 等。这里就讲下怎么设置统一的 header 吧!
(1)创建 OkHttpClient 的时候通过添加拦截器,然后在拦截器的 intercept() 方法中设置统一的 header:
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request request = originalRequest.newBuilder()
.header("token", "123")
.header("sign", "456")
.build();
return chain.proceed(request);
}
}).build();
复制代码
(2)通过 client() 方法设置自定义的 OkHttpClient:
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)// 设置自定义的 OkHttpClient
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
复制代码
示例源码:RetrofitActivity-testCustomOkHttpClient
五、关于 ConverterRetrofit 默认用 ResponseBody 来接收服务器返回的数据,如果想要转换成对应的实体类,那么在创建 Retrofit 的实例的时候可以通过 addConverterFactory() 方法设置一个数据解析器,数据解析器有多种选择,Retrofit 文档中就提供了很多种:
- 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
除了文档提供的这几种,其实还有一种常用的:
fastjson:'org.ligboy.retrofit2:converter-fastjson-android
这里使用 Gson 进行演示。
(1)创建接口:
public interface PostmanService {
@GET("get")
Call testGet();
}
复制代码
这里直接用实体类 PostmanGetBean 替换 ResponseBody。
(2)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())// 添加 Gson 解析器
.build();
// 省略网络请求代码
复制代码
这里添加了 Gson 作为数据解析器。
示例源码:RetrofitActivity-testGet
六、关于 CallAdapter前面创建接口的时候,发现接口中的方法返回类型都是 Call,如果想要返回其他类型,那么在创建 Retrofit 的实例的时候可以通过 addCallAdapterFactory() 方法设置一个 CallAdapter,Retrofit 提供了如下 CallAdapter:
- guava:com.squareup.retrofit2:adapter-guava
- Java8:com.squareup.retrofit2:adapter-java8:2.0.2
- rxjava:com.squareup.retrofit2:adapter-rxjava
这里使用 RxJava 进行演示。
(1)添加相关依赖:
// 支持 rxjava2 implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' // rxjava2 compile 'io.reactivex.rxjava2:rxjava:2.2.13' compile 'io.reactivex.rxjava2:rxandroid:2.1.1' 复制代码
(2)创建接口:
@GET("get")
Observable testCallAdapter();
复制代码
这里使用 Observable 替换 Call。
(3)发起请求:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())// 设置 RxJava 作为当前的 CallAdapter
.build();
PostmanService service = retrofit.create(PostmanService.class);
Observable observable = service.testCallAdapter();
observable.subscribeOn(Schedulers.io()) // 在 IO 线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) // 在主线程处理请求结果
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ResponseBody responseBody) {
try {
System.out.println(responseBody.string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
复制代码
这里设置 RxJava 作为当前的 CallAdapter,并且调用 Observable 的相关方法进行网络请求与请求结果的处理。
示例源码:RetrofitActivity-testCallAdapter
同样是Builder的模式创建的,可以指定BaseUrl,指定OkHttpClient,指定适配器,以及序列号工厂
- 创建Retrofit
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(baseUrl)
.client(new OkHttpClient())
// 指定Rxjava适配器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// 指定序列化结构
.addConverterFactory(GsonConverterFactory.create())
.build();
复制代码
- 创建接口
public interface ApiService {
@GET("/wxarticle/list")
Call getNetData();
@GET("/wxarticle/list")
Observable getNetData2();
}
复制代码
- 请求
// 创建 ApiService ApiService apiService = retrofit.create(ApiService.class); // 使用原始自带的请求 retrofit2.Call7.2解析netData = apiService.getNetData(); netData.enqueue(new retrofit2.Callback () { @Override public void onResponse(retrofit2.Call call, retrofit2.Response response) { } @Override public void onFailure(retrofit2.Call call, Throwable t) { } }); // 适配成Rxjava流的请求 apiService.getNetData2() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { }, error -> { }); 复制代码
创建retrofit的 Builder.Build方法
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 虽然这里用的工厂模式,但是 就new了一个OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// 因为在android中platform就是 Android,Platform中的一个内部类Android
callbackExecutor = platform.defaultCallbackExecutor();
}
// 这就是添加所有的adapter,这里用到的适配器模式,也就是从Okhttp的Call适配成Retrofit的Call
// 也可以添加咱们自己的适配器,比如RxJava2CallAdapterFactory.create(),Rxjava的。
// 还可以添加协程的 或者是LiveData的
List callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// 这个是转化成javabean的转化类,有Gson 有 Moshi,有Protobuf,
List converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
// 执行 Retrofit 的构造方法
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
// 创建APIService,用的是动态代理的模式,在运行时创建了一个对象
public T create(final Class 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 {
// 如果是来自Object方法,继续走原来的逻辑
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
// 这个在Android中 isDefaultMethod()方法默认返回false。因为咱们就是用的接口中的方法
// 不会调用接口中的默认方法
//Java 8 引入了新的语言特性——默认方法(Default Methods
// 所以会走 loadServiceMethod(method).invoke(args)
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
// 返回一个ServiceMethod,执行里面的方法
ServiceMethod> loadServiceMethod(Method method) {
// 先从缓存中拿,有直接返回
ServiceMethod> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 因为用的反射,所以缓存一份
// 这里会返回 HttpServiceMethod ,ServiceMethod的一个子类
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
复制代码
HttpServiceMethod:把Retorfit的注解参数以及Url 变成 Okhttp的Requst,有返回回来的Respone,还有在这里适配的请求,以及 适配成各种返回值,默认 Okhttp的Call 适配成 Retrofit的Call,或者 Rxjava 的 Observable,或者是协程,以及序列化 都是在这里转化的
7.3看一下请求,直接看默认的,不用看Rxjava的即可,看Retrofit的Call怎样转化到Okhttp的Call的当我们执行的时候会走到 HttpServiceMethod中的invoke
abstract class HttpServiceMethodextends ServiceMethod { static HttpServiceMethod parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { // 是否是kotlin中的协程方法 boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; boolean continuationWantsResponse = false; boolean continuationBodyNullable = false; // 得到所有的注解 Annotation[] annotations = method.getAnnotations(); // 获取返回类型 Type adapterType; // 先不看 有关协程的, if (isKotlinSuspendFunction) { Type[] parameterTypes = method.getGenericParameterTypes(); Type responseType = Utils.getParameterLowerBound( 0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]); if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) { // Unwrap the actual body type from Response . responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType); continuationWantsResponse = true; } else { } adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType); annotations = SkipCallbackExecutorImpl.ensurePresent(annotations); } else { // 获取返回类型 adapterType = method.getGenericReturnType(); } // 这里会走到Retrofit的nextCallAdapter方法,也就是从刚才咱们设置的Adapter的集合中遍历拿到 这个返回类型的 CallAdapter callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); // 得到返回序列化类型,下面通过这个类型去找 序列化方法 Type responseType = callAdapter.responseType(); if (responseType == okhttp3.Response.class) { } if (responseType == Response.class) { throw methodError(method, "Response must include generic type (e.g., Response )"); } if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) { throw methodError(method, "HEAD method must use Void as response type."); } // 拿到 刚才设置的 converterFactories 集合,遍历拿到属于序列化方式 Converter responseConverter = createResponseConverter(retrofit, method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; if (!isKotlinSuspendFunction) { // 咱们先看这个不是协程的,CallAdapted 就是ResponseCallAdapter 或者是 RxJava2CallAdapter return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); } } } 复制代码
刚才咱们也看到了OkHttpCall,这里面封装这Okhttp的Call。当我们调用请求的时候,实际就是调用的Okhttp的请求
final class OkHttpCall结尾:implements Call { @Override public void enqueue(final Callback callback) { Objects.requireNonNull(callback, "callback == null"); // 拿到Okhttp的call okhttp3.Call call; Throwable failure; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; if (call == null && failure == null) { try { // 默认使用的就是OkhttpClient的 new Call,上面说过,虽然用的是工厂,但是还是直接new的OkHttpClient call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } // 调用请求 call.enqueue( new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response response; try { // 请求成功之后,来解析这个请求,返回真正的锁需要的 response = parseResponse(rawResponse); } catch (Throwable e) { throwIfFatal(e); callFailure(e); return; } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); // TODO this is not great } } }); } } // 解析返回值 Response parseResponse(okhttp3.Response rawResponse) throws IOException { try { // responseConverter 就是默认的转化器,GsonRequestBodyConverter也可以,序列化成对应的格式 T body = responseConverter.convert(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { // If the underlying source threw an exception, propagate that rather than indicating it was // a runtime exception. catchingBody.throwIfCaught(); throw e; } }
全文讲解到了Retrofit的使用与方法示例;以及Retrofit的源码分析,全文篇幅较长可收藏反复学习阅读。
最近女闺蜜对我可温柔了,老是给我东西吃,还要经常来我家玩;要我晚上传输更多的Android开发知识;我最近晚上忙的整理出的Android开发学习资料偷偷留给你们一份《Android开发核心技术知识》,不说了女闺蜜叫我了。



