Retrofit
1.Retrofit注解分类
1.1HTTP请求方法
GET |
---|
POST |
PUT |
DELETE |
HEAD |
PATCH |
OPTIONS |
HTTP |
1.2标记类注解
FormUrlEncoded |
---|
Multipart |
Streaming |
Streaming代表响应的数据以流的形式返回,如果不使用它,则默认会把全部数据加载到内存,所以下载大文件时需要加上这个注解.
1.3参数类注解
Header |
---|
Headers |
Body |
Path |
Field |
FieldMap |
Part |
PartMap |
Query |
QueryMap |
2.GET请求访问网络
2.1无参的get请求
public interface BaiduApi {
@GET("/")
Call<ResponseBody> getBaiduHtml();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.baidu.com/")
.build();
BaiduApi baiduApi = retrofit.create(BaiduApi.class);
baiduApi.getBaiduHtml().enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
if(response.isSuccessful()) {
Log.d("tag", response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("tag","失败");
}
});
最开始我的Call<>里面写的是String,最后给我报了一个
com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
错误,chatgpt里面说由Gson库抛出的,通常表示输入的JSON数据格式不正确,
给出的解释是:
当你使用
Call<String>
时,Retrofit 需要将响应体的字节流转换为字符串,以便于你使用。但是,当响应体的内容是 HTML 时,有可能出现编码问题或者特殊字符,导致转换失败。相比之下,使用
ResponseBody
可以让你直接操作响应体的字节流,而不需要进行转换。这样可以保证获取到原始的响应体数据,而不会因为转换失败而出现问题。当你需要获取响应体的字节流时,或者无法确定响应体的编码格式时,建议使用
ResponseBody
。而当你明确知道响应体的编码格式并且需要直接使用响应体内容时,可以考虑使用Call<String>
。
所以一般还是用ResponseBody最保险
注意:baseUrl里的参数必须以 / 结尾,否则会抛出异常 IllegalArgumentException
最后**Log.d(“tag”,response.body().string);**打印出来的东西为:
百度一下,你就知道
2.2动态配置URL地址@Path
public interface WithParamsBaiduApi {
@GET("{path}")
Call<ResponseBody>getCall(@Path("path")String path);
}
Retrofit retrofit1 = new Retrofit.Builder().baseUrl("https://so.youkuaiyun.com/so/search/")
.build();
WithParamsBaiduApi baiduApi1 = retrofit1.create(WithParamsBaiduApi.class);
baiduApi1.getCall("?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=").enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.isSuccessful()){
try {
Log.d("tag","yes"+" "+response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("tag","isFail");
}
});
baseUrl里面的url为https://so.youkuaiyun.com/so/search/但是,我的
@GET(“{path}”)
CallgetCall(@Path(“path”)String path);
表明了getCall需要参数,我的**getCall()**里面传入了
?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=
所以最后GET的为
https://so.youkuaiyun.com/so/search/?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=
最后Log.d(“tag”,response.body().string());
打印出来的为:
2.3动态指定查询条件:@Query()
public interface Query {
@GET("ip")
Call<ResponseBody>getbody(@retrofit2.http.Query("ip")String ip);
}
Retrofit retrofit2 = new Retrofit.Builder().baseUrl("https://so.youkuaiyun.com/so/search/").build();
Query query = retrofit2.create(Query.class);
query.getbody("q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=").enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.isSuccessful()) {
try {
Log.d("tag", response.body().string());
} catch (IOException e) {
e.printStackTrace();
Log.d("tag","io");
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("tag","失败");
}
});
Log.d(“tag”,response.body.string());
相当于我请求:
https://so.youkuaiyun.com/so/search/ip?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=
打印出来的是:
2.4动态配置查询条件组:@QueryMap
public interface QueryMap {
@GET("ip")
Call<ResponseBody>getCall(@retrofit2.http.QueryMap Map<String,String>map);
}
Retrofit retrofit3 = new Retrofit.Builder().baseUrl("https://so.youkuaiyun.com/so/search/").build();
Map<String,String>map = new HashMap<>();
map.put("q","%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=");
map.put("urw","");
QueryMap queryMap = retrofit3.create(QueryMap.class);
queryMap.getCall(map).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.isSuccessful()){
try {
Log.d("tag",response.body().string());
} catch (IOException e) {
e.printStackTrace();
Log.d("tag","net");
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("tag","失败");
// "1"
}
});
Log.d(“tag”,response.body().string());
相当于查询的是
https://so.youkuaiyun.com/so/search/ip?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=
出来是
3.POST请求访问网络
3.1传输数据类型为键值对: @Field
public interface postField {
@FormUrlEncoded
@POST("/")
Call<WeatherBeans> requestWeatherBeans(
@Field("app") String app,
@Field("weaid") String weaid,
@Field("appkey") String appkey,
@Field("sign") String sign,
@Field("format") String format);
}
public class WeatherBeans implements Serializable {
@SerializedName("success")
public String success;
@SerializedName("result")
public List<ResultDTO> result;
public static WeatherBeans objectFromData(String str) {
return new Gson().fromJson(str, WeatherBeans.class);
}
}
public class ResultDTO implements Serializable {
@SerializedName("weaid")
public String weaid;
@SerializedName("days")
public String days;
@SerializedName("week")
public String week;
@SerializedName("cityno")
public String cityno;
@SerializedName("citynm")
public String citynm;
@SerializedName("cityid")
public String cityid;
@SerializedName("temperature")
public String temperature;
@SerializedName("humidity")
public String humidity;
@SerializedName("weather")
public String weather;
@SerializedName("weather_icon")
public String weatherIcon;
@SerializedName("weather_icon1")
public String weatherIcon1;
@SerializedName("wind")
public String wind;
@SerializedName("winp")
public String winp;
@SerializedName("temp_high")
public String tempHigh;
@SerializedName("temp_low")
public String tempLow;
@SerializedName("humi_high")
public String humiHigh;
@SerializedName("humi_low")
public String humiLow;
@SerializedName("weatid")
public String weatid;
@SerializedName("weatid1")
public String weatid1;
@SerializedName("windid")
public String windid;
@SerializedName("winpid")
public String winpid;
@SerializedName("weather_iconid")
public String weatherIconid;
@SerializedName("weather_iconid1")
public String weatherIconid1;
public static ResultDTO objectFromData(String str) {
return new Gson().fromJson(str, ResultDTO.class);
}
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.k780.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
postField netService = retrofit.create(postField.class);
Call<WeatherBeans> call = netService.requestWeatherBeans("weather.future","1","10003","b59bc3ef6191eb9f747dd4e83c99f2a4","json");
call.enqueue(new Callback<WeatherBeans>() {
@Override
public void onResponse(Call<WeatherBeans> call, Response<WeatherBeans> response) {
if(response.isSuccessful()){
WeatherBeans beans = response.body();
for (int i = 0;i<beans.result.size();i++) {
Log.d("tag2", beans.result.get(i).cityid);
}
}
Log.d("tag2",response.body().success);
}
@Override
public void onFailure(Call<WeatherBeans> call, Throwable t) {
Log.d("tag2","失败"+t);
}
});
最后Log.d()打印出来的是:
D/tag2: 101010100
D/tag2: 101010100
D/tag2: 1
3.1.1POST与GET的注解的不同
3.1.1.1GET注解
弄GET注解的时候,
例如这个:
public interface Query {
@GET("ip")
Call<ResponseBody>getbody(@retrofit2.http.Query("ip1")String ip);
}
Retrofit retrofit2 = new Retrofit.Builder().baseUrl("https://so.youkuaiyun.com/so/search/").build();
Query query = retrofit2.create(Query.class);
query.getbody("q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=").enqueue(new Callback<ResponseBody>()
我最后的url是:https://so.youkuaiyun.com/so/search/ip1?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=
这是因为在Query接口中定义的是@GET(“ip1”),所以最后的URL中的路径部分是"ip1",而不是"ip"。同时,在调用getbody方法时,传入的参数是一个字符串,而该参数实际上是用作查询参数的值,会被自动拼接到URL中。因此,最后的URL就是"https://so.youkuaiyun.com/so/search/ip1?q=%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AEURL%E5%9C%B0%E5%9D%80%40Path%20retrofit&t=&u=&urw=“。](https://so.youkuaiyun.com/so/search/ip1?q=动态配置URL地址@Path retrofit&t=&u=&urw=”,因为在Query接口中定义的是@GET(“ip1”),所以最后的URL中的路径部分是"ip1",而不是"ip"。同时,在调用getbody方法时,传入的参数是一个字符串,而该参数实际上是用作查询参数的值,会被自动拼接到URL中。因此,最后的URL就是"https://so.youkuaiyun.com/so/search/ip1?q=动态配置URL地址@Path retrofit&t=&u=&urw="。)
3.1.1.2POST注解
而POST注解的时候
例如:
@FormUrlEncoded
@POST("ip")
Call<WeatherBeans> requestWeatherBeans(
@Field("app") String app,
@Field("weaid") String weaid,
@Field("appkey") String appkey,
@Field("sign") String sign,
@Field("format") String format);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.k780.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
postField netService = retrofit.create(postField.class);
Call<WeatherBeans> call = netService.requestWeatherBeans("weather.future","1","10003","b59bc3ef6191eb9f747dd4e83c99f2a4","json");
这里的传入的url是:
http://api.k780.com/ip
如果写成
@FormUrlEncoded
@POST("/")
Call<WeatherBeans> requestWeatherBeans(
@Field("app") String app,
@Field("weaid") String weaid,
@Field("appkey") String appkey,
@Field("sign") String sign,
@Field("format") String format);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.k780.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
postField netService = retrofit.create(postField.class);
Call<WeatherBeans> call = netService.requestWeatherBeans("weather.future","1","10003","b59bc3ef6191eb9f747dd4e83c99f2a4","json");
这里的传入的url是:
http://api.k780.com/
而:
Call<WeatherBeans> call = netService.requestWeatherBeans("weather.future","1","10003","b59bc3ef6191eb9f747dd4e83c99f2a4","json")
这段代码并不是给url传递参数,这是因为在使用 @Field
注解时,Retrofit 会将这些参数转换为 “application/x-www-form-urlencoded” 的格式,然后作为请求体发送到服务器。也就是说,这些参数并不是直接附加到 URL 上,而是作为请求体的一部分发送到服务器。因此,即使在 @POST("/")
的情况下,这些参数也不会直接附加到 URL 上。
如果您将@POST("/")
更改为@GET("/")
并且不使用@FormUrlEncoded
,则@Field
注释将无效。此时,请求参数将被编码到URL的查询部分中,而不是通过请求正文发送。
3.2传输数据类型为JSON字符串:@Body
@Body会将请求参数放到请求体中,所以适用于POST请求
@Body标签不能和@FormUrlEncoded或@Multipart标签同时使用,会报错
public interface body {
@POST("/")
Call<ResponseBody> requestWeatherBeans(
@Body bodytext bodytext);
}
public class bodytext {
private String app;
private String weaid;
private String appkey;
private String sign;
private String format;
public bodytext(String app,String weaid,String appkey,String sign,String format) {
this.app = app;
this.appkey = appkey;
this.format = format;
this.sign = sign;
this.weaid = weaid;
}
}
Retrofit retrofit1 = new Retrofit.Builder().baseUrl("http://api.k780.com/").
addConverterFactory(GsonConverterFactory.create())
.build();
body body = retrofit1.create(com.example.retrofit_0.post.body.class);
bodytext bodytext = new bodytext("weather.future","1","10003","b59bc3ef6191eb9f747dd4e83c99f2a4","json");
Call<ResponseBody>call1 = body.requestWeatherBeans(bodytext);
call1.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.isSuccessful()){
try {
body1.setText(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
最后成功
感觉没啥说的
3.3单个文件上传:@Part
- Multipart注解表示允许多个@Part
- updateUser方法的第一个参数是准备上传的图片文件,使用了MultipartBody.Part
- 第二个参数是RequestBody类型的,它用来传递简单的键值对
public interface UpLoadFileForPart {
@
@POST("user/photo")
Call<User> updateUser(@Part MultipartBody.Part photo , @Part("description")RequestBody description);
}
File file = new File(Environment.getExternalStorageDirectory() , "Ning.png");
//根路径下,file的名字为NING.png
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image.png") ,file);
//最后这一句让Ning是一个image.png类型的
MultipartBody.Part photo = MultipartBody.Part.createFormData("photos" ,"Ning.png" , photoRequestBody);
//这一步是将图片文件转换为一个MultipartBody.Part类型的对象,以便于在Retrofit请求中使用。MultipartBody.Part可以包含一个文件或者表单字段,需要提供一个唯一的key(如“photos”)以及文件名(如“Ning.png”),并使用RequestBody类型的实例来描述文件的内容。在这个例子中,photoRequestBody就是一个RequestBody实例,它包含了文件的二进制内容和MIME类型。然后使用MultipartBody.Part.createFormData()方法将这些信息打包成一个MultipartBody.Part对象,传入updateUser()方法的photo参数中。
UpLoadFileForPart upLoadFileForPart = retrofit.create(UpLoadFileForPart.class);
Call<User> call1 = upLoadFileForPart.updateUser(photo , RequestBody.create(null , "Ning"));
@POST("user/photo")
表示请求的URL为 http://<base_url>/user/photo
,其中 <base_url>
是在 Retrofit
的构造器中通过 baseUrl()
方法设置的基础 URL。
- @Part MultipartBody.Part photo:这是一个二进制文件,用于上传图片或文件。
- @Part(“description”) RequestBody description:这是一个字符串描述,用于描述上传的文件或图片。
MultipartBody.Part是一个封装了二进制数据的类,它包含了数据的Content-Type、文件名等信息,可以通过MultipartBody.Part.createFormData方法来创建。
RequestBody是一个抽象类,它代表了请求体的内容。在这个例子中,我们使用了它的子类,即okhttp3.RequestBody。我们可以使用RequestBody.create方法来创建RequestBody对象,然后将其作为@Part参数的值传递。
3.4多个文件上传:@PartMap
public interface UpLoadFileForPart {
@Multipart
@POST("user/photo")
Call<User> updateUser(@PartMap Map<String,RequestBody>photo , @Part("description")RequestBody description);
}
4.消息报头(Header)
- 在HTTP请求中,为了过滤掉不安全的访问、添加特殊加密的访问或者防止被攻击等,以便减轻服务器的压力和保证请求的安全,通常会在消息报头中携带一些特殊的消息头处理。
- Retrofit提供了@Header来添加消息报头。添加消息报头有静态和动态两种方式
4.1静态
public interface SomeService {
@GET("some/endpoint")
@Headers("Accept-Encoding: application/json")
Call<ResponseBody> getCarType();
}
使用Headers注解来添加消息报头
如果想要添加多个消息报头,则可以使用{}包起来
public interface SomeService {
@GET("some/endpoint")
@Headers({
"Accept-Encoding: application/json"
"User-Agent: MoonRetrofit"
})
Call<ResponseBody> getCarType();
}
4.2动态
使用@Header注解,通过getCarType方法来动态添加。外界传参进来
public interface SomeService {
@GET("some/endpoint")
Call<ResponseBody> getCarType(@Header("Location") String location);
}