Retrofit2完全教程 (B)

2024-03-16 11:58
文章标签 教程 完全 retrofit2

本文主要是介绍Retrofit2完全教程 (B),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文章只是个人笔记 ,没有时间整理,非喜勿喷

1、官网的例子很简单,可先粗粗看一下,之后看文章一。

官网 : https://square.github.io/retrofit/

2、文章一:

https://blog.csdn.net/carson_ho/article/details/73732076

看完文章一后,基本可以上手,文章三可以作为扩展资料看看

3、Retrofit OkHttp3 打印日志 网络请求参数配置,与gson的关联请看下文:

通过 OkHttpClient.Builder 生成的 client 会自动把请求的页面加载完,一般的http没什么影响,但如果是请求一个大文件就会报异常。(Builder 会自动设置某些参数,稍后要把那个关键的设置参数找出来)

 OkHttpClient client = new OkHttpClient(); 不会自动加载大文件

文章二: https://blog.csdn.net/tantion/article/details/88813403

4、进阶:

(1)Retrofit 进阶: 自定拦截器,处理 http 状态 400,500

(2)Retrofit进阶:自定义GsonConverter处理错误情况 - “begin array but was object”

网络请求返回code,如果想code!=1时跳转到出错(throw Exception)流程,可以通过自定义拦截器,也可以通过自定义GsonConverter。个人建议放在拦截器比较好,放在GsonConverter容易忘记,新手排查有点难。

  {"code":1,"message":"查询成功","detail":{"aa":"123","bb":"123","cc":"123"}}

 

(3)Retrofit 进阶 :是怎么将回调函数放到UI线程(主线程)中的(源码分析)

  (4)   Rxjava+Retrofit 请求返回的数据是String,返回的类型直接写ResponseBody,写String 或者 Object(返回的值没有了双引号)不行。

Observable<ResponseBody> login2(@Body BaseReq<LoginRequest> loginReq);....@Overridepublic void onNext(ResponseBody baseRes) {L.w("tan","onNext");//L.w("tan",baseRes.resultMsg);try{L.w("tan",baseRes.string());}catch (Exception e){e.printStackTrace();}}

5、参考资料

(1)retrofit 同步请求。https://www.jianshu.com/p/5eb51d134995

(2)retrofit2 中请求的相对路径和绝对路径。

https://blog.csdn.net/baidu_27763659/article/details/83014551

6、插件GsonFormat快速生成JSon实体类 (Retrofit)

https://blog.csdn.net/tantion/article/details/88822473

7、文章三:

https://www.jianshu.com/p/308f3c54abdd

------------------------------------------------------------------------

原文重点摘要:

0、

1、Retrofit将 Http请求 抽象成 Java接口(一个接口可以定义多个网络请求方法)

2、retrofit的baseUrl不可以为空,要以http或者https开头,'/'结尾

3、替换path中的占位符

path = "blog/{id}"
Call<ResponseBody> getCall(@Path("id") int id);
替换path中的占位符

4、把url作为参数

@GET
Call<ResponseBody> download(@Url String url);


5、post 表单 . @Field & @FieldMap ,与 @FormUrlEncoded 注解配合使用
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);


6、GET 表单 与 @Query和@QueryMap 配合使用

@GET("ajax.php")
Call<Translation> getCall(@Query("a") String a, @Query("f"));@GET("login/user={user}&password={password}")
Observable<RtmpControlResponse> getRtmpTokenTest(@Path("user") String user, @Path("password") String password);

 

自己的例子,

1、get请求

2、post表单请求,

3、post 在body中发送json 

3.1 retrofit 自动把java.class转成json字符串,并放在body中
3.2 重写Factory方法 手动把json字符串,并放在body中发送
3.3 通过RequestBody 手动把json字符串,并放在body中发送

public interface PostBodyInterface {@POST("bussIntfAction!getVersionUpdateInfo.action")Call<ResponseBody> getCall(@Body PostBodyReq postBodyReq);
}/*** post 在body中发送json* retrofit 自动把java.class转成json字符串,并放在body中*/public void postBodyRequest() {HttpLoggingInterceptor logging = new HttpLoggingInterceptor();//设置日志Levelif (BuildConfig.DEBUG) {// development buildlogging.setLevel(HttpLoggingInterceptor.Level.BODY);} else {// production buildlogging.setLevel(HttpLoggingInterceptor.Level.BASIC);}OkHttpClient.Builder httpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(15, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS)//添加拦截器到OkHttp,这是最关键的.addInterceptor(logging);Log.w("tan","postBodyRequest");//步骤4:创建Retrofit对象Retrofit retrofit = new Retrofit.Builder().baseUrl("http://14.116.xxx.xxx:9001/dp-bussintf/") // 设置 网络请求 Url.addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖).client(httpClient.build()).build();// 步骤5:创建 网络请求接口 的实例PostBodyInterface request = retrofit.create(PostBodyInterface.class);PostBodyReq postBodyReq=new PostBodyReq();postBodyReq.appId="";postBodyReq.method="bussIntfAction!getVersionUpdateInfo.action";postBodyReq.tokenId="";PostBodyData postBodyData=new PostBodyData();postBodyData.versionTypeName="45";postBodyData.versionType="1";postBodyReq.data=postBodyData;//对 发送请求 进行封装(设置需要翻译的内容)Call<ResponseBody> call = request.getCall(postBodyReq);//步骤6:发送网络请求(异步)call.enqueue(new Callback<ResponseBody>() {//请求成功时回调@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {// 步骤7:处理返回的数据结果:输出翻译的内容try{Log.w("tan",response.body().string());//不要用下面这个//Log.w("tan",response.body().toString());}catch (Exception e){e.printStackTrace();}}//请求失败时回调@Overridepublic void onFailure(Call<ResponseBody> call, Throwable throwable) {Log.w("tan","请求失败");Log.w("tan",throwable.getMessage());}});}public interface PostBodyStringInterface {@POST("bussIntfAction!getVersionUpdateInfo.action")Call<ResponseBody> getCall(@Body RequestBody data);@Headers({"Content-Type: application/json","Accept: application/json"})@POST("bussIntfAction!getVersionUpdateInfo.action")Call<ResponseBody> getCallForString(@Body String data);
}/*** post 在body中发送json* 通过RequestBody 手动把json字符串,并放在body中发送*/public void postBodyStringRequest() {HttpLoggingInterceptor logging = new HttpLoggingInterceptor();//设置日志Levelif (BuildConfig.DEBUG) {// development buildlogging.setLevel(HttpLoggingInterceptor.Level.BODY);} else {// production buildlogging.setLevel(HttpLoggingInterceptor.Level.BASIC);}OkHttpClient.Builder httpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(15, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS)//添加拦截器到OkHttp,这是最关键的.addInterceptor(logging);Log.w("tan","postBodyRequest");Gson gson=new Gson();//步骤4:创建Retrofit对象Retrofit retrofit = new Retrofit.Builder().baseUrl("http://14.116.xxx.xxx:9001/dp-bussintf/") // 设置 网络请求 Url.addConverterFactory(GsonConverterFactory.create(gson)) //设置使用Gson解析(记得加入依赖).client(httpClient.build()).build();// 步骤5:创建 网络请求接口 的实例PostBodyStringInterface request = retrofit.create(PostBodyStringInterface.class);String data="{\"appId\":\"\",\"data\":{\"versionType\":\"1\",\"versionTypeName\":\"45\"},\"method\":\"bussIntfAction!getVersionUpdateInfo.action\",\"tokenId\":\"\"}";RequestBody body=RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),data);//对 发送请求 进行封装(设置需要翻译的内容)Call<ResponseBody> call = request.getCall(body);//步骤6:发送网络请求(异步)call.enqueue(new Callback<ResponseBody>() {//请求成功时回调@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {// 步骤7:处理返回的数据结果:输出翻译的内容try{Log.w("tan",response.body().string());//不要用下面这个//Log.w("tan",response.body().toString());}catch (Exception e){e.printStackTrace();}}//请求失败时回调@Overridepublic void onFailure(Call<ResponseBody> call, Throwable throwable) {Log.w("tan","请求失败");Log.w("tan",throwable.getMessage());}});}/*** post 在body中发送json* 重写Factory方法 手动把json字符串,并放在body中发送*/public void postBodyStringForFactory() {HttpLoggingInterceptor logging = new HttpLoggingInterceptor();//设置日志Levelif (BuildConfig.DEBUG) {// development buildlogging.setLevel(HttpLoggingInterceptor.Level.BODY);} else {// production buildlogging.setLevel(HttpLoggingInterceptor.Level.BASIC);}OkHttpClient.Builder httpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(15, TimeUnit.SECONDS).writeTimeout(15, TimeUnit.SECONDS)//添加拦截器到OkHttp,这是最关键的.addInterceptor(logging);Log.w("tan","postBodyRequest");//步骤4:创建Retrofit对象Retrofit retrofit = new Retrofit.Builder().baseUrl("http://14.116.xxx.xxx:9001/dp-bussintf/") // 设置 网络请求 Url.addConverterFactory(new ToStringConverterFactory()) //设置使用Gson解析(记得加入依赖).client(httpClient.build()).build();// 步骤5:创建 网络请求接口 的实例PostBodyStringInterface request = retrofit.create(PostBodyStringInterface.class);String data="{\"appId\":\"\",\"data\":{\"versionType\":\"1\",\"versionTypeName\":\"45\"},\"method\":\"bussIntfAction!getVersionUpdateInfo.action\",\"tokenId\":\"\"}";//RequestBody body=RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),data);//对 发送请求 进行封装(设置需要翻译的内容)Call<ResponseBody> call = request.getCallForString(data);//步骤6:发送网络请求(异步)call.enqueue(new Callback<ResponseBody>() {//请求成功时回调@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {// 步骤7:处理返回的数据结果:输出翻译的内容try{Log.w("tan",response.body().string());//不要用下面这个//Log.w("tan",response.body().toString());}catch (Exception e){e.printStackTrace();}}//请求失败时回调@Overridepublic void onFailure(Call<ResponseBody> call, Throwable throwable) {Log.w("tan","请求失败");Log.w("tan",throwable.getMessage());}});}/*** get 请求*/public void getRequest() {Log.w("tan","getRequest");//步骤4:创建Retrofit对象Retrofit retrofit = new Retrofit.Builder().baseUrl("http://fy.iciba.com/") // 设置 网络请求 Url.addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖).build();// 步骤5:创建 网络请求接口 的实例GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);//a=fy&f=auto&t=auto&w=hello%20world//对 发送请求 进行封装Call<Translation> call = request.getCall("fy","auto","auto","hello%20world");//步骤6:发送网络请求(异步)call.enqueue(new Callback<Translation>() {//请求成功时回调@Overridepublic void onResponse(Call<Translation> call, Response<Translation> response) {// 步骤7:处理返回的数据结果response.body().show();Translation result = response.body();}//请求失败时回调@Overridepublic void onFailure(Call<Translation> call, Throwable throwable) {System.out.println("连接失败");}});}/*** post 发送表单*/public void postFormRequest() {//步骤4:创建Retrofit对象Retrofit retrofit = new Retrofit.Builder().baseUrl("http://fanyi.youdao.com/") // 设置 网络请求 Url.addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖).build();// 步骤5:创建 网络请求接口 的实例PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);//对 发送请求 进行封装(设置需要翻译的内容)Call<Translation1> call = request.getCall("I love you");//步骤6:发送网络请求(异步)call.enqueue(new Callback<Translation1>() {//请求成功时回调@Overridepublic void onResponse(Call<Translation1> call, Response<Translation1> response) {// 步骤7:处理返回的数据结果:输出翻译的内容Log.w("tan",response.body().getTranslateResult().get(0).get(0).toString());}//请求失败时回调@Overridepublic void onFailure(Call<Translation1> call, Throwable throwable) {Log.w("tan","请求失败");Log.w("tan",throwable.getMessage());}});}

------------------------------------------------------------------------------------------------------

下文是文章三的内容:

本文注目录:

  • Retrofit入门
  • Retrofit注解详解
  • Gson与Converter
  • RxJava与CallAdapter
  • 自定义Converter
  • 自定义CallAdapter
  • 其它说明

前言

本文中的Retrofit均指代Retrofit2.0。
本文涉及到的代码以及测试使用的接口可在Github上找到。
测试接口服务器在 server 项目下,直接运行 RESTServer.main() 即可启动测试服务器,所面代码示例均使用该接口(接口地址 http://localhost:4567/ ).
当然你也可以自己借助 json-server 或 最新开源的Parse 搭建一个REST API,不过都需要安装Node.js,有兴趣的可以去试试。

接口列表:

地址请求方法参数说明
/blogGETpage={page},sort=asc或desc分页获取Blog列表,每页10条
/blog/{id}GETid获取指定ID的Blog
/blogPOST{"author":"","title":"","content":""}创建一个新Blog
/blog/{id}PUT{"author":"","title":"","content":""} 中至少一个修改Blog
/blog/{id}DELETEid删除一个Blog
/formPOST任意,最终以Json Object形式返回用于测试Form表单,支持文件上传
/headersGETshowAll=true或false,默认false返回自定义请求头,all=true是显示全部

注:以上的接口的{id}{page}均代表一个纯数字,/blog/{id} 可以用 /blog?id=XXX 代替,page同理。

前面写了你应该知道的HTTP基础知识 介绍了HTTP的相关知识,不知道那些想了解Retrofit的同鞋是不是去看了Retrofit的官方教程,曾经我在你真的会用Gson吗?Gson使用指南(四) 中说当你了解了注解、反射、泛型、HTTP的内容只需要看一篇Retrofit的代码示例就可以轻松玩转Retrofit,不知道你玩转了没?
当然注解、反射、泛型的内容还没有写,Retrofit的内容却先来了!毕竟看懂Retrofit也只需要会使就行,你准备好了吗?

1、Retrofit入门

Retrofit 其实相当简单,简单到源码只有37个文件,其中22个文件是注解还都和HTTP有关,真正暴露给用户的类并不多,所以我看了一遍 官方教程 大多数情景就可以无障碍使用,如果你还没有看过,可以先去看看,虽然是英文,但代码才是最好的教程不是么?当然本篇文章会介绍得详细一点,不能写一篇水文,毕竟我给它命名为《你真的会用Retrofit2吗?Retrofit2完全教程》。

1.1、创建Retrofit实例

Retrofit retrofit = new Retrofit.Builder().baseUrl("http://localhost:4567/").build();

创建Retrofit实例时需要通过Retrofit.Builder,并调用baseUrl方法设置URL。
注1: Retrofit2 的baseUlr 必须以 /(斜线) 结束,不然会抛出一个IllegalArgumentException,所以如果你看到别的教程没有以 / 结束,那么多半是直接从Retrofit 1.X 照搬过来的。
注2: 上面的 注1 应该描述为 baseUrl 中的路径(path)必须以 / 结束, 因为有些特殊情况可以不以/结尾(感谢@liujc 提出,81楼),比如 其实这个 URL https://www.baidu.com?key=value用来作为baseUrl其实是可行的,因为这个URL隐含的路径就是 /(斜线,代表根目录) ,而后面的?key=value在拼装请求时会被丢掉所以写上也没用。之所以 Retrofit 2 在文档上要求必须以 /(斜线) 结尾的要求想必是要消除歧义以及简化规则。——2017.10.27

1.2、接口定义

以获取指定id的Blog为例:

public interface BlogService {@GET("blog/{id}")Call<ResponseBody> getBlog(@Path("id") int id);
}

注意,这里是interface不是class,所以我们是无法直接调用该方法,我们需要用Retrofit创建一个BlogService的代理对象。

BlogService service = retrofit.create(BlogService.class);

拿到代理对象之后,就可以调用该方法啦。

1.3、接口调用

Call<ResponseBody> call = service.getBlog(2);
// 用法和OkHttp的call如出一辙,
// 不同的是如果是Android系统回调方法执行在主线程
call.enqueue(new Callback<ResponseBody>() {@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {try {System.out.println(response.body().string());} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onFailure(Call<ResponseBody> call, Throwable t) {t.printStackTrace();}
});

打印结果:

{"code":200,"msg":"OK","data":{"id":2,"date":"2016-04-15 03:17:50","author":"怪盗kidou","title":"Retrofit2 测试2","content":"这里是 Retrofit2 Demo 测试服务器2"},"count":0,"page":0}

示例源码见 Example01.java

2、Retrofit注解详解

上面提到Retrofit 共22个注解,这节就专门介绍这22个注解,为帮助大家更好理解我将这22个注解分为三类,并用表格的形式展现出来,表格上说得并不完整,具体的见源码上的例子注释。

第一类:HTTP请求方法

 

HTTP请求方法注解


以上表格中的除HTTP以外都对应了HTTP标准中的请求方法,而HTTP注解则可以代替以上方法中的任意一个注解,有3个属性:methodpath,hasBody,下面是用HTTP注解实现上面 Example01.java 的例子。

 

public interface BlogService {/*** method 表示请求的方法,区分大小写* path表示路径* hasBody表示是否有请求体*/@HTTP(method = "GET", path = "blog/{id}", hasBody = false)Call<ResponseBody> getBlog(@Path("id") int id);
}

注:method 的值 retrofit 不会做处理,所以要自行保证其准确性,之前使用小写也可以是因为示例源码中的服务器不区分大小写,所以希望大家注意,感谢 @言過祺實 发现该问题。
示例源码见 Example02.java

第二类:标记类

 

标记类注解


示例源码见 Example03.java

 

第三类:参数类

参数类注解

注1:{占位符}和PATH尽量只用在URL的path部分,url中的参数使用QueryQueryMap 代替,保证接口定义的简洁
注2:QueryFieldPart这三者都支持数组和实现了Iterable接口的类型,如ListSet等,方便向后台传递数组。

Call<ResponseBody> foo(@Query("ids[]") List<Integer> ids);
//结果:ids[]=0&ids[]=1&ids[]=2

Path 示例源码见 Example01.java
Field、FieldMap、Part和PartMap 示例源码见 Example03.java
Header和Headers 示例源码见 Example04.java
Query、QueryMap、Url 示例源码见 Example05.java

3、Gson与Converter

在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody,
这也是为什么我在前面的例子接口的返回值都是 Call<ResponseBody>
但如果响应体只是支持转换为ResponseBody的话何必要引入泛型呢,
返回值直接用一个Call就行了嘛,既然支持泛型,那说明泛型参数可以是其它类型的,
Converter就是Retrofit为我们提供用于将ResponseBody转换为我们想要的类型,
有了Converter之后我们就可以写把我们的第一个例子的接口写成这个样子了:

public interface BlogService {@GET("blog/{id}")Call<Result<Blog>> getBlog(@Path("id") int id);
}

当然只改变泛型的类型是不行的,我们在创建Retrofit时需要明确告知用于将ResponseBody转换我们泛型中的类型时需要使用的Converter

引入Gson支持:

compile 'com.squareup.retrofit2:converter-gson:2.0.2'

通过GsonConverterFactory为Retrofit添加Gson支持:

Gson gson = new GsonBuilder()//配置你的Gson.setDateFormat("yyyy-MM-dd hh:mm:ss").create();Retrofit retrofit = new Retrofit.Builder().baseUrl("http://localhost:4567/")//可以接收自定义的Gson,当然也可以不传.addConverterFactory(GsonConverterFactory.create(gson)).build();

示例源码见 Example06.java

这样Retrofit就会使用Gson将ResponseBody转换我们想要的类型。

这是时候我们终于可以演示如使创建一个Blog了!

@POST("blog")
Call<Result<Blog>> createBlog(@Body Blog blog);

@Body注解的的Blog将会被Gson转换成RequestBody发送到服务器。

BlogService service = retrofit.create(BlogService.class);
Blog blog = new Blog();
blog.content = "新建的Blog";
blog.title = "测试";
blog.author = "怪盗kidou";
Call<Result<Blog>> call = service.createBlog(blog);

结果:

Result{code=200, msg='OK', data=Blog{id=20, date='2016-04-21 05:29:58', author='怪盗kidou', title='测试', content='新建的Blog'}, count=0, page=0}

示例源码见 Example07.java

如果你对Gson不熟悉可以参考我写的《你真的会用Gson吗?Gson使用指南》 系列。

4、RxJava与CallAdapter

说到Retrofit就不得说到另一个火到不行的库RxJava,网上已经不少文章讲如何与Retrofit结合,但这里还是会有一个RxJava的例子,不过这里主要目的是介绍使用CallAdapter所带来的效果。

第3节介绍的Converter是对于Call<T>T的转换,而CallAdapter则可以对Call转换,这样的话Call<T>中的Call也是可以被替换的,而返回值的类型就决定你后续的处理程序逻辑,同样Retrofit提供了多个CallAdapter,这里以RxJava的为例,用Observable代替Call

引入RxJava支持:

compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
// 针对rxjava2.x(adapter-rxjava2的版本要 >= 2.2.0)
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

通过RxJavaCallAdapterFactory为Retrofit添加RxJava支持:

Retrofit retrofit = new Retrofit.Builder().baseUrl("http://localhost:4567/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create())// 针对rxjava2.x.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();

接口设计:

public interface BlogService {@POST("/blog")Observable<Result<List<Blog>>> getBlogs();
}

使用:

BlogService service = retrofit.create(BlogService.class);
service.getBlogs(1).subscribeOn(Schedulers.io()).subscribe(new Subscriber<Result<List<Blog>>>() {@Overridepublic void onCompleted() {System.out.println("onCompleted");}@Overridepublic void onError(Throwable e) {System.err.println("onError");}@Overridepublic void onNext(Result<List<Blog>> blogsResult) {System.out.println(blogsResult);}});

结果:

Result{code=200, msg='OK', data=[Blog{id=1, date='2016-04-15 03:17:50', author='怪盗kidou', title='Retrofit2 测试1', content='这里是 Retrofit2 Demo 测试服务器1'},.....], count=20, page=1}

示例源码见 Example08.java

「20160608补充」:像上面的这种情况最后我们无法获取到返回的Header和响应码的,如果我们需要这两者,提供两种方案:
1、用Observable<Response<T>> 代替 Observable<T> ,这里的Responseretrofit2.Response
2、用Observable<Result<T>> 代替 Observable<T>,这里的Result是指retrofit2.adapter.rxjava.Result,这个Result中包含了Response的实例

5、自定义Converter

本节的内容是教大家实现在一简易的Converter,这里以返回格式为Call<String>为例。

在此之前先了解一下Converter接口及其作用:

public interface Converter<F, T> {// 实现从 F(rom) 到 T(o)的转换T convert(F value) throws IOException;// 用于向Retrofit提供相应Converter的工厂abstract class Factory {// 这里创建从ResponseBody其它类型的Converter,如果不能处理返回null// 主要用于对响应体的处理public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {return null;}// 在这里创建 从自定类型到ResponseBody 的Converter,不能处理就返回null,// 主要用于对Part、PartMap、Body注解的处理public Converter<?, RequestBody> requestBodyConverter(Type type,Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {return null;}// 这里用于对Field、FieldMap、Header、Path、Query、QueryMap注解的处理// Retrfofit对于上面的几个注解默认使用的是调用toString方法public Converter<?, String> stringConverter(Type type, Annotation[] annotations,Retrofit retrofit) {return null;}}
}

我们要想从Call<ResponseBody> 转换为 Call<String> 那么对应的F和T则分别对应ResponseBodyString,我们定义一个StringConverter并实现Converter接口。

public static class StringConverter implements Converter<ResponseBody, String> {public static final StringConverter INSTANCE = new StringConverter();@Overridepublic String convert(ResponseBody value) throws IOException {return value.string();}
}

我们需要一个Fractory来向Retrofit注册StringConverter

public static class StringConverterFactory extends Converter.Factory {public static final StringConverterFactory INSTANCE = new StringConverterFactory();public static StringConverterFactory create() {return INSTANCE;}// 我们只关实现从ResponseBody 到 String 的转换,所以其它方法可不覆盖@Overridepublic Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {if (type == String.class) {return StringConverter.INSTANCE;}//其它类型我们不处理,返回null就行return null;}
}

使用Retrofit.Builder.addConverterFactory向Retrofit注册我们StringConverterFactory

Retrofit retrofit = new Retrofit.Builder().baseUrl("http://localhost:4567/")// 我们自定义的一定要放在Gson这类的Converter前面 .addConverterFactory(StringConverterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build();

注:addConverterFactory是有先后顺序的,如果有多个ConverterFactory都支持同一种类型,那么就是只有第一个才会被使用,而GsonConverterFactory是不判断是否支持的,所以这里交换了顺序还会有一个异常抛出,原因是类型不匹配。

只要返回值类型的泛型参数就会由我们的StringConverter处理,不管是Call<String>还是Observable<String>

有没有很简单?如果你有其它的需求处理的就自己实现吧。

示例源码见 Example09.java

6、自定义CallAdapter

本节将介绍如何自定一个CallAdapter,并验证是否所有的String都会使用我们第5节中自定义的Converter。

先看一下CallAdapter接口定义及各方法的作用:

public interface CallAdapter<T> {// 直正数据的类型 如Call<T> 中的 T// 这个 T 会作为Converter.Factory.responseBodyConverter 的第一个参数// 可以参照上面的自定义ConverterType responseType();<R> T adapt(Call<R> call);// 用于向Retrofit提供CallAdapter的工厂类abstract class Factory {// 在这个方法中判断是否是我们支持的类型,returnType 即Call<Requestbody>和`Observable<Requestbody>`// RxJavaCallAdapterFactory 就是判断returnType是不是Observable<?> 类型// 不支持时返回nullpublic abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,Retrofit retrofit);// 用于获取泛型的参数 如 Call<Requestbody> 中 Requestbodyprotected static Type getParameterUpperBound(int index, ParameterizedType type) {return Utils.getParameterUpperBound(index, type);}// 用于获取泛型的原始类型 如 Call<Requestbody> 中的 Call// 上面的get方法需要使用该方法。protected static Class<?> getRawType(Type type) {return Utils.getRawType(type);}}
}

了解了CallAdapter的结构和其作用之后,我们就可以开始自定义我们的CallAdapter了,本节以CustomCall<String>为例。

在此我们需要定义一个CustomCall,不过这里的CustomCall作为演示只是对Call的一个包装,并没有实际的用途。

public static class CustomCall<R> {public final Call<R> call;public CustomCall(Call<R> call) {this.call = call;}public R get() throws IOException {return call.execute().body();}
}

有了CustomCall,我们还需要一个CustomCallAdapter来实现 Call<T>CustomCall<T>的转换,这里需要注意的是最后的泛型,是我们要返回的类型。

public static class CustomCallAdapter implements CallAdapter<CustomCall<?>> {private final Type responseType;// 下面的 responseType 方法需要数据的类型CustomCallAdapter(Type responseType) {this.responseType = responseType;}@Overridepublic Type responseType() {return responseType;}@Overridepublic <R> CustomCall<R> adapt(Call<R> call) {// 由 CustomCall 决定如何使用return new CustomCall<>(call);}
}

提供一个CustomCallAdapterFactory用于向Retrofit提供CustomCallAdapter

public static class CustomCallAdapterFactory extends CallAdapter.Factory {public static final CustomCallAdapterFactory INSTANCE = new CustomCallAdapterFactory();@Overridepublic CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {// 获取原始类型Class<?> rawType = getRawType(returnType);// 返回值必须是CustomCall并且带有泛型if (rawType == CustomCall.class && returnType instanceof ParameterizedType) {Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType);return new CustomCallAdapter(callReturnType);}return null;}
}

使用addCallAdapterFactory向Retrofit注册CustomCallAdapterFactory

Retrofit retrofit = new Retrofit.Builder().baseUrl("http://localhost:4567/").addConverterFactory(Example09.StringConverterFactory.create()).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(CustomCallAdapterFactory.INSTANCE).build();

注: addCallAdapterFactoryaddConverterFactory同理,也有先后顺序。

示例源码见 Example10.java

7、其它说明

7.1 Retrofit.Builder

前面用到了 Retrofit.Builder 中的baseUrladdCallAdapterFactoryaddConverterFactorybuild方法,还有callbackExecutorcallFactoryclientvalidateEagerly这四个方法没有用到,这里简单的介绍一下。

方法用途
callbackExecutor(Executor)指定Call.enqueue时使用的Executor,所以该设置只对返回值为Call的方法有效
callFactory(Factory)设置一个自定义的okhttp3.Call.Factory,那什么是Factory呢?OkHttpClient就实现了okhttp3.Call.Factory接口,下面的client(OkHttpClient)最终也是调用了该方法,也就是说两者不能共用
client(OkHttpClient)设置自定义的OkHttpClient,以前的Retrofit版本中不同的Retrofit对象共用同OkHttpClient,在2.0各对象各自持有不同的OkHttpClient实例,所以当你需要共用OkHttpClient或需要自定义时则可以使用该方法,如:处理Cookie、使用stetho 调式等
validateEagerly(boolean)是否在调用create(Class)时检测接口定义是否正确,而不是在调用方法才检测,适合在开发、测试时使用

7.2 Retrofit的Url组合规则

BaseUrl和URL有关的注解中提供的值最后结果
http://localhost:4567/path/to/other//posthttp://localhost:4567/post
http://localhost:4567/path/to/other/posthttp://localhost:4567/path/to/other/post
http://localhost:4567/path/to/other/https://github.com/ikidouhttps://github.com/ikidou

从上面不能难看出以下规则:

  • 如果你在注解中提供的url是完整的url,则url将作为请求的url。
  • 如果你在注解中提供的url是不完整的url,且不以 / 开头,则请求的url为baseUrl+注解中提供的值
  • 如果你在注解中提供的url是不完整的url,且以 / 开头,则请求的url为baseUrl的主机部分+注解中提供的值

7.3 Retrofit提供的Converter

ConverterGradle依赖
Gsoncom.squareup.retrofit2:converter-gson:2.0.2
Jacksoncom.squareup.retrofit2:converter-jackson:2.0.2
Moshicom.squareup.retrofit2:converter-moshi:2.0.2
Protobufcom.squareup.retrofit2:converter-protobuf:2.0.2
Wirecom.squareup.retrofit2:converter-wire:2.0.2
Simple XMLcom.squareup.retrofit2:converter-simplexml:2.0.2
Scalarscom.squareup.retrofit2:converter-scalars:2.0.2

7.4 Retrofit提供的CallAdapter:

CallAdapterGradle依赖
guavacom.squareup.retrofit2:adapter-guava:2.0.2
Java8com.squareup.retrofit2:adapter-java8:2.0.2
rxjavacom.squareup.retrofit2:adapter-rxjava:2.0.2

7.5 关于源码

看到这儿可能有小伙伴要问为什么源码没有把类拆分到单独的文件,命名也不能体现其用途,这里主要是因为方便大家看源码,而不是将注意力放在反复跳转上,另一方面也是因为同一个例子中不可避免的使用其它小节要介绍的内容,所以就直接用了ExampleXX的形式,不过在项目中千万不要使用这种方式,一定要好好命名,做到见名知意。

结语

其它本博客的内容早就已经完成好了,但由于当时HTTP、反射、注解的博客一篇也没有写,所以一直没有发,期间也有不少的博主写了Retrofit2的博文,不过呢没有自定义相关的内容也没有对各个注解进行详解,所以我还是决定发出来帮助一下那此对Retrofit2无从下手同鞋。

这次Retrofit2的内容就到这里啦,下次再见。



作者:怪盗kidou
链接:https://www.jianshu.com/p/308f3c54abdd
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

这篇关于Retrofit2完全教程 (B)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/815421

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

沁恒CH32在MounRiver Studio上环境配置以及使用详细教程

目录 1.  RISC-V简介 2.  CPU架构现状 3.  MounRiver Studio软件下载 4.  MounRiver Studio软件安装 5.  MounRiver Studio软件介绍 6.  创建工程 7.  编译代码 1.  RISC-V简介         RISC就是精简指令集计算机(Reduced Instruction SetCom

前端技术(七)——less 教程

一、less简介 1. less是什么? less是一种动态样式语言,属于css预处理器的范畴,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 ,也可以借助Node.js在服务端运行。 less的中文官网:https://lesscss.cn/ 2. less编译工具 koala 官网 http://koala-app.

【Shiro】Shiro 的学习教程(三)之 SpringBoot 集成 Shiro

目录 1、环境准备2、引入 Shiro3、实现认证、退出3.1、使用死数据实现3.2、引入数据库,添加注册功能后端代码前端代码 3.3、MD5、Salt 的认证流程 4.、实现授权4.1、基于角色授权4.2、基于资源授权 5、引入缓存5.1、EhCache 实现缓存5.2、集成 Redis 实现 Shiro 缓存 1、环境准备 新建一个 SpringBoot 工程,引入依赖:

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

PHP APC缓存函数使用教程

APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。(Linux APC扩展安装) 系统缓存 它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s(一小时)。但是这样仍会浪费大量C