Retrofit学习记录
Retrofit是Square公司开发的一款针对Android网络请求的框架,底层是基于OkHttp实现的,与OkHttp不同的是,它更多的使用运行时注解的方式提供功能
使用流程
1. 使用前的准备工作
配置依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
加入网络权限
<uses-permission android:name="android.permission.INTERNET"/>
2. Retrofit注解的分类
Retrofit的注解分三大类:
-
HTTP请求方法注解
1.1 GET
1.2 POST
1.3 PUT
1.4 DELETE
1.5 HEAD
1.6 PATCH
1.7 OPTIONS
1.8 HTTP -
标记类注解
2.1 FormUrlEncoded:标明这是一个表单请求
2.2 Multipart:Multipart注解表示允许多个@Part
2.3 Streaming:代表响应数据以流形式返回。若不使用,则默认吧全部数据加载到内存,下载大文件时需要加上此注解 -
参数类注解
有Header、Headers、Body、Path、Field、FieldMap、Part、PartMap、Query、QueryMap等
3. 编写实体类
Ip
public class Ip {
private String ip;
public Ip(String ip) {
this.ip = ip;
}
}
IpData
public class 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;
}
}
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;
}
}
4. 创建布局
res/values/dimen
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
activity_main
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
android:id="@+id/bt_request"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:text="请求网络"
android:gravity="center"
android:background="@android:color/darker_gray"/>
</RelativeLayout>
简单写个按钮叭
5. GET请求网络
先使用GET方式来访问网络,创建接口IpService
IpService
public interface IpService {
@GET("getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg();
}
Retrofit提供的请求方式注解有@GET和@POST等,分别代表GET请求和POST请求,这里使用GET。另外定义了getIpMsg()方法返回Call<IpModel>类型参数,接下来创建Retrofit,创建接口文件
MainActivity
public class MainActivity extends AppCompatActivity {
private Button bt_request;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_request = (Button) findViewById(R.id.bt_request);
bt_request.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getIpInformation("59.108.54.37");
}
});
}
private void getIpInformation(String ip) {
String url = "https://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
//增加返回值为String的支持
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
IpService ipService = retrofit.create(IpService.class);
Call<IpModel> call = ipService.getIpMsg();
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
}
});
}
}
一步一步分解来看
Retrofit是通过建造者模式建造出来的,请求URL是拼接而成的,是由baseUrl传入的URL加入请求网络接口的@GET("getIpInfo.php?ip=59.108.54.37")中的URL拼接而来的,如下
String url = "http://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
//增加返回值为String的支持
.addConverterFactory(GsonConverterFactory.create())
.build();
接下来用Retrofit的动态代理获取之前定义的接口,并调用该接口定义的getIpMsg()方法得到Call对象,如下
IpService ipService = retrofit.create(IpService.class);
Call<IpModel> call = ipService.getIpMsg();
最后异步请求网络,回调Callback是运行在UI线程的,得到返回的Response后将返回数据的country用Toast显示出来,同步请求的话就调用execute()方法,中断网络请求就调用cancel()方法即可,如下
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
}
});
5.1 动态配置URL地址:@Path
Retrofit提供很多请求参数的注解,使得请求网络时更加便捷,其中,@Path用来动态配置URL地址
IpServiceForPath
public interface IpServiceForPath {
@GET("{path}/getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg(@Path("path") String path);
}
GET注解中包含了{path},对应着@Path注解中的"path",而用来替换{path}的正是需要传入的"StringPath"的值。在getIpMsg()方法中添加path。因此,上面GET请求的方法修改如下
private void getIpInformationForPath(String path) {
//GET请求网络,动态配置Path
String url = "https://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
//增加返回值为String的支持
.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();
Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
}
});
}
5.2 动态指定查询条件:@Query
之前的例子就是为了查询ip的地址位置,每次查询更换不同的ip即可,可以用@Query来动态指定ip地址,编写如下接口
IpServiceForQuery
public interface IpServiceForQuery{
@GET("getIpInfo.php")
Call<IpModel> getIpMsg(@Query("ip")String ip);
}
请求网络时,传入想要查询的IP即可
getIpInformationForQuery
private void getIpInformationForQuery(String ip) {
String url = "https://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
IpServiceForQuery ipService = retrofit.create(IpServiceForQuery.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();
Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
}
});
}
5.3 动态指定查询条件组:@QueryMap
网络请求中一般为了更精确的查找我们需要的数据,需要传入很多查询参数,如果使用@Query会比较麻烦,这时我们可以采用@QueryMap,将所有参数集成在一个Map中统一传递
IpServiceForQueryMap
public interface IpServiceForQueryMap {
@GET("getIpInfo.php")
Call<IpModel> getIpMsg(@QueryMap Map<String, String> options);
}
6. POST请求访问网络
6.1 传输数据类型为键值对:@Field
传输数据类型为键值对是我们最常用的POST请求数据类型,淘宝IP库支持数据类型为键值对的POST请求,编写接口如下
IpServiceForPost
public interface IpServiceForPost {
@FormUrlEncoded
@POST("getIpInfo.php")
Call<IpModel> getIpMsg(@Field("ip") String first);
}
首先用@FormUrlEncoded注解来标明这是一个表单请求,然后在getIpMsg()方法中使用@Field注解来标识对应的String类型数据的键,从而组成一组键值对进行传递,请求网络代码如下
postIpInformation
private void postIpInformation(String ip) {
String url = "https://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();
Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
}
});
}
6.2 传输数据类型JSON字符串:@Body
我们也可以用POST方式将JSON字符串作为请求体发送到服务器,请求网络接口如下
IpServiceForPostBody
public interface IpServiceForPostBody {
@POST("getIpInfo.php")
Call<IpModel> getIpMsg(@Body Ip ip);
}
用@Body这个注解标识参数对象即可,Retrofirt会将Ip对象转为字符串对象
请求网络如下
postInformationForBody
private void postIpInformationForBody(String ip) {
String url = "https://ip.taobao.com/service/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
IpServiceForPostBody ipService = retrofit.create(IpServiceForPostBody.class);
Call<IpModel> call = ipService.getIpMsg(new Ip(ip));
call.enqueue(new Callback<IpModel>() {
@Override
public void onResponse(Call<IpModel> call, Response<IpModel> response) {
String country = response.body().getData().getCountry();
Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(Call<IpModel> call, Throwable t) {
}
});
}
6.3 单个文件上传:@Part
public interface UploadFileForPart{
@Multipart
@POST("user/photo")
Call<User> updateUser(@Part MultipartBody.Part photo,
@Part("description") RequestBody description)
}
Multipart注解表示允许多个@Part。updateUser方法的第一个参数是准备上传的图片文件,使用了MultipartBody.Part类型,另一个参数是RequestBody类型,用来传递简单的键值对。
6.4 多个文件上传:@PartMap
与单文件上传类似,只不过用Map封装了上传文件,并用@PartMap注解来标识,其余和单文件上传一致
public interface UploadFileForPartMap{
@Multipart
@POST("user/photo")
Call<User> updateUser(@PartMap Map<String, RequestBody> photos,
@Part("description") RequestBody description)
}
6.5 消息报头Header
在Http请求中,为了防止攻击或过滤掉不安全的访问,或者添加特殊加密的访问等,以便减轻服务器的压力和保证请求的安全,通常都会在消息报头中携带一些特殊的消息头作处理,Retrofit也提供了@Header添加消息报头。添加消息报头的方式有两种,一种是静态的,一种是动态的,先来看静态方式
静态添加
interface SomeService{
@GET("some/endpoint")
@Headers("Accept-Encoding: application/json")
Call<ResponseBody> getCarType();
}
使用@Header注解添加消息报头,如果想要添加多个消息报头,则使用{ }包含起来
interface SomeService{
@GET("some/endpoint")
@Headers({
"Accept-Encoding: application/json",
"User-Agent: RetrofitTest"
})
Call<ResponseBody> getCarType();
}
动态添加
interface SomeService{
@GET("some/endpoint")
Call<ResponseBody> getCarType(@Headers("Location") String Location);
}
调用getCarType()方法动态添加报头
本文摘自《进阶之光——刘望舒》,学习路线上的总结记录,不以盈利为目的
欢迎指正。