【产品名称】
Retrofit
【产品介绍】
Retrofit是目前比较流行的Android 网络请求框架Retrofit主页,他是基于OkHttp进行了进一步封装,真正进行网络请求工作的是OKHttp,但是Retrofit 利用动态代理等处理,让开发者使用一些注解、很简单的代码就实现网络请求。(用了都说好,哈哈😄),关于Retrofit的是如何工作的可以看看我上一篇写的源码面前无秘密之——Retrofit 源码分析,本文主要探讨Retrofit的使用。
为了方便介绍Retrofit 使用Spring boot 搭建了一个简单的api,方便下面介绍Retrofit使用
【产品成分】
就那么多代码 源代码
【产品使用】
上面都不重点,正文开始,上车🚲走起
一、准备工作,引入依赖
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
二、写点代码,给我来个(Retrofit)对象
1.根据api 定义对应接口
interface ApiService {
/**
* GET 请求方式 路由 friend_list
* 请求参数是 page
* 相应数据是json格式,根据json数据格式来定义方法返回类型
*返回值是Call包裹的,可以调用execute或enqueue发起网络请求
*/
@GET("friend_list")
fun getFriends(@Query("page") page:Int=1): Call<Result<List<User>>>
}
2. 创建retrofit 实例
val retrofit = Retrofit.Builder()
//api的baseUrl
.baseUrl("http:192.168.31.7:8080/")
// ① 添加一个Gson转换器,用于把请求到的json数据转换成对应的java对象
.addConverterFactory(GsonConverterFactory.create())
.build()
//使用create方法创建ApiService 接口实例
val apiService = retrofit.create(ApiService::class.java)
**说明:**在①处需要添加一个依赖 implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
网络请求一般返回的是Json数据,使用GsonConverterFactory 它可以帮我们自动把请求到的json数据转换为对应的java对象
三、发起一个网络请求
apiService.getFriends().enqueue(object : Callback<Result<List<User>>> {
override fun onFailure(call: Call<Result<List<User>>>, t: Throwable) {
Log.i("MainActivity","${t.message}")
}
override fun onResponse(call: Call<Result<List<User>>>, response: Response<Result<List<User>>>) {
if (response.isSuccessful) {
Log.i("MainActivity","success")
Log.i("MainActivity",response.body().toString())
}else{
Log.i("MainActivity","http code=${response.code()},message=${response.message()}")
}
}
})
解释说明: getFriends()
方法返回值是Call对象,调用enqueue 发起异步请求,enqueue参数是CallBack接口,它有两个方法回调 网络请求失败、异常走onFailure
,网络请求有响应走onResponse
方法。onFailure
和onResponse
方法 回调都是在主线程调用的,不需要我们切回主线程。 一般我们说的网络请求成功了都是HTTP code==200
的时候, onResponse
并不是只有code==200
才走的,它只是说明请求有相应,具体是否是我们想要的成功还需使用 response.isSuccessful
方法判断,这个方法认为HTTP code属于[200,300)区间内都是成功的。
Retofit要求java8+和android 21+的支持,所以需要在模块的build.gradle文件的 android{ } 配置如下
android{
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions{
jvmTarget="1.8"
}
}
如果没有上面配置 编译时就会出现这个错误提示
java.util.concurrent.ExecutionException: com.android.tools.r8.a: Invoke-customs are only supported starting with Android O (–min-api 26)
四、如何根据api 定义我们的接口方法
1. GET请求 提交表单传参 ( @Query)
api : 请求方式 GET …/friend_list?page=1&sort=desc
参数 page 、sort GET请求方法是把参数拼接到URL上的。通过?
连接参数多个参数之间同&
分割
响应结果:
{
"code": 0,
"msg": "success",
"data": [
{
"id": 2,
"email": "abc@qq.com",
"name": "Li",
"sex": 1,
"age": 20,
"avatar": "https://212"
},
...
]
}
Retrofit 方法定义
@GET("friend_list")
fun getFriends(
//通过Query注解定义参数注解的value值就是请求参数名
@Query("page") page: Int = 1,
@Query("sort") sort: String
): Call<Result<List<User>>>
2. POST请求提交表单 (@Field)
api : 请求方法 POST …/login
参数 email、password 这些参数是在请求body中的
响应数据
{
"code": 0,
"msg": "success",
"data": {
"id": 3,
"email": "kk@gmail.com",
"name": "Emily",
"sex": 1,
"age": 20,
"avatar": "https://111"
}
}
Retrofit 方法定义
@FormUrlEncoded
@POST("login")
fun login(
@Field("email") email: String,
@Field("password") password: String
): Call<Result<User?>>
**解释说明: **请求参数使用@Field注解,方法处理需要添加POST注解外,还需要添加 @FormUrlEncoded
注解否则会报错。 添加这个注解就会在请求Headers 添加Content-Type=application/``x-www-form-urlencoded
这样请求表单参数数据就会通过URL编码格式编码 参考链接
3. 动态URL,路由参数 (@Path)
api : 请求方式GET …/user/{id}
参数 id,通过/拼接到请求的url上
响应数据
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"email": "xxx@xx.com",
"name": "xxxx",
"sex": 1,
"age": 20,
"avatar": "https://xxxx"
}
}
Retrofit 方法定义
@GET("user/{id}")
fun getUserInfo(@Path("id") id: Int): Call<Result<User?>>
解释说明: url中可变的部分用{xxx} 表示,请求的时候,使用@Path
的参数值会把{xxx} 替换掉,@Path
的value值要和路由中定义的{xxxx} 大括号中的值相同。例如 如果是调用getUserInfo(2)
那个请求的就是.../user/2
这个地址。
4.提交json数据 (@Body)
api :请求方式POST …/add_book
请求参数 是json数据格式如下
{
"name": "《第一行代码》",
"author": "郭霖",
"desc": "xxxxx",
"cover": "xxxxx"
}
响应数据:
{
"code": 0,
"msg": "success",
"data": {
"name": "《第一行代码》",
"author": "郭霖",
"desc": "xxxxx",
"cover": "xxxxx"
}
}
Retrofit 方法定义
@POST("add_book")
fun addBook(@Body book: Book): Call<Result<Book>>
5.POST 提交带文件的表单,上传文件
api: 请求方式POST …/modify_user_info
请求参 name (字符串)、avatar (文件)
响应结果
{
"code": 0,
"msg": "success",
"data": null
}
Retrofit 方法定义
第一种方式
@Multipart
@POST("modify_user_info")
fun modifyUserInfo(
@Part("name") name: String,
@Part file: MultipartBody.Part
): Call<Result<Nothing?>>
**解释说明:**使用@Multipart
即就是设置Content-Type=``multipart/form-data
表单中可以带文件,在方法参数前加上@Part
注解,普通key-value 要在@Part
注解中声明value的值,MultipartBody.Part
参数则不能填value值。下面说说则么创建MultipartBody.Part
吧
//第一步, 根据文件或字节数据创建RequestBody对象
val requestBody = RequestBody.create(MultipartBody.FORM, imageData)
//添加文件,第一个参数是api要求的参数、第二个参数是要发送文件的文件名称,第三个参数是文件数据对应的RequestBody
val part = MultipartBody.Part.createFormData("avatar", "android_logo.png", requestBody)
第二种方式,直接传入RequestBody
对象
@POST("modify_user_info")
fun modifyUserInfo(@Body body: RequestBody): Call<Result<Nothing?>>
关于RequestBody
创建如下
//与上面一种方式相同 先根据文件或字节数据创建RequestBody对象
val imageDataBody = RequestBody.create(MultipartBody.FORM, imageData)
//构建一个表单body
val multipartBody = MultipartBody.Builder()
//添加普通的key-value 表单数据
.addFormDataPart("name", "kk")
//添加文件,第一个参数是api要求的参数、第二个参数是要发送文件的文件名称,第三个参数是文件数据对应的RequestBody
.addFormDataPart("avatar", "android_logo.png", imageDataBody)
.build()
如果有多个文件就多添加几个addFormDataPart
6.其它的一些注解
1、@Url
不指定@GET、@POST等直接指定路由,而是在方法的参数上用@Url 修饰,通过传入的参数不同,动态改变请求的路由地址。eg.
@GET
fun getData(@Url url:String, @Query("page") page: Int = 1): Call<Result<List<User>>>
我觉得一般可以用于参数和返回值相同的一类 api上。
- @QueryMap
当参数多但又不确定个数,就可以用@XXXMap (@QueryMap
、@FieldMap
、@PartMap
)类注解。eg.
@GET("friend_list")
fun getData(@QueryMap map: Map<String,String>): Call<Result<List<User>>>
@QueryMap、@FieldMap、@PartMap 也是类似
五、ConverterFactory
converterFactory 转换工厂负责将网络返回的数据转化为对应的java数据类型。 Retrofit 默认有一个BuiltInConverters 工厂 将返回的数据转化为okhttp3.ResponseBody
对象。上面的例子中我们构建Retrofit对象时通过addConverterFactory(GsonConverterFactory.create())
方法添加了一个GsonConverterFactory
工厂。如果没有添加这个我们上面接口返回值就要改一改了
@GET("friend_list")
fun getData(@QueryMap map: Map<String,String>): Call<ResponseBody>
Retrofit官方给我们提供了一些转换器工厂,比如xml、Jackson等。有时候我们可以需要Json字符串,不要转换成对应的对象,那么我们可以将返回值写成ResponseBody
然后自己处理流数据,这也是一种办法,但太麻烦了,这时候就可以用ScalarsConverterFactory
转换工厂(需要导入 implementation ``'``com.squareup.retrofit2:converter-scalars``:``(insert latest version)``'``)
它会把请求的数据转换为String。及方法返回值写成Call 即可。
六. kotlin 协程的支持
用法很简单,在方法前添加suspend
,返回结果不需要用Call包裹,直接返回想要的结果,如下
@GET("user/{id}")
suspend fun getUserInfo1(@Path("id") id: Int):Result<User?>
更多细节可以参考我之前写的文章 kotlin 协程在 Android 中的使用——Jetpack 中的协程、Retofit中使用协程及源码分析
【生产企业】
Square
最近去医院拿了点药💊喝,看药品的说明书结构还挺清晰,就仿药品说明方式写这篇文章(就是感觉好玩)。大家敲代码同时,也要注意身体,多多锻炼。身体好才是最重要的。不说了,喝药去了
参考链接
1.表单数据
2.Retrofit主页