睡前给IT女闺蜜讲解的;Retrofit原理及源码解析,她居然……对我
前言:昨天晚上正准备睡觉,女闺蜜打电话来想问关于Retrofit的网络框架;于是找出这篇全网最详细的Retrofit资料,给她讲解;究竟是什么Retrofit解析让她感动的如此献殷勤?
一、什么是Retrofit
Retrofit 是一个用于 Android 和 Java 平台的类型安全的网络请求框架。Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。 网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装,可以结合Rxjava、协程、以及LiveData使用。返回的序列化数据可以是 Gson、Jackson、Moshi、Protobuf等等
二、Retrofit的使用方法与示例
2.1 使用前准备
(1)加入网络权限 在 AndroidManifest.xml 文件中加入如下:
<uses-permission android:name="android.permission.INTERNET"/>复制代码
(2)添加 Retrofit 库的依赖 因为需要将服务器返回的 ResponseBody 转换成实体类,所以需要添加 Gson 库的依赖作为数据解析器。 最终在当前使用的 module 下的 build.gradle 中加入如下依赖:
// Retrofitimplementation 'com.squareup.retrofit2:retrofit:2.5.0'// Gsonimplementation '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<PostmanGetBean> 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);Call<PostmanGetBean> call = service.testGet();复制代码
(5)进行网络请求
call.enqueue(new Callback<PostmanGetBean>() { @Override public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) { System.out.println(response.body().getUrl()); } @Override public void onFailure(Call<PostmanGetBean> 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<PostmanGetBean> testHTTP();}复制代码
运行结果:
与 @GET 注解示例一样。
示例源码:RetrofitActivity-testHTTP
3.2 第二类:标记
3.2.1 @FormUrlEncoded 注解
简介: 表示请求体是一个 Form 表单。
示例:
这里使用 postman 提供的 POST 接口进行演示。
单个键值对传:
(1)创建实体类:
public class PostmanPostBean { // 字段与重写 toString() 方法省略,具体看 demo}复制代码
(2)创建接口:
public interface PostmanService { @POST("post") @FormUrlEncoded Call<PostmanPostBean> 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<PostmanPostBean> call = service.testFormUrlEncoded1("wildma", "123456");call.enqueue(new Callback<PostmanPostBean>() { @Override public void onResponse(Call<PostmanPostBean> call, Response<PostmanPostBean> response) { System.out.println(response.body().getForm().toString()); } @Override public void onFailure(Call<PostmanPostBean> call, Throwable t) { }});复制代码
运行结果:
FormEntity{username='wildma', password='123456'}复制代码
示例源码:RetrofitActivity-testFormUrlEncoded1
传入一个 Map 集合:
向 Post 表单传入键值对除了上面一个个传,还可以使用注解 @FieldMap 传一个 Map 集合,如下:
(1)创建接口:
public interface PostmanService { @POST("post") @FormUrlEncoded Call<PostmanPostBean> testFormUrlEncoded2(@FieldMap Map<String, Object> map);}复制代码
(3)发起请求:
// 省略创建 Retrofit 的实例代码Map<String, Object> map = new HashMap<>();map.put("username", "wildma");map.put("password", "123456");Call<PostmanPostBean> 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<UploadImgBean> 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<UploadImgBean> call = service.testFileUpload1(filePart, appKey);call.enqueue(new Callback<UploadImgBean>() { @Override public void onResponse(Call<UploadImgBean> call, Response<UploadImgBean> response) { System.out.println(response.body().toString()); } @Override public void onFailure(Call<UploadImgBean> 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 传一个键值对为 <String, RequestBody> 的 Map 集合,如下:
(1)创建接口:
public interface FileUploadService { @POST("?service=App.CDN.UploadImg") @Multipart Call<UploadImgBean> testFileUpload2(@PartMap Map<String, RequestBody> 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<String, RequestBody> 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<UploadImgBean> call = service.testFileUpload2(requestBodyMap);call.enqueue(new Callback<UploadImgBean>() { @Override public void onResponse(Call<UploadImgBean> call, Response<UploadImgBean> response) { System.out.println(response.body().toString()); } @Override public void onFailure(Call<UploadImgBean> 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<ResponseBody> testFileDownload();}复制代码
这里使用了 @Streaming 注解用来表示响应体的数据用流的形式返回。
(3)发起请求:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://wildma.github.io/") .addConverterFactory(GsonConverterFactory.create()) .build();FileDownloadService service = retrofit.create(FileDownloadService.class);Call<ResponseBody> call = service.testFileDownload();call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { InputStream is = response.body().byteStream(); // 保存文件... } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { }});复制代码
示例源码:RetrofitActivity-testFileDownload
3.3 第三类:网络请求参数
3.3.1 @Header、@Headers 与 @HeaderMap 注解
简介: @Header 与 @HeaderMap 用于添加不固定值的请求头,@Headers 用于添加固定值的请求头。@Header 与 @HeaderMap 是作为请求方法的参数传入,@Headers 则直接添加到请求方法上。
示例:
// @Header@GET("headers")Call<ResponseBody> testHeader(@Header("token") String token);// @Headers@Headers("token: 123")@GET("headers")Call<ResponseBody> testHeaders();// @Headers 多个请求头@Headers({"token: 123", "sign: 456"})@GET("headers")Call<ResponseBody> testHeaders2();// @HeaderMap@GET("headers")Call<ResponseBody> testHeaderMap(@HeaderMap Map<String, String> map);复制代码
示例源码:RetrofitActivity-testHeader()、testHeaders()、testHeaders2()
3.3.2 @Body 注解
简介: @Body 用于非表单请求体。很多时候后台要求前端传一个 json 字符串的请求体,这时候我们可以使用 @Body 注解来轻松实现,因为该注解可以直接传一个实体类,发起请求的过程中会把该实体类转换成 json 字符串的请求体传给后台。
示例:
(1)创建接口:
public interface PostmanService { @POST("post") Call<ResponseBody> testBody(@Body TestBodyBean testBodyBean);}复制代码
(2)发起请求:
// 省略创建 Retrofit 的实例代码TestBodyBean bean = new TestBodyBean();bean.setUsername("wildma");bean.setPassword("123456");PostmanService service = retrofit.create(PostmanService.class);Call<ResponseBody> call = service.testBody(bean);// 省略网络请求代码复制代码
示例源码:RetrofitActivity-testBody()
3.3.3 @Field 与 @FieldMap 注解
简介: 用于向 Post 表单传入键值对。
示例:
具体使用前面讲 @FormUrlEncoded 注解的时候已经讲过了。
示例源码:RetrofitActivity-testFormUrlEncoded1()、testFormUrlEncoded2()
3.3.4 @Part 与 @PartMap 注解
简介: 用于表单字段,适用于有文件上传的情况。
示例:
具体使用前面讲 @Multipart 注解的时候已经讲过了。
示例源码:RetrofitActivity-testFileUpload1()、testFileUpload2()
3.3.5 @Query 与 @QueryMap 注解
简介: 用于表单字段,功能与 @Field、@FiledMap 一样,区别在于 @Query、@QueryMap 的数据体现在 URL 上,而 @Field、@FiledMap 的数据体现在请求体上,但生成的数据是一样的。
示例:
(1)创建接口:
public interface PostmanService { @GET("get") Call<ResponseBody> 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<ResponseBody> 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<ResponseBody> testQueryMap(@QueryMap Map<String, String> params);}复制代码
发起请求的代码就不贴出来了,传一个对应的 Map 集合进来即可。
示例源码:RetrofitActivity-testQuery()、testQueryMap()
3.3.6 @QueryName 注解
简介: 用于没有值的查询参数,该注解实际项目中很少用到,功能与 @Query、@QueryMap 类似,参数都拼接在 URL 上,但是 @Query、@QueryMap 在 URL 上是以键值对拼接的,而 @QueryName 只是拼接键,没有值。
示例:
(1)创建接口:
public interface PostmanService { @GET("get") Call<ResponseBody> 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<ResponseBody> 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<List<RepoBean>> 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<ResponseBody> 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<ResponseBody> 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<ResponseBody> 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
五、关于 Converter
Retrofit 默认用 ResponseBody 来接收服务器返回的数据,如果想要转换成对应的实体类,那么在创建 Retrofit 的实例的时候可以通过 addConverterFactory() 方法设置一个数据解析器,数据解析器有多种选择,Retrofit 文档中就提供了很多种:
Gson: com.squareup.retrofit2:converter-gsonJackson: com.squareup.retrofit2:converter-jacksonMoshi: com.squareup.retrofit2:converter-moshiProtobuf: com.squareup.retrofit2:converter-protobufWire: com.squareup.retrofit2:converter-wireSimple XML: com.squareup.retrofit2:converter-simplexmlScalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
除了文档提供的这几种,其实还有一种常用的:
fastjson:'org.ligboy.retrofit2:converter-fastjson-android
这里使用 Gson 进行演示。
(1)创建接口:
public interface PostmanService { @GET("get") Call<PostmanGetBean> 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-guavaJava8:com.squareup.retrofit2:adapter-java8:2.0.2rxjava:com.squareup.retrofit2:adapter-rxjava
这里使用 RxJava 进行演示。
(1)添加相关依赖:
// 支持 rxjava2implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'// rxjava2compile 'io.reactivex.rxjava2:rxjava:2.2.13'compile 'io.reactivex.rxjava2:rxandroid:2.1.1'复制代码
(2)创建接口:
@GET("get")Observable<ResponseBody> 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<ResponseBody> observable = service.testCallAdapter();observable.subscribeOn(Schedulers.io()) // 在 IO 线程进行网络请求 .observeOn(AndroidSchedulers.mainThread())// 在主线程处理请求结果 .subscribe(new Observer<ResponseBody>() { @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
七、Retrofit源码剖析
7.1创建以及使用
同样是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<ResponseBody> getNetData(); @GET("/wxarticle/list") Observable<ResponseBody> getNetData2();}复制代码请求
// 创建 ApiService ApiService apiService = retrofit.create(ApiService.class); // 使用原始自带的请求retrofit2.Call<NetData> netData = apiService.getNetData();netData.enqueue(new retrofit2.Callback<NetData>() { @Override public void onResponse(retrofit2.Call<NetData> call, retrofit2.Response<NetData> response) { } @Override public void onFailure(retrofit2.Call<NetData> call, Throwable t) { }}); // 适配成Rxjava流的请求apiService.getNetData2() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { }, error -> { });复制代码7.2解析
创建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<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); // 这个是转化成javabean的转化类,有Gson 有 Moshi,有Protobuf, List<Converter.Factory> 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> T create(final Class<T> 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; @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 HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> 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); if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) { // Unwrap the actual body type from Response<T>. 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<ResponseT, ReturnT> 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<String>)"); } if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) { throw methodError(method, "HEAD method must use Void as response type."); } // 拿到 刚才设置的 converterFactories 集合,遍历拿到属于序列化方式 Converter<ResponseBody, ResponseT> 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<T> implements Call<T> {@Overridepublic void enqueue(final Callback<T> 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<T> 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<T> 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开发核心技术知识》,不说了女闺蜜叫我了。
页:
[1]