前沿
Retrofit是Square公司开发的一款针对Android网络请求的框架,Retrofit底层基于OkHttp实现的.与其他网络框架不同的是,它更多使用运行时注解的方式提供功能.
使用前准备
先配置build.gradle:
//Retrofit联网框架
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
//如果用到gson解析 需要添加下面的依赖
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
//ConverterFactory的String依赖包
implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
上述最后2个依赖包是为了增加支持返回值为Gson/String 类型数据所添加的.如果想添加其他类型的支持,可以添加如下依赖包.
- Jackson: com.squareup.retrofit2:converter-jackson
- Moshi: com.squareup.retrofit2:converter-moshi
- Protobuf: com.squareup.retrofit2:converter-protobuf
- Wire: com.squareup.retrofit2:converter-wire
- Simple XML: com.squareup.retrofit2:converter-simplexml
其中Gson、String类型最常见.
另外,需要注意的是,由于Retrofit 2.X 是网络请求,别网络在manifest加入访问网络的权限.
<uses-permission android:name="android.permission.INTERNET"/>
Retrofit 的注解分类
与其他请求框架不同的是, 它使用了注解.Retrofit 的注解分为三大类,分别是HTTP请求方法注解、标记类注解和参数类注解。
其中,HTTP 请求方法往解有8种,它们是GET、POST、PUT、DELETE、HEAD、PATCH、OPTIONS 和HTTP.其前7种分别对应HTTP的请求方法;HTTP则可以替换以上7种,也可以扩展请求方法。
标记类注解有3种,它们是FormUrlEnocoded、Mutipart、Streaming。 FormUrlEnocoded和Multipart 后面会讲到。Streaming代表响应的数据以流的形式返回,如果不使用它,则默认会把全部数据加载到内存,所以下大文件时需要加上这个注解。
参数类注解有Header、Headers、Body、Path、Field、FieldMap、Part、Query 和QueryMap等,下面将介绍几种参数类注解的用法。
Get请求访问网络
我们以淘宝的一个链接: http://ip.taobao.com/service/getIpInfo.php?ip=59.108.54.37 进行学习.
其中实体类代码如下:
IpModel 类
public class IpModel {
private int code;
private IpData data;
public void setCode(int code) {
this.code = code;
}
public int getCode() {
return this.code;
}
public void setData(IpData data) {
this.data = data;
}
public IpData getData() {
return this.data;
}
}
IpData类:
private String country;
private String country_id;
private String area;
private String area_id;
private String region;
private String region_id;
private String city;
private String city_id;
private String county;
private String county_id;
private String isp;
private String isp_id;
private String ip;
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return this.country;
}
public void setCountry_id(String country_id) {
this.country_id = country_id;
}
public String getCountry_id() {
return this.country_id;
}
public void setArea(String area) {
this.area = area;
}
public String getArea() {
return this.area;
}
public void setArea_id(String area_id) {
this.area_id = area_id;
}
public String getArea_id() {
return this.area_id;
}
public void setRegion(String region) {
this.region = region;
}
public String getRegion() {
return this.region;
}
public void setRegion_id(String region_id) {
this.region_id = region_id;
}
public String getRegion_id() {
return this.region_id;
}
public void setCity(String city) {
this.city = city;
}
public String getCity() {
return this.city;
}
public void setCity_id(String city_id) {
this.city_id = city_id;
}
public String getCity_id() {
return this.city_id;
}
public void setCounty(String county) {
this.county = county;
}
public String getCounty() {
return this.county;
}
public void setCounty_id(String county_id) {
this.county_id = county_id;
}
public String getCounty_id() {
return this.county_id;
}
public void setIsp(String isp) {
this.isp = isp;
}
public String getIsp() {
return this.isp;
}
public void setIsp_id(String isp_id) {
this.isp_id = isp_id;
}
public String getIsp_id() {
return this.isp_id;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getIp() {
return this.ip;
}
1.直接使用
如果是直接执行@GET注解,代码如下:
@GET("getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg();
Retrofit提供的请求方式注解有@GET和@POST等,分别代表GET请求和POST请求,我们在这里用的是GET请求,访问的地址是”getIpInfo.php?ip=59.108.54.37”。另外定义了getlpMsg方法,这个方法返回 Call类型的参数。接下来创建Retrofit,并创建接口文件,代码如下所示:
String url = "http://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
IpService ipService = retrofit.create(IpService.class);
Call<IpModel> call = ipService.getIpMsg();
Retrofit是通过构造者模式构建出来的.请求URL是拼接而成的,它由baseUrl传入的URL加上请求网络接口的@GET(“getIpInfo.php?ip=59.108.54.37”)中的URL拼接而成的.接下来用Retrofit动态代理获取到之前定义的接口,并调用该接口定义的getIpMsg 方法得到Call对象.接下来用Call请求网络并处理回调.代码如下所示:
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Log.i("gaoleihua", "country" + country);
Toast.makeText(MainActivity.this, country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
这里是异步请求网络,回调的Calbak是运行在UI线程的。得到返回的Response 后将返回数据的country字段用Toast显示出来。如果想同步请求网络,请使用all.execute(); 如果想中断网络请求,则可以使用call.cancel().
2.动态配置URL地址:@path
Retrofit提供了很多请求参数注解,这使得请求网路时更加便捷。其中,@Path用来动态地配置URL地址。请求网络接口代码如下所示:
@GET("{path}/getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg(@Path("path") String path);
在GET注解中包含了{path},它对应着@Path 注解中的”path”,而用来替换{path}的正是
要传入的”String path”的值。请求网络的代码如下所示:
String url = "http://ip.taobao.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
IpServiceForPath ipService = retrofit.create(IpServiceForPath.class);
Call<IpModel> call = ipService.getIpMsg(path);
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Log.i("gaoleihua", "country" + country);
Toast.makeText(MainActivity.this, country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
其中 通过传入 “service” 来替换@GET注解中的{path}的值.
整个完整的url拼接之后,依旧是 http://ip.taobao.com/service/getIpInfo.php?ip=59.108.54.37
3.动态指定查询条件:@Query
之前的例子就是为了查询ip的地址位置,每次查询更换不同的ip就可以了,可以使用@Query来动态地指定ip的值.请求网络接口的代码如下所示:
@GET("getIpInfo.php")
Call<IpModel> getIpMsg(@Query("ip") String ip);
请求网络时,只需要传入想要查询的ip值就可以了.
4.动态指定查询条件组:@QueryMap
在网络请求中一般为了更精确地查找我们所需要的数据,需要传入很多查询参数,如果用@Query会比较麻烦,这时我们可以采用@QueryMap,将所有的参数集成在一个Map中统一传递,如下所示:
@GET("getIpInfo.php")
Call<IpModel> getIpMsg(@QueryMap Map<String, String> options);
Post请求访问网络
1.传递数据类型为键值对:@Filed
传递数据类型为键值对,这是我们最常用的POST请求数据类型,淘宝IP库支持数据类型为键值对的POST请求.请求网络接口的代码如下所示:
@FormUrlEncoded
@POST("getIpInfo.php")
Call<IpModel> getIpMsg(@Field("ip") String first);
首先用@FormUrlEncoded注释来标明这是一个表单请求,然后在getIpMsg方法中使用@Field(“ip”)注解来标识所对应的String类型数据的键,从而组成一组键值对进行传递.请求网络的代码如下所示:
String url = "http://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
IpServiceForPost ipService = retrofit.create(IpServiceForPost.class);
Call<IpModel> call = ipService.getIpMsg(ip);
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Log.i("gaoleihua", "country" + country);
Toast.makeText(MainActivity.this, country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
2.传递数据类型JSON字符串:@Body
这个在使用开发中使用得很少.
我们也可以用POST方式将JSON字符串作为请求体发送到服务器.
用@Body这个注解标识参数对象即可,Retrofit会将对象转换为字符串
3.单个文件上传:@Part
public interface UploadFileForPart {
@Multipart
@POST("user/photo")
Call<User> updataUser(@PartMultipartBody.Part photo,@Part("description")
RequestBody description);
}
Multipart注解标识允许多个@Part. updataUser方法的第一个参数是准备上传的图片文件,使用了PartMultipartBody.Part 类型;另一个参数是RequestBody类型,它用来传递简单的键值对.
File file=new File(Environment.getExternalStorageDirectory(),"gaoleihua.png");
RequestBody photoRequestBody=RequestBody.create(MediaType.parse("image/png"),file);
MultipartBody.Part photo=MultipartBody.Part.createFormData("photos","gaoleihua.png",photoRequestBody);
UploadFileForPart uploadFileForPart=retrofit.create(UploadFileForPart.class);
Call<User> call=uploadFileForPart.updataUser(photo,RequestBody.create(null,"gaoleihua"));
4.多个文件上传:@PartMap
public interface UploadFileForPart {
@Multipart
@POST("user/photo")
Call<User> updataUser(@PartMap Map<String,RequestBody>photos,@Part("description")
RequestBody description);
}
这和单文件上传是类似的,只是使用Map封装了上传的文件,并用@PartMap 注解来标识起来.其他的和单文件上传都一样,这里就不赘述了.
消息报头Header
在HTTP请求中,为了防止攻击或过滤掉不安全的访问,或者添加特殊加密的访问等,以便减轻服务器的压力和保证请求的安全,通常都会在消息报头中携带一些特殊的消息头处理.Retrofit也提供了@Header 来添加消息报头。添加消息报头有两种方式。一种是静态的,另一种是动态的.
下面先来看静态的方式,如下所示:
interface SomeService{
@GET ("some/endpoint")
@Headers ("Accept一Encoding: application/json")
Call<ResponseBody> getCarType();
}
使用@Headers注解添加消息报头。如果想要添加多个消息报头,则可以使用{}包含起来:
interface SomeService {
@GET ("some/endpoint")
@Headers ({"Accept -Encoding:application/json",
"User- Agent:MoonRetrofit"
})
Call<ResponseBody> getCarType();
}
以动态的方式添加消息报头如下所示:
interface SomeService {
@GET ("some/endpoint")
Call<ResponseBody> getCarType(
@Header ("Location") String location)
);
}
使用@Header注释,可以通过调用getCarType方法来动态地添加消息报头.
源码
欢迎star,您的鼓励是我写作最大的动力.