Retrofit的使用

news2024/12/27 7:30:51

文章目录

  • Retrofit的使用
    • 最好用的网络库: Retrofit
      • Retrofit的基本用法
      • 处理复杂接口的地址类型
      • Retrofit构建器的最佳写法

Retrofit的使用

最好用的网络库: Retrofit

  • Retrofit是一款由Square公司开发的网络库,但是它和OkHttp定位完全不同,OkHttp的侧重点是底层通信的实现,而Retrofit的侧重点是上层接口的封装
  • 事实上,Retrofit就是Square公司在OkHttp的基础上进一步开发出来的应用层网络通信库,使得我们可以用更加面向对象的思维进行网络操作

Retrofit的基本用法

  • 首先我们可以配置好一个根路径,然后在指定服务接口地址地址时只需要使用相对路径即可,这样就不用每次都指定完整URL地址了
  • 另外Retrofit允许我们对服务器接口进行归类,将功能同属一类的服务器接口定义到同一个接口文件当中,从而让代码结构变得更加合理
  • 最后我们也完全不用关心网络通信的细节,只需要在接口文件中声明一系列方法和返回值,然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数.
  • 我们在程序中调用该方法的时候,Retrofit会自动向对应的服务器接口发起请求,并将相应的数据解析成返回值声明的类型,这就使得我们可以用更加面向对象的方式来进行网络操作.
  • 要想使用Retrofit,我们必须在项目当中添加必要的依赖库
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KXtGhhHO-1672298010670)(C:/Users/zhengbo/%E6%88%91%E7%9A%84%E5%AD%A6%E4%B9%A0/Typora%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%AE%89%E5%8D%93/image-20221227123048115.png)]

  • 因为Retrofit是基于OkHttp开发的,因此添加上述第一条依赖会自动将Retrofit,OkHttp和Okio这几个库一起下载,我们无需再手动引入OkHttp库

  • 另外Retrofit还会将服务器返回的JSON数据自动解析成对象,因此上述第二条依赖就是一个Retrofit转换库,它借助GSON来解析JSON数据,所以会将GSON库一起下载下来,这样我们就不需要再导入GSON库了

  • 继续使用上面的JSON接口,所以先定义一个App类,并加入id,name,version这三个字段

  • class App(val id: Int, val name: String, val version: String)
    
  • 接下来我们可以根据服务器接口的功能进行归类,创建不同种类的接口文件,并在其中定义对应具体服务器接口的方法

  • 但是在我们本地Apache服务器只有一个提供JSON数据的接口,所以只需要定义一个接口文件,并包含一个方法即可

  • 新建AppService接口,代码如下所示

interface AppService {
    @GET("get_data.json")
    fun getAppData() : Call<List<App>>
}
  • 通常Retrofit的接口文件建议以具体功能种类名开头,以Service结尾,这是一种比较好的命名方式
  • 在上述代码当中,在方法上面添加了@GET注解,表示当调用getAppData()方法的时候,Retrofit会发起一条GET请求,请求的地址就是我么你在@GET注解中传入的具体参数,在这个地方只需要我们传入相对路径就可以了,根路劲会在稍后进行设置.
  • 然后再上述的代码当中,getAppData()方法的返回值必须声明成为Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象,由于服务器响应的是一个包含App数据类型的JSON数据,因此在声明中将泛型指定为了List
  • 定义好接口之后,接下来在界面上添加一个按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/getAppData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Get App Data" />


</LinearLayout>
  • 在MainActivity当中处理它的点击事件
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        getAppData.setOnClickListener {
            //首先使用Retrofit.Builder来构建Retrofit对象
            val retrofit = Retrofit.Builder()
                //指定retrofit请求的根路径
                .baseUrl("http://10.0.2.2/")
                //指定retrofit在解析数据的时候所使用的转换库
                .addConverterFactory(GsonConverterFactory.create())
                .build()
            //使用retrofit对象的create方法,传入具体Service接口所对应的Class类型,创建一个该接口的动态代理对象
            //有了动态代理对象之后,就可以随意调用接口中定义的所有方法了,而retrofit会自动执行具体的处理就可以了
            val appService = retrofit.create(AppService::class.java)
            //然后调用getAppData()方法会返回一个Call<List<App>>对象
            //这时候我们再调用一下enqueue()方法,retrofit就会根据注解中配置的服务器接口去进行网络请求了
            //服务器响应的数据会回调到enqueue()方法中传入的Callback实现当中
            appService.getAppData().enqueue(object : Callback<List<App>> {
                override fun onResponse(call: Call<List<App>>, response: Response<List<App>>) {
                    //调用response.body()方法将会得到retrofit解析后的对象,也就是List<App>数据,最后遍历其中的数据,将数据打印出来即可
                    val list = response.body()
                    if (list != null) {
                        for (app in list) {
                            Log.d("MainActivity", "id is ${app.id}")
                            Log.d("MainActivity", "name is ${app.name}")
                            Log.d("MainActivity", "version is ${app.version}")
                        }
                    }
                }

                override fun onFailure(call: Call<List<App>>, t: Throwable) {
                    t.printStackTrace()
                }
            })

        }
    }
}
  • 因为服务器使用的是HTTP,需要对网络安全进行以下配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ELmsYR7K-1672298010672)(C:/Users/zhengbo/%E6%88%91%E7%9A%84%E5%AD%A6%E4%B9%A0/Typora%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%AE%89%E5%8D%93/image-20221227133023187.png)]

  • 这里设置允许使用明文的方式来进行网络请求,同时声明了网络权限,运行程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDoXcah7-1672298010673)(C:/Users/zhengbo/%E6%88%91%E7%9A%84%E5%AD%A6%E4%B9%A0/Typora%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%AE%89%E5%8D%93/image-20221227133449467.png)]

  • 可以看到服务器的数据成功响应并解析出来了

处理复杂接口的地址类型

  • 在真实的开发环境中,服务器所提供的接口地址不可能会一直如此简单,但是合理使用Retrofit是可以应对千变万化的情况的
  • 先定义一个Data类
class Data(val id: String, val content: String)
  • 当服务器的地址如下所示的时候
GET http://example.com/get_data.json
  • 这是最简单的一种情况,接口地址是静态的,永远不会改变,那么对应到Retrofit当中,使用如下写法即可
interface ExampleService {
    @GET("get_data.json")
    fun getData() : Call<Data>
}
  • 但是服务器不会总是给我们提供这种静态类型的接口,很多的时候,接口地址中的部分内容可能会是动态变化的,比如下面这个接口地址
GET http://example.com/<page>/get_data.json
  • 在这个接口当中,部分代表页数,我们传入不同的页数,服务器返回的数据也会不同,这种接口地址对应到Retrofit当中的写法如下所示
interface ExampleService {
    @GET("{page}/get_data.json")
    fun getData(@Path("page") page: Int) : Call<Data>
}
  • 在@GET注解中指定的接口地址当中,这里使用了一个{page}占位符,然后又在getData()方法中添加了一个page参数,并使用@Path(“page”)注解来声明这个参数,这样当调用getData()方法发起请求的时候,Retrofit就会自动将page参数的值替换到占位符的位置,从而组成一个合法的请求地址
  • 另外很多服务器接口还要求我们传入一系列的参数,格式如下
GET http://example.com/get_data.json?u=<user>&t=<token>
  • 这是一种标准的带参数GET请求的格式,接口地址的最后用问号来连接参数部分,每一个参数都是一个等号连接键值对,多个参数之间使用"&"符号进行分隔
  • 在上述的地址中,服务器要求我们传入user和token两个参数的值,对于这种情况,虽然也可以使用上述@Path注解的方式来进行解决,但是这样会比较麻烦,Retrofit针对这种带参数的GET请求,专门提供了一种语法支持
interface ExampleService {
    @GET("get_data.json")
    fun getData(@Query("u") user: String, @Query("t") token: String) : Call<Data>
}
  • 这样在getData()方法中添加了user和token两个参数,并使用@Query注解对它们进行声明,这样发起网络请求的时候,Retrofit就会自动按照带参数GET请求的格式将这两个参数构建到请求地址当中
  • 不过HTTP并不只是只是之后GET这一种请求类型,还包含,POST,PUT,PATCH,DELETE
  • 这些方法之间的分工也很明确,GET用于从服务器获取数据,POST用于向服务器提交数据,PUT和PATCH请求用于修改服务器上的数据,DELETE用于删除服务器上面的数据
  • 而Retrofit对常用的HTTP请求类型都进行了支持,使用@GET,@POST,@PUT,@PATCH,@DELETE等注解,就可以让Retrofit发出相应的请求了.

Retrofit构建器的最佳写法

  • 就是说下面这种获取Service接口的动态代理对象的写法实际上是有一些麻烦的
val retrofit = Retrofit.Builder()
	.baseUrl("http://10.0.2.2/")
	.addConverterFactory(GsonConverterFactory.create())
	.build()
val appService = retrofit.create(AppService::class.java)
  • 我们想要得到AppService的动态代理对象,需要先使用Retrofit.Builder构建出一个Retrofit对象,然后调用Retrofit对象的create()方法创建动态代理对象.如果只是写一次还好,每次调用任何服务接口都要像这样写一次的话,肯定没人能受的了.
  • 事实上,确实也没有每次都要写一遍的必要,因为构建出的Retrofit对象是全局通用的,只需要在调用create()方法时针对不同的Service接口传入相应的Class类型即可.因此,我们可以将通用的这部分功能封装起来,从而简化获取Service接口动态代理对象的过程.
  • 新建一个ServiceCreator单例类,代码如下所示
object ServiceCreator {
    private const val BASE_URL = "http://10.0.2.2"
    private val retrofit = Retrofit.Builder()
    	.baseUrl(BASE_URL)
    	.addConverterFactory(GsonConverterFactory.create())
    	.build()
    
    fun<T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
}
  • 这里定义了一个单例类,并在它的内部定义了一个BASE_URL常量,用于指定Retrofit的根路径.
  • 然后同样在内部使用Retrofit.Builder构建一个Retrofit对象,注意这些都是private修饰符来进行修饰声明的,相当于对于外部而言,它们都是不可见的.
  • 最后提供一个外部可见的create()方法,并接收一个Class类型的参数,当在外部调用这个方法的时候,实际上就是调用了Retrofit对象的create()方法,从而创建出相应的Service接口的动态代理对象.
  • 经过这样封装之后,Retrofit的用法将会变得异常简单,比如我们想要获得一个AppService接口的动态代理对象,只需要使用如下写法即可
val appService = ServiceCreator.create(AppService::class.java)
  • 之后就可以随意调用AppService接口中定义的任何方法了.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/126083.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java集合类——LinkedList(单链表及双链表)

一&#xff0c;ArrayList的缺陷 1.空间浪费 在之前的博客中&#xff0c;我利用源码详细的讲解了ArrayList这个集合类&#xff08;尤其是扩容机制&#xff09;&#xff0c;可以知道ArrayList的底层主要是一个动态的可变数组&#xff0c;容量满的时候需要进行1.5倍扩容。但是我…

第二十讲:神州路由器静态路由的配置

实验拓扑图如下所示 设备 端口 IP 子网掩码 网关 Router-A G0/0 120.83.200.55 255.255.255.0 无 G0/3 192.168.0.1 255.255.255.0 无 Router-B G0/0 120.83.200.56 255.255.255.0 无 G0/3 192.168.1.1 255.255.255.0 无 PC1 192.168.0.2 255.255.255…

jQuery 的基本使用

1、jQuery 介绍 1.1、JavaScript 库 JavaScript库&#xff1a;即 library&#xff0c;是一个封装好的特定的集合&#xff08;方法和函数&#xff09;。从封装一大堆函数的角度理解库&#xff0c;就是在这个库中&#xff0c;封装了很多预先定义好的函数在里面&#xff0c;比如动…

【C++】const关键字

【C】const关键字 0x1 常量 C定义常量有两种方式 #define 宏常量&#xff1a;#define 常量名 常量值 通常在文件上方定义&#xff0c;表示一个常量宏常量不可以修改 // 宏常量 #define MAX 999int main() {return 0; }const修饰的变量&#xff1a; const 数据类型 常量名 …

docker 安装Es

1、下载镜像文件 docker pull elasticsearch:7.4.2 存储和检索数据 docker pull kibana:7.4.2 可视化检索数据 2、创建实例 1、ElasticSearch mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data echo "http.host: 0.0.0.0" >…

第三十六章 数论——容斥原理

第三十六章 数论——容斥原理一、容斥原理1、定理内容二、代码模板1、问题&#xff08;1&#xff09;如何求出能够被整除的个数&#xff1f;&#xff08;2&#xff09;如何枚举出2n−12^n-12n−1种情况&#xff1f;2、代码实现&#xff1a;一、容斥原理 1、定理内容 我们在高…

开启微信小程序的学习窗口(第一课)

第一个问题 什么是微信小程序 微信小程序&#xff0c;小程序的一种&#xff0c;英文名Wechat Mini Program&#xff0c;是一种不需要下载安装即可使用的应用&#xff0c;它实现了应用“触手可及”的梦想&#xff0c;用户扫一扫或搜一下即可打开应用。 全面开放申请后&#xff0…

Educational Codeforces Round 93 (Rated for Div. 2) K. Lonely Numbers

Problem - C - Codeforces 翻译&#xff1a; 给定一个数组&#x1d44e;1&#xff0c;&#x1d44e;2&#xff0c;…&#xff0c;&#x1d44e;&#x1d45b;&#xff0c;由0到9的整数组成。一子数组&#x1d44e;&#x1d459;,&#x1d44e;&#x1d459; 1,&#x1d44e;&…

R实战 | 置换多元方差分析(以PCoA的PERMANOVA分析为例)

adonis-cover置换多元方差分析&#xff08;Permutational multivariate analysis of variance&#xff0c;PERMANOVA&#xff09;&#xff0c;又称非参数多因素方差分析&#xff08;nonparametric multivariate analysis of variance&#xff09;、或者ADONIS分析。它利用距离矩…

第003课 - 分布式基础概念

文章目录 集群、分布式、节点远程调用负载均衡服务注册/发现和注册中心服务熔断和降级API网关我们以前将所有的代码、页面、sql语句,写到一个应用,如果有一个地方有问题,整个就不可用了。 我们可以基于业务边界进行服务微化和拆分。 如果有一个出现了问题,不影响其他服务…

迅为LS2K0500开发板龙芯全国产处理器LoongArch架构核心主板

全国产开发板&#xff1a; 迅为iTOP-LS2K0500开发采用龙芯LS2K0500处理器&#xff0c;基于龙芯自主指令系统&#xff08;LoongArch&#xff09;架构&#xff0c;片内集成64位LA264处理器核、32位DDR3控制器、2DGPU、DVO显示接口、两路PCle2.0、两路SATA2.0、四路USB2.0、一路US…

梯度下降算法、随机梯度下降算法、动量随机梯度下降算法、AdaGrad算法、RMSProp算法、Adam算法详细介绍及其原理详解

相关文章 梯度下降算法、随机梯度下降算法、动量随机梯度下降算法、AdaGrad算法、RMSProp算法、Adam算法详细介绍及其原理详解反向传播算法和计算图详细介绍及其原理详解 文章目录相关文章前言一、回归拟合问题二、损失函数三、梯度下降算法四、随机梯度下降算法五、动量随机梯…

国际山岳日,周大福百年承诺续写永恒美好

纵横古今&#xff0c;俯瞰万里 每一寸绿野都孕育万物生机 每一座山林都彰示生命之本 百周年承诺 守护自然生态 周大福珠宝集团坚守“用真诚让幸福永恒“的企业理念 我们的百周年承诺包括对地球真诚且有效的付出服务 致力守护珍贵的大自然环境&#xff0c;为人类和星球幸福…

吉林优美姿文化:抖音怎么做爆款输出?

要知道&#xff0c;现在自媒体发展的越来越好了&#xff0c;其中发展的最好的就是抖音平台&#xff0c;大家如果要利用抖音平台达到引流的目的的话&#xff0c;也要去学习一下抖音相关的技巧&#xff0c;那么抖音怎么去买号呢&#xff1f;跟着吉林优美姿小编来一起看看吧&#…

亚马逊---人工智能入门---学习笔记

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​ &#x1f4e3;系列专栏&#xff1a;蓝桥杯算法笔记 &#x1f4ac;总结&#xff1a;希望你看完之…

SpringBoot 的配置

目录 配置文件到底有什么作用呢 ? SpringBoot的配置文件的格式有哪些呢? properties配置文件 yml配置文件 properties乱码问题 多平台的配置文件设置 配置文件到底有什么作用呢 ? 配置文件主要是配置项目的一些重要的数据.. 比如配置数据库的连接信息 数据库是非常重…

虚拟机中如何安装Liunx环境

安装步骤 首先 准备一个Linux系统镜像 这是下载地址&#xff1a;https://cn.ubuntu.com/download/server/step1 然后打开虚拟机软件&#xff0c;点击新建 配置虚拟机名称 配置内存【建议4GB&#xff0c;内存小就少弄一顿】【再点击下一步】 硬盘配置 点击下一步 到这一步&am…

MVP、原型、概念验证,傻傻分不清楚?

MVP、原型以及概念验证这三者的概念虽然没有密切的联系&#xff0c;但也有不少人会分不清这三者的区别&#xff0c;在这篇文章中&#xff0c;我们会帮大家区分一下这三个概念。 首先是MVP&#xff0c;MVP是Minimum Viable Product的缩写&#xff0c;即最小可行性产品。MVP通过…

计算机网络---DHCP和自动配置

什么是DHCP HCP&#xff08;动态主机配置协议&#xff09;是一个局域网的网络协议&#xff0c;客户机 / 服务器协议。指的是由服务器控制一段IP地址范围&#xff0c;客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码。默认情况下&#xff0c;DHCP作为Windows Se…

在SPDK中体验一下E810网卡ADQ直通车

早在2019年&#xff0c;Intel发布第二代Xeon Scalable系列处理器的同时&#xff0c;也推出了E800系列网卡。该网卡的亮点除了支持100Gb&#xff0c;便是新增了ADQ功能。1. 了解ADQADQ 全称Application Device Queues&#xff0c;是一种队列和控制技术&#xff0c;可提高应用程序…