文章目录


对Retrofit已经使用了一点时间了,是时候归纳一下各种网络请求的service了。

下面分为 ​​GET​​、​​POST​​、​​DELETE​​还有​​PUT​​的请求,说明​​@Path​​、​​@Query​​、​​@QueryMap​​、​​@Body​​、​​@Field​​的用法。

添加依赖

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

初始化Retrofit

val retrofit = Retrofit.Builder()
.baseUrl("http://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()

GET

一个简单的​​get请求​​:​​http://api.github.com/News​

 @GET("News")
Call<NewsBean> getItem();

@Path

interface ApiService {

@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<List<Repo>>

}

创建 ApiService 实例

val service: ApiService = retrofit.create(ApiService::class.java)
service.listRepos("zyj1609wz").execute()

请求url

https://api.github.com/users/zyj1609wz/repos

抓包如下:

Retrofit 注解参数详解_上传文件

@Query

​Query​​参数在​​URL问号之后​

interface ApiService {

@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String, @Query("sort") sort: String): Call<List<Repo>>
}

抓包如下:

Retrofit 注解参数详解_okhttp上传图片_02

@QueryMap

多个参数在URL问号之后,且个数不确定

interface ApiService {

@GET("users/repos")
fun listRepos(@QueryMap map: Map<String, String>): Call<List<Repo>>

}

POST

@Body

body 传字符串

 @POST("users/repos")
fun listRepos(@Body name: String): Call<List<Repo>

Retrofit 注解参数详解_retrofit_03

​body 传对象, retrofit 会自动把对象解析成 json 字符串​

@POST("users/repos")
fun listRepos(@Body user: User): Call<List<Repo>>

抓包看结果

Retrofit 注解参数详解_okhttp_04

form表单1:@FormUrlEncoded 、@Field

​Form 表单​​提交数据, 数据也是放在 body 里面,通常 ​​Form 表单​​和 ​​@Field​​ 注解联合使用。

  • Content-Type:application/x-www-form-urlencoded

使用 ​​form​​表单提交数据,首先要在接口方法上添加 ​​@FormUrlEncoded​​ 注解。参数使用 ​​@Field​​ 、 ​​@FieldMap​

@POST("users/repos")
@FormUrlEncoded
fun listRepos(@Field("name") name: String): Call<List<Repo>>

抓包来看结果:

body 数据如下:​​name=zhaoyanjun​

Retrofit 注解参数详解_okhttp上传图片_05

多个 ​​@Field​​ 字段

@POST("users/repos")
@FormUrlEncoded
fun listRepos(@Field("name") name: String, @Field("age") age: Int): Call<List<Repo>>

抓包看结果:

body 数据格式如下:​​name=zhaoyanjun&age=20​

Retrofit 注解参数详解_上传文件_06

多个参数除了用多个 ​​@Field​​ ,还可以用 ​​@FieldMap​

@POST("users/repos")
@FormUrlEncoded
fun listRepos(@FieldMap name: Map<String, String>): Call<List<Repo>>

form表单2:FormBody

出了上面的 ​​@FormUrlEncoded​​ 我们还可以使用 ​​FormBody​​ 的方式提交。

首先定义接口:

@POST("users/repos")
fun listRepos(@Body body: RequestBody): Call<List<Repo>>

构建 ​​FormBody​

val body = FormBody.Builder()
.add("name", "zhaoyanjun")
.add("age", "20")
.build()

service.listRepos(body).execute()

结果如下:

Retrofit 注解参数详解_retrofit_07

可以看到 ​​Content-Type: application/x-www-form-urlencoded​

body 参数是:​​name=zhaoyanjun&age=20​

完美实现 form 表单提交数据。

@Multipart

其实无论什么库,只要是发送 Http 请求,都得遵守 Http 协议,所以熟悉协议内容对理解库原理、调试是有很大帮助的。

​Http​​ 上传协议为 ​​MultiPart​​。下面是通过抓包获取的一次多文件+文本的上传消息,每行前面的行数是为了标注说明方便加上的,实际请求中没有。

上传单个文件

@POST("find")
@Multipart
fun listRepos(@Part part: MultipartBody.Part): Call<List<Repo>>

使用:

val file = File(externalCacheDir?.absolutePath + File.separator + "123.txt")
val requestFile = file.asRequestBody()
//创建 Part
val filePart = MultipartBody.Part.createFormData("file", file.name, requestFile)

//发起请求
service.listRepos(filePart).execute()

抓包看结果:

  • Content-Type: ​​multipart/form-data; boundary=fa722b45-62ed-4481-a9fa-8b5812686d0c​

Retrofit 注解参数详解_retrofit_08

上传多个文件【方式一】

第一种方式, 写多个 MultipartBody.Part ,比如:

@POST("find")
@Multipart
fun listRepos(@Part part: MultipartBody.Part, @Part part2: MultipartBody.Part): Call<List<Repo>>

使用:

//创建第一个 Part
val file = File(externalCacheDir?.absolutePath + File.separator + "123.txt")
val requestFile = file.asRequestBody()
val filePart = MultipartBody.Part.createFormData("file", file.name, requestFile)

//创建第二个 Part
val file2 = File(externalCacheDir?.absolutePath + File.separator + "456.txt")
val requestFile2 = file2.asRequestBody()
val filePart2 = MultipartBody.Part.createFormData("file2", file2.name, requestFile2)

//执行
service.listRepos(filePart, filePart2).execute()

抓包看结果:

Retrofit 注解参数详解_retrofit_09

上传多个文件【方式二】

@POST("find")
fun listRepos(@Body body: MultipartBody): Call<List<Repo>>

使用

//创建第一个 Part
val file = File(externalCacheDir?.absolutePath + File.separator + "123.txt")
val requestFile = file.asRequestBody()
val filePart = MultipartBody.Part.createFormData("file", file.name, requestFile)

//创建第二个 Part
val file2 = File(externalCacheDir?.absolutePath + File.separator + "456.txt")
val requestFile2 = file2.asRequestBody()
val filePart2 = MultipartBody.Part.createFormData("file2", file2.name, requestFile2)

//构建 body
val body = MultipartBody.Builder()
.setType(MultipartBody.FORM)

// .addFormDataPart("file", file.name, requestFile)
.addPart(filePart)

// .addFormDataPart("file2", file2.name, requestFile2)
.addPart(filePart2)

.build()

//发起请求
service.listRepos(body).execute()

复核参数

构建普通参数的 ​​Part​

val part = MultipartBody.Part.createFormData("age", "20")

//或者
val body = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("age", "20")
.build()

@Header

添加 Header 方式一

@POST("find")
fun listRepos(@Header("Accept-Encoding") encoding: String): Call<List<Repo>>

抓包看结果

Retrofit 注解参数详解_okhttp_10

添加多个 ​​Header​​ , 使用 ​​@HeaderMap​

@POST("find")
fun listRepos(@HeaderMap headers: Map<String, String>): Call<List<Repo>>

添加 Header 方式二

单个参数

@POST("find")
@Headers("token:zhaoyanjun")
fun listRepos(): Call<List<Repo>>

多个参数

@Headers("name:zhaoyanjun", "age:20")
@POST("find")
fun listRepos(): Call<List<Repo>>

添加 Header 方式三:拦截器

private val client = OkHttpClient.Builder()
.addInterceptor(HeaderInterceptor())
.build()

private val retrofit = Retrofit.Builder()
.baseUrl("http://10.8.67.211:8080")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()

class HeaderInterceptor : Interceptor {

override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val oldRequest = chain.request()
val builder = oldRequest.newBuilder()
builder.addHeader("name", "zhaoyanjun")
builder.addHeader("age", "20")
val request = builder.build()
return chain.proceed(request)
}
}

添加 Header 方式四:Request

val client = OkHttpClient.Builder().build()

val request = Request.Builder()
.url("http://www..")
.addHeader("name", "zhaoyanjun")
.addHeader("age", "20")
.build()

client.newCall(request).execute()