找回密码
 立即注册
查看: 276|回复: 0

睡前给IT女闺蜜讲解的;Retrofit原理及源码解析,她居然……对我

[复制链接]
发表于 2022-6-1 15:04 | 显示全部楼层 |阅读模式
前言:


昨天晚上正准备睡觉,女闺蜜打电话来想问关于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[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 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[parameterTypes.length - 1]);      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> {@Override  public 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开发核心技术知识》,不说了女闺蜜叫我了。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2025-5-2 19:35 , Processed in 0.136836 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表