深度详解retrofit2使用基础入门

程序员七哥 程序员七哥     2022-11-22     721

关键词:

前言

何为Retrofit?

借用官网的原话,

 

Type-safe HTTP client for Android and Java by Square, Inc.

适用于Android 和 Java 的类型安全的HTTP客户端,由Square提供的。(敲黑板)

由此我们可以得知,Retrofit是一种HTTP客户端框架,使用它,我们可以完成有关HTTP的工作!

(心中是不是会想到,Okhttp也是网络框架,到底哪一个好用而不贵呢?带着这个疑问,开始本篇文章!)

目前Retrofit最新版本是2.3.0,可以看看Retrofit的官网Retrofit的github!本文中使用的Retrofit是Retrofit2.3.0版本。

一. Retrofit 入门

1.1 使用Retrofit前 ,需要先引入Retrofit 库。

  1. 在Android中使用Retrofit ,只需要在build.gradle文件中添加以下代码,

 

compile 'com.squareup.retrofit2:retrofit:2.3.0'

然后,同步就可以了!就是这么简单!

  2. 在Java中使用Retrofit ,如果没有使用Maven或者其他项目管理工具的话,那么就需要我们手动将jar 包下载下来,而且需要下载好几个jar。下载完成后,加入到项目的build path。具体的需要下载的jar截图如下所示,

 

如果使用了Maven或者其他项目管理工具,那么就只需要在相关的依赖管理文件中加入Retrofit 的依赖即可!例如在Maven依赖管理文件中添加如下代码,

 

 

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>retrofit</artifactId>
  <version>2.3.0</version>
</dependency>

    a. Retrofit 的网络请求其实是交给okhttp处理的,所以需要引入okhttp,我们都知道okhttp也是Square的开源杰作。(敲黑板)

    b. okio是什么玩意?okio 是一个包装了 java.io 和 java.nio api 的库,以便可以更容易的访问、存储以及处理数据,它是Square公司推出的Java IO库,也是OKHttp依赖的IO库。(敲黑板)

Retrofit ,OKhttp,Okio 应该可以称为Square全家桶吧!

  PS:

 因为需要服务端提供接口数据,所以我在自己电脑上搭建了一个web服务器。具体如何搭建,请看java web开发(二) 接口开发

1.2 下面就先以一个实例开始Retrofit !(本文是在Java使用Retrofit ,下篇文章讲解在Android使用Retrofit !)

 

  1. 定义接口。

  使用Retrofit ,首先需要将你的HTTP API改造成Java接口。例如,

 

	public interface ApiService 

		@GET("StudentInq")
		Call<ResponseBody> getStudents();
	

ApiService 接口定义了一个方法getStudents(),@GET表示该方法是GET请求,该方法没有参数,@GET("StudentInq")中的“StudentInq”是path(相对URL),这个path和baseUrl一起组成了接口的请求全路径,例如baseUrl是“http://localhost:8080/mServer/”,那么全路径就是“http://localhost:8080/mServer/getStudent”。(baseUrl下文会提到)

 

  2. 实例化Retrofit。 

     a. 首先定义了服务请求的URL,

 

// 服务请求url
	public static final String API_URL = "http://localhost:8080/mServer/";

 

这个API_URL就是baseUrl,是由ip和端口等组成。

PS: 请求URL,需要以“/”结尾,否则会报错。(敲黑板)

    b. 创建Retrofit 实例,

 

	Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();

通过构造者模式创建了Retrofit ,其中设置了请求的

baseUrl。

     c. 接着创建接口实例,

 

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

从源码中可以得知,内部使用 了动态代理模式。

    d. 下面就可以调用接口中的方法了,

 

// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents();
		//异步执行请求
		call.enqueue(...);

如果是同步请求,调用execute;而发起一个异步请求则调用enqueue。
  下面是完整的代码,

 

public class GetTest 

	// 服务请求url
	public static final String API_URL = "http://localhost:8080/mServer/";

	public interface ApiService 

		@GET("StudentInq")
		Call<ResponseBody> getStudents();
	

	public static void main(String[] args) 
		getList();
	

	/**
	 * 获取数据
	 */
	private static void getList() 
		// 创建Retrofit实例
		Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();
		// 生成ApiService接口代理
		ApiService service = retrofit.create(ApiService.class);
		// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents();
		//异步执行请求
		call.enqueue(new Callback<ResponseBody>() 

			@Override
			public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) 
				// TODO Auto-generated method stub
				try 
					System.out.println(response.body().string());
				 catch (IOException e) 
					// TODO Auto-generated catch block
					e.printStackTrace();
				
			

			@Override
			public void onFailure(Call<ResponseBody> arg0, Throwable arg1) 
				// TODO Auto-generated method stub

			
		);
	


  运行后的截图如下,

 


  使用Retrofit 完成网络请求确实很方便、快捷,并且代码量也少!

  上面是使用Retrofit 的简单实例,可以看到,在定义接口时使用了注解,那么下面我们就来看看Retrofit 的注解。

二. Retrofit 注解

   Retrofit 共22个注解,这些注解大致分三类,请求方法类、标记类和参数类。Retrofit 的注解是运行时注解,在运行时创建动态代理的方式来提供AOP能力。

  2.1 请求方法类


  1. 序号1~7。

    a. 都是HTTP的请求方法;

    b. 这些注解都有一个value()方法,例如,

 

		@GET("StudentInq")
		Call<ResponseBody> getStudents();

 

    下面是GET注解的源码,

它有一个value()方法,接收一个字符串(StudentInq),“StudentInq”这个就是path,也就是相对URL,它与baseUrl组成全路径;

    c. 这个path可以使用变量,如 id ,并使用 @Path("id") 注解为 id 提供值,例如;

 

@GET("StudentInq/id")
		Call<ResponseBody> getStudentById(@Path("id")int id);

  2. 序号8,HTTP注解。

 

    a. 用于替代以上7个注解,以及其他扩展方法;

    b. 有三个属性,method、path、hasBody,首先看看HTTP注解的源码,

     method:请求方法(区分大小写),

     path:请求相对路径,

     hasBody:是否有body(请求体),

下面是HTTP注解的使用例子,

 

	@HTTP(method="GET",path="StudentInq/id",hasBody=false)
		Call<ResponseBody> getStudentById(@Path("id")int id);

  2.2 标记类

  2.3 参数类

    1. @Headers

    使用 @Headers 注解设置固定的请求头,所有请求头不会相互覆盖,即使名字相同。

    2. @Header

    使用 @Header 注解动态更新请求头,匹配的参数必须提供给 @Header ,若参数值为 null ,这个头会被省略,否则,会使用参数值的 toString 方法的返回值。

    3. @Body

    使用 @Body 注解,指定一个对象作为 request body。

    4. @Field

    表单提交,与 FieldMap、FormUrlEncoded注解 配合使用。

    5. @FieldMap

    表单提交,与 Field、FormUrlEncoded 注解配合使用;接受 Map<String, String> 类型,非 String 类型会调用 toString() 方法。

    6. @Part

    表单字段,与 PartMap、@Multipart注解 配合,适合文件上传情况。表示多部分请求中的单个部分。该注解的的参数类型有三种:

       a. 如果类型是MultipartBody.Part,则内容将直接使用。 忽略注释中的名称(即@Part MultipartBody.Part部分);

       b. 如果类型是RequestBody,则该值将直接与其内容类型一起使用。 在注释中提供零件名称(例如@Part(“foo”)RequestBody foo);

       c. 其他对象类型将通过使用转换器转换为适当的表示形式。 在注释中提供零件名称(例如,@Part(“foo”)Image photo);值可以是null,它将从请求主体中被省略。

    7. @PartMap

    表单字段,与 Part 、@Multipart注解配合,适合文件上传情况;默认接受 Map<String, RequestBody> 类型,非 RequestBody 会通过 Converter 转换。

    8. @Path

    请求 URL 可以替换模块来动态改变,替换模块是 包含的字母数字字符串,替换的参数必须使用 @Path 注解的相同字符串。

    9. @Query和@QueryMap

    将数据转换成“键=值”的形式,并且添加到URL的结尾。例如,id=110&sex=nan。

    10. @Url

    动态指定Path路径。

  PS:

    1. Query、QueryMap 与 Field、FieldMap 功能一样,生成的数据形式一样;只不过Query、QueryMap 的数据体现在 Url 上;Field、FieldMap 的数据是请求体;

    2. 占位符和PATH尽量只用在URL的path部分,url中的参数使用Query和QueryMap 代替,保证接口定义的简洁。

  以上就是Retrofit 的全部注解。这些概念还是比较抽象,下面就通过实例,看看这些注解的具体使用。

三. 注解实例

  3.1 @Path 使用。

使用GET请求,接口请求全路径是“http://localhost:8080/mServer/getStudent/1”,下面是具体实现代码,

 

public class PathTest 

	// 服务请求url
	public static final String API_URL = "http://localhost:8080/mServer/";

	public interface ApiService 

		@GET("getStudent/id")//这里的id 表示是一个变量
		Call<ResponseBody> getStudents(@Path("id") int id);

	

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		getList();
	

	/**
	 * 获取数据
	 */
	private static void getList() 
		// 创建Retrofit实例
		Retrofit retrofit = new Retrofit.Builder().baseUrl(API_URL).build();
		// 生成ApiService接口代理
		ApiService service = retrofit.create(ApiService.class);
		// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents(1);
		// 异步执行请求
		call.enqueue(new Callback<ResponseBody>() 

			@Override
			public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) 
				// TODO Auto-generated method stub
				try 
					System.out.println(response.body().string());
				 catch (IOException e) 
					// TODO Auto-generated catch block
					e.printStackTrace();
				
			

			@Override
			public void onFailure(Call<ResponseBody> arg0, Throwable arg1) 
				// TODO Auto-generated method stub

			
		);
	

运行后客户端的截图,

并且我在服务端打印了RequestURI和RequestURL,下面是运行截图,

  3.2 @Query、@QueryMap和@URL 使用。

    1. 首先看@Query注解。定义接口,

 

...
@GET("StudentInq")
		Call<ResponseBody> getStudents(@Query("sex") String sex,@Query("mobile") String mobile);
...

接口调用,

...
// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents("nan","123456789");
...

我在服务端doget()方法中打印了请求参数和其他信息,下面是主要代码,

...
Enumeration enu=request.getParameterNames();  
		while(enu.hasMoreElements())  
		String paraName=(String)enu.nextElement();  
		System.out.println(paraName+": "+request.getParameter(paraName));  
		
		
		System.out.println("getRequestURI: "+request.getRequestURI());  
		System.out.println("getRequestURL: "+request.getRequestURL());  
		System.out.println("getQueryString: "+request.getQueryString());  //获取get方式中的参数列表
...

运行后服务端截图,

 


  2. 接着是@QueryMap注解。

定义接口,

...
	@GET("StudentInq")
		Call<ResponseBody> getStudents(@QueryMap Map<String, String> map);
...

接口调用,

...
Map<String, String> map=new HashMap<>();
		map.put("id", "110");
		map.put("sex", "nan");
		// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents(map);
...

运行后服务端截图,

  小结:

    @Query注解和@QueryMap注解,他们的功能本质上是一样的,都是把数据转化成类似“id=110&sex=nan”这样的格式,并且添加到请求的路径尾部;只不过@Query是一个一个接收参数,而@QueryMap是一次性接收一个集合。

    @Query注解和@QueryMap注解的参数如果不是String(或Map的第二个泛型参数不是String)时会被默认会调用toString转换成String类型。

  3. @URL注解。

定义接口,

...
		@GET
		Call<ResponseBody> getStudents(@Url String name);
...

接口调用,

...
		// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents("StudentInq");
...

就不显示运行后服务端截图了,前面的运行结果一样!使用@URL注解可以设置Path(相对URL)。
  3.3 @Headers和@Header注解。

首先是@Headers注解。

...		
@Headers("Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
		    "User-Agent: Retrofit-Sample-App",
             "Cache-Control:no-cache")
		@GET("StudentInq")
		Call<ResponseBody> getStudents();
...

定义接口后,调用接口。我在服务端打印了请求头信息,具体代码如下,

...
		Enumeration<String> reqHeadInfos = request.getHeaderNames();// 获取所有的请求头
		while (reqHeadInfos.hasMoreElements()) 
			String headName = (String) reqHeadInfos.nextElement();
			String headValue = request.getHeader(headName);// 根据请求头的名字获取对应的请求头的值
			System.out.println(headName + ": " + headValue);
		
...

调用接口后服务端截图,


  接着看@Header注解,

定义接口,

...
		@GET("StudentInq")
		Call<ResponseBody> getStudents(@Header("Pragma") String Pragma);
...

调用接口,

...
// 调用具体接口方法
		Call<ResponseBody> call = service.getStudents("no-cache");
...

调用接口后服务端截图,


  小结:@Headers和@Header这两个注解都是用来设置请求头信息的,只不过@Headers是设置固定值的请求头,而@Header可以设置动态的请求头。

  3.4 @Field和@FieldMap。

这两个注解,我们前面已经给出了定义,需要配合@FormUrlEncoded使用。

定义接口,

...
		
		@FormUrlEncoded
		@POST("getStudent")
		Call<ResponseBody> getStudent(@Field("id")int id,@Field("name")String name,@Field("sex")String sex);
		
		@FormUrlEncoded
		@POST("getStudent")
		Call<ResponseBody> getStudent(@FieldMap Map<String, String>map);
...

定义了两个方法,都是POST请求。第一个方法使用@Field定义了几个参数,而第二个方法使用@FieldMap定义了一个map。接口调用,

...
// 调用具体接口方法
				Call<ResponseBody> call = service.getStudent(111,"xx","nan");
		Map<String, String> map=new HashMap<>();
		map.put("id", "119");
		map.put("name", "xw");
		map.put("sex", "nan");
		// 调用具体接口方法
		Call<ResponseBody> call1 = service.getStudent(map);
...

我在服务端doPost()方法中打印请求参数列表,具体代码如下,

...
		Enumeration enu=request.getParameterNames();  
		while(enu.hasMoreElements())  
		String paraName=(String)enu.nextElement();  
		System.out.println(paraName+": "+request.getParameter(paraName));  
		
...

调用接口后服务端截图,

 


  小结:@Field和@FieldMap这两个注解,在本质上是一样的,都是将请求的数据按照 key1=val1&key2=val2 的方式进行编码,并放置在请求体中,并且Form表单的编码格式必须是“application/x-www-form-urlencoded”(也就是POST需要使用@FormUrlEncoded)。换句话说,在使用@Field和@FieldMap这两个注解时,需要满足两个方面:

    1. 接口方法必须是POST请求;

    2. 接口方法必须有@FormUrlEncoded注解;

如果在使用时,缺少上面的某一条件,程序会报错!至于二者的区别也很明显,就不多说了!

  至此,有关注解方面的,就到此结束了!

 

  由于篇幅原因,有关Retrofit的其他内容,就暂时放到下篇文章!详情请看 深度详解Retrofit2使用(二)实践

欢迎大家关注我的公众号


 

 

 

 

 

 


 

 

 

 

深度详解retrofit2使用实践

   深度详解Retrofit2使用(一)基础入门 这篇文章主要描述了Retrofit的注解,没有涉及具体Retrofit的使用。今天就以Android为平台,看看Retrofit是如何使用。一.准备 1.1导入Retrofit库。上篇文章,我们提到过在And... 查看详情

使用 Moshi/Retrofit2 访问深度嵌套的 JSON 数组

】使用Moshi/Retrofit2访问深度嵌套的JSON数组【英文标题】:AccessingdeeplynestedJSONarraywithMoshi/Retrofit2【发布时间】:2019-03-0400:47:58【问题描述】:我只需要“照片”数组。我的JSON如下所示:"photos":"page":1,"pages":"1000","perpage":1,"total":"100... 查看详情

a.深度学习基础入门篇[二]:机器学习常用评估指标:aucmapisfidperplexitybleurouge等详解

A.深度学习基础入门篇[二]:机器学习常用评估指标1.基础指标简介机器学习的评价指标有精度、精确率、召回率、P-R曲线、F1值、TPR、FPR、ROC、AUC等指标,还有在生物领域常用的敏感性、特异性等指标。在分类任务中࿰... 查看详情

keras深度学习实战(13)——目标检测基础详解(代码片段)

Keras深度学习实战(13)——目标检测基础详解0.前言1.目标检测概念2.创建自定义目标检测数据集2.1windows2.2Ubuntu2.3MacOS3.使用选择性搜索在图像内生成候选区域3.1候选区域3.2选择性搜索3.3使用选择性搜索生成候选区域4.交并... 查看详情

转载零基础入门深度学习-感知器(代码片段)

【转载】零基础入门深度学习(1)-感知器机器学习深度学习转载自https://www.zybuluo.com/hanbingtao/note/433855无论即将到来的是大数据时代还是人工智能时代,亦或是传统行业使用人工智能在云上处理大数据的时代,作为一个有理... 查看详情

零基础入门深度学习目录

...ensorFlow2🚩作者:K同学啊🥇精选专栏:《深度学习100例》🔥推荐专栏:《零基础入门深度学习》📚选自专栏:《Matplotlib教程》🧿优秀专栏:《Python入门100题》📕入门篇1.环境配置【零... 查看详情

python基础|timerandomcollectionsitertools标准库详解

⭐本专栏旨在对Python的基础语法进行详解,精炼地总结语法中的重点,详解难点,面向零基础及入门的学习者,通过专栏的学习可以熟练掌握python编程,同时为后续的数据分析,机器学习及深度学习的代码能力打下坚实的基础。 查看详情

python基础|文件异常以及模块详解

⭐本专栏旨在对Python的基础语法进行详解,精炼地总结语法中的重点,详解难点,面向零基础及入门的学习者,通过专栏的学习可以熟练掌握python编程,同时为后续的数据分析,机器学习及深度学习的代码能力打下坚实的基础。 查看详情

转载零基础入门深度学习-感知器(代码片段)

【转载】零基础入门深度学习(1)-感知器机器学习深度学习转载自https://www.zybuluo.com/hanbingtao/note/433855无论即将到来的是大数据时代还是人工智能时代,亦或是传统行业使用人工智能在云上处理大数据的时代,作为一个有理... 查看详情

python零基础入门:字符串使用详解(常用方法及使用案例)(代码片段)

...苦短,我用Python——Lifeisshort,youneedPython字符串详解1、字符串的定义1)单引号字符串2)双引号字符串3)多行字符串4)打印字符串2、字符串拼接3、检查字符串4、字符串是数组1࿰ 查看详情

web开发基础php开发基础快速入门-php程序符号标记和程序注释的使用及空白符详解

前言PHP开发基础开速入门系列《​【web开发基础】php开发基础快速入门(1)-PHP介绍及开发环境快速安装和基本使用介绍​》《​【web开发基础】php开发基础快速入门(2)-PHP的程序开发​》这是PHP开发基础快速入门的第三篇文章,前两... 查看详情

转载零基础入门深度学习-参考文献

【转载】零基础入门深度学习-参考文献机器学习深度学习参考资料零基础入门深度学习(1)-感知器TomM.Mitchell,“机器学习”,曾华军等译,机械工业出版社零基础入门深度学习(2)-线性单元和梯度下降TomM.Mitchell,“机器学习”,曾华军... 查看详情

转载零基础入门深度学习-参考文献

【转载】零基础入门深度学习-参考文献机器学习深度学习参考资料零基础入门深度学习(1)-感知器TomM.Mitchell,“机器学习”,曾华军等译,机械工业出版社零基础入门深度学习(2)-线性单元和梯度下降TomM.Mitchell,“机器学习”,曾华军... 查看详情

(转)零基础入门深度学习-长短时记忆网络(lstm)

...理大数据的时代,作为一个有理想有追求的程序员,不懂深度学习(DeepLearning)这个超热的技术,会不会感觉马上就out了?现在救命稻草来了,《零基础入门深度学习》系列文章旨在讲帮助爱编程的你从零基础达到入门级水平。... 查看详情

python基础|函数及面向过程编程详解

...熟练掌握python编程,同时为后续的数据分析,机器学习及深度学习的代码能力打下坚实的基础。 查看详情

学习资料

一、深度学习1、https://www.zybuluo.com/hanbingtao/note/433855零基础入门深度学习(1)-感知器 零基础入门深度学习(2)-线性单元和梯度下降 零基础入门深度学习(3)-神经网络和反向传播算法 零基础入门深度学习(4)-卷积神经网络&nb... 查看详情

深度学习入门基础二简单理解transformer

【深度学习入门基础】二、简单理解Transformer文章目录【深度学习入门基础】二、简单理解Transformer自注意力层多头注意力Transformer输入(输出)嵌入位置编码Add和NormFeedForwardLinearNxseq2seq模型和marked注意力机制训练其他这... 查看详情

深度学习入门基础二简单理解transformer

【深度学习入门基础】二、简单理解Transformer文章目录【深度学习入门基础】二、简单理解Transformer自注意力层多头注意力Transformer输入(输出)嵌入位置编码Add和NormFeedForwardLinearNxseq2seq模型和marked注意力机制训练其他这... 查看详情