Retrofit 是一个用于在 Android 应用程序中进行网络请求的开源库。它是由 Square 公司开发的,提供了一种方便的方式来处理 HTTP 请求和响应。Retrofit 可以帮助开发者将 HTTP 请求映射到 Java 接口,并将服务器的响应映射到 Java 对象,从而简化了网络通信的过程。本质还是OkHttp。
RequestBody:
RequestBody
是 Retrofit 中的类,用于表示请求体的内容。它通常用于传输文本数据或二进制数据。- 您可以使用
RequestBody.create()
方法来创建RequestBody
对象,以便将文本、JSON、XML 等数据添加到请求体中。- 通常,
RequestBody
用于发送普通的文本数据或 JSON 数据,例如提交表单数据或发送请求体为 JSON 格式的数据。//用于发送一段文本内容 RequestBody textRequestBody = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"),"Hello 3G"); //用于发送一段json数据 RequestBody JsonRequestBody = RequestBody.create(MediaType.parse("application/json"),"{\"key\":\"value\"}");
常用内容类型(MediaType):
- “text/html”:用于传输包含 HTML 标记的文本数据,通常在浏览器中显示网页内容。
- “application/json”:用于传输 JSON 格式的文本数据,通常用于 API 请求和响应,表示数据的结构化表示。
- “application/xml”:用于传输 XML 格式的文本数据,通常用于数据交换和配置文件。
- “text/css”:用于传输层叠样式表(CSS)的文本数据,通常用于定义网页的样式。
- “text/plain”:用于传输纯文本数据,没有特定的格式或编码。
- “application/octet-stream”: 表示二进制数据,通常用于文件上传。
charset=utf-8:表示编码格式
MultipartBody.Part:
MultipartBody.Part
是 Retrofit 中的类,用于表示多部分数据(multipart data)的一部分。它通常用于上传文件或二进制数据。MultipartBody.Part
通常包含一个数据部分和一个请求头部分,用于描述要上传的文件或二进制数据。- 您可以使用
MultipartBody.Part.createFormData()
或MultipartBody.Part.create()
方法来创建MultipartBody.Part
对象,以便将文件或二进制数据添加到请求体中。File file = new File("数据.txt"); //要上传的文件的内容 RequestBody fileRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file); //将文件转化为二进制数据 MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "example.txt",fileRequestBody); //包装文件 //这种创建方法只有内容,没有文件名和字段名 MultipartBody.Part part = MultipartBody.Part.create(fileRequestBody);
"file"
: 这是请求中的字段名,它表示要上传的文件字段的名称。在服务器端,您可以使用这个字段名来识别和处理上传的文件。"example.txt"
: 这是上传的文件的文件名,它表示要上传的文件的名称。这是可选的,但通常用于标识文件的名称。服务器可能会使用该名称来保存上传的文件。"fileRequestBody"
:这是上传的文件
请求方法注解 | 说明 |
---|---|
@GET | GET 请求,通常用于获取(或读取)服务器上的资源 |
@POST | POST 请求,通常用于向服务器提交数据 |
@PUT | PUT 请求,PUT 请求通常用于更新或替换服务器上的资源,请求体包含要更新的数据,通常用于整体替换而不是部分修改。 |
@DELETE | DELETE 请求,DELETE 请求通常用于删除服务器上的资源 |
@PATCH | PATCH 请求,PATCH 请求类似于 PUT 请求,但通常用于部分更新资源,而不是整体替换。请求体包含要应用的部分更改。 |
@HEAD | HEAD 请求,HEAD 请求与 GET 请求类似,但服务器不会返回响应主体,**只返回响应头信息。**它通常用于检查是否修改过或资源是否存在。 |
@OPTIONS | OPTIONS 请求,OPTIONS 请求用于获取关于资源的通信选项 |
@HTTP | 可替代以上所有注解,具有三个属性:method、path、hasBody |
@PUT的用法:
@Path("id") int resourceId
: 这是一个路径参数,它表示要更新的资源的唯一标识符。在请求 URL 中的{id}
将被替换为提供的resourceId
。@Body RequestBody requestBody
: 这是请求体,它包含要发送到服务器的数据。
public interface ApiService {
@PUT("resource/{id}")
Call<ResponseBody> updateResource(@Path("id") int resourceId, @Body RequestBody requestBody);
}
@DELETE:
public interface ApiService {
@DELETE("resource/{id}")
Call<ResponseBody> deleteResource(@Path("id") int resourceId);
}
@PATCH:
public interface ApiService {
@PATCH("resource/{id}")
Call<ResponseBody> patchResource(@Path("id") int resourceId, @Body RequestBody requestBody);
}
@HTTP的用法:
method 属性:method 属性用于指定请求方法,即您要执行的 HTTP 请求类型。这可以是标准的 HTTP 方法,如 “GET”、“POST”、“PUT”、“DELETE” 等,也可以是自定义的方法名。
path 属性:path 属性用于指定请求的路径或终端端点。
hasBody 属性:hasBody 属性是一个布尔值,**用于指示请求是否包含请求体。**通常,对于 GET 请求,请求体为空,因此 hasBody 属性通常设置为 false。对于 POST、PUT 等请求,请求体包含数据,因此 hasBody 属性通常设置为 true。
例如:
@HTTP(method = "SEARCH", path = "search", hasBody = true) Call<SearchResult> search(@Body SearchRequest searchRequest);
请求头注解 | 说明 |
---|---|
@Headers | 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在 |
@Header | 作为方法的参数传入,用于添加不固定的header,它会更新已有请求头 |
请求头用于说明是谁或什么在发送请求、请求源于何处,或者客户端的喜好及能力。
例如:
使用
Headers
添加固定的请求头@Headers({ "Authorization: Bearer Token123", "User-Agent: My-App" }) @GET("user/profile") Call<UserProfile> getUserProfile();
使用
Header
动态添加请求头@GET("user/profile") Call<UserProfile> getUserProfile(@Header("Authorization") String authToken);
标记类注解 | 说明 |
---|---|
@FromUrlCoded | 用于表示请求发送编码表单数据,通常与 @Field 注解一起使用,每个键值对需要使用 @Field 注解来注解。 |
@Multipart | 用于表示请求发送 multipart/form-data 格式的数据,通常在需要文件上传的场景中使用。每个键值对需要使用 @Part 注解来注解键名,随后的对象需要提供给 @Part 注解。 |
@Streaming | 用于表示希望以字节流的形式返回响应。如果不使用该注解,默认情况下 Retrofit 会将数据全部加载到内存中。用于处理大文件下载。 |
请求参数注解 | 说明 |
---|---|
@Body | 用于使用Post请求发送数据,将实例对象转化为Json数据。 |
@Filed | 用于发送表单,需要结合@FormUrlEncoded使用 |
@FiledMap | 用于发送表单,需要结合@FormUrlEncoded使用,允许您动态构建键值对的 Map ,对于需要发送大量表单字段或需要动态构建表单数据的情况非常有用。 |
@Part | 用于上传文件或二进制数据,例如图像、音频文件等,需要结合@Multipart使用 |
@PartMap | @PartMap 注解通常用于上传多个文件,需要结合@Multipart使用 |
@Path | 用于Url中的占位符 |
@Query | 用于Get请求中的参数 |
@QueryMap | 与Query类似,用于不确定表单参数 |
@Url | 指定请求路径 |
@Filed
的基本使用方法:@FormUrlEncoded
@POST("submit-form")
Call<ResponseBody> submitForm(@Field("name") String name, @Field("email") String email);
@FiledMap
的基本使用方法:@FormUrlEncoded
@POST("submit-form")
Call<ResponseBody> submitForm(@FieldMap Map<String, String> formData);
@Filed和FiledMap的区别:
- 使用方式:
@Field
注解用于单独指定每个表单字段的名称和值,每个字段都需要在方法参数中单独列出。这适用于已知要发送的字段,且字段数量较少的情况。@FieldMap
注解允许将多个表单字段的键值对表示为一个Map
,并将整个Map
作为参数传递给方法。这对于需要发送大量表单字段或需要动态构建表单数据的情况非常有用。- 动态性:
@Field
注解的字段名称和值通常是硬编码的,即在代码中显式指定。如果需要在每次请求中更改字段名称或值,需要修改方法参数和注解。@FieldMap
注解允许动态构建键值对的Map
,因此可以根据需要添加、删除或修改字段。
@Part
的使用方法:@Multipart
@POST("upload-mixed")
Call<ResponseBody> uploadMixedData(
@Part MultipartBody.Part file, //上传整个文件
@Part("description") RequestBody description //上传文本数据
);
@PartMap
的使用方法:@Multipart
@POST("upload-multiple")
Call<ResponseBody> uploadMultipleFiles(@PartMap Map<String, MultipartBody.Part> files);
@Path
的用法:@GET("user/{userId}/orders/{orderId}")
Call<Order> getOrderDetails(@Path("userId") String userId, @Path("orderId") String orderId);
@Query
的用法: @GET("https://devapi.qweather.com/v7/weather/3d")
Call<WeatherData> getWeatherDataById(@Query("location") String location, @Query("key") String key);
QueryMap
的用法: @GET("v7/weather/3d")
Call<WeatherData> get3DayWeather(@QueryMap Map<String, String> queryParams);
Url
的用法: @GET
Call<ResponseBody> fetchData(@Url String url);
<!-- 添加网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.10.1'
简化数据类的定义:
implementation 'org.projectlombok:lombok:1.18.30'
可以在Gradle/Maven仓库查询所有的依赖:
Gradle仓库
Maven
使用GsonFromatPlus插件,一键解析JSON数据格式
@lombok.NoArgsConstructor
@lombok.Data
public class WeatherData {
@SerializedName("code")
private String code;
@SerializedName("updateTime")
private String updateTime;
@SerializedName("fxLink")
private String fxLink;
@SerializedName("daily")
private List<DailyDTO> daily;
@SerializedName("refer")
private ReferDTO refer;
@lombok.NoArgsConstructor
@lombok.Data
public static class ReferDTO {
@SerializedName("sources")
private List<String> sources;
@SerializedName("license")
private List<String> license;
}
@lombok.NoArgsConstructor
@lombok.Data
public static class DailyDTO {
@SerializedName("fxDate")
private String fxDate;
@SerializedName("sunrise")
private String sunrise;
@SerializedName("sunset")
private String sunset;
@SerializedName("moonrise")
private String moonrise;
@SerializedName("moonset")
private String moonset;
@SerializedName("moonPhase")
private String moonPhase;
@SerializedName("moonPhaseIcon")
private String moonPhaseIcon;
@SerializedName("tempMax")
private String tempMax;
@SerializedName("tempMin")
private String tempMin;
@SerializedName("iconDay")
private String iconDay;
@SerializedName("textDay")
private String textDay;
@SerializedName("iconNight")
private String iconNight;
@SerializedName("textNight")
private String textNight;
@SerializedName("wind360Day")
private String wind360Day;
@SerializedName("windDirDay")
private String windDirDay;
@SerializedName("windScaleDay")
private String windScaleDay;
@SerializedName("windSpeedDay")
private String windSpeedDay;
@SerializedName("wind360Night")
private String wind360Night;
@SerializedName("windDirNight")
private String windDirNight;
@SerializedName("windScaleNight")
private String windScaleNight;
@SerializedName("windSpeedNight")
private String windSpeedNight;
@SerializedName("humidity")
private String humidity;
@SerializedName("precip")
private String precip;
@SerializedName("pressure")
private String pressure;
@SerializedName("vis")
private String vis;
@SerializedName("cloud")
private String cloud;
@SerializedName("uvIndex")
private String uvIndex;
}
@Override
public String toString() {
return "WeatherData{" +
"code='" + code + '\'' +
", updateTime='" + updateTime + '\'' +
", fxLink='" + fxLink + '\'' +
", daily=" + daily +
", refer=" + refer +
'}';
}
}
命名方式:以功能开头,以Service结尾。
public interface WeatherService {
/**
* @GET("posts/{Key}") : @Get是Retrofit的一个注解,表示HTTP的GET请求,
* 这个请求内容是动态的,使用{Id}来表示,是实际请求中{Id}被替换为具体数据。
*
* Call<*> : *是一个自定义的数据类型,get方法用于获取特定Id的文章
* @Query("location") : 使用Retrofit注解‘@Query’用来指定动态的URL内容参数。
*/
//完整的请求地址:https://devapi.qweather.com/v7/weather/3d?location=101010100&key=64f323b501dc410cb7ec4fd1b503aab4
@GET("https://devapi.qweather.com/v7/weather/3d")
Call<WeatherData> getWeatherDataById(@Query("location") String location, @Query("key") String key);
/**
* 也可以写为:
* @GET("/v7/weather/3d") 这样的格式
* 这里可以省略是因为在构建retrofit时有指定头Url
*/
/**
* @POST("Posts") : @Post是Retrofit的一个注解,HTTP POST 请求,用于创建新的文章。请求的 URL 是 "posts"。
*
* Call<*> 该方法返回一个 Call 对象,用于执行网络请求,并在请求成功或失败时提供响应。
* @Body WeatherData weatherData : 使用了 Retrofit 注解 @Body,
* 表示方法参数WeatherData weatherData将被转换为请求的请求体.
* 将数据发送到Posts地址处
*/
@POST("Posts")
Call<WeatherData> createPost(@Body WeatherData weatherData);
@Multipart
@POST("upload-mixed")
Call<ResponseBody> uploadMixedData(
@Part MultipartBody.Part file,
@Part("description") RequestBody description
);
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://devapi.qweather.com")
.addConverterFactory(GsonConverterFactory.create()) //转化器
.build();
//实例化API接口
WeatherService apiService = retrofit.create(WeatherService.class);
Call<WeatherData> call = apiService.getWeatherDataById("101010100", "64f323b501dc410cb7ec4fd1b503aab4");
String URl = call.request().url().toString();
call.enqueue(new Callback<WeatherData>() {
@Override
public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {
if (response.isSuccessful()) {
WeatherData data = response.body();
Log.d("TAG", "onResponse: " + data.toString());
Log.d("TAG", "onResponse: " + URl);
}
}
@Override
public void onFailure(Call<WeatherData> call, Throwable t) {
Log.d("TAG", "onResponse: " + "data.toString()");
Log.d("TAG", "onResponse: " + URl);
}
});
//用于发送一段文本内容
RequestBody textRequestBody = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"),"Hello 3G");
//用于发送一段json数据
RequestBody JsonRequestBody = RequestBody.create(MediaType.parse("application/json"),"{\"key\":\"value\"}");
File file = new File("数据.txt"); //要上传的文件的内容
RequestBody fileRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file); //将文件转化为二进制数据
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "example.txt",fileRequestBody); //包装文件
MultipartBody.Part part = MultipartBody.Part.create(fileRequestBody);
}
}
其中使用了Retrofit.Builder()
构建一个Retrofit对象,baseUrl
用于指定了一个Retorfit请求的根路径,addConverterFactory
方法用于指定Retorfit进行解析数据的转化库。这两个方法是必须调用的。