背景
如果看到了 OkHttp, RxJava 相关文章的话,一定会同时看到 Retrofit。
OkHttp 是对 http 客户端进行了封装,RxJava 是反应式宣言,那 Retrofit 是什么呢?
于是这便是本文的写作背景。
第一个例子
Retrofit 的一个用法是直接把 Http 请求封装为 Java 对象。举个例子就明白了。
封装请求参数
原始的 url 为:
curl -s "http://ip.taobao.com/service/getIpInfo2.php?ip=119.75.213.61" {"code":0,"data":{"ip":"119.75.213.61","country":"中国","area":"","region":"北京","city":"北京","county":"XX","isp":"电信","country_id":"CN","area_id":"","region_id":"110000","city_id":"110100","county_id":"xx","isp_id":"100017"}}
下面开始变魔术,将 url 网络调用变为 java 方法调用,调用方对网络基本无感知。
IPService.java
package testRetrofit.ip;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface IPService {
@GET("service/getIpInfo2.php")
Call<ResponseBody> getip(@Query("ip") String ip);
}
MainIp.java
package testRetrofit.ip;
import java.io.IOException;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Retrofit;
public class MainIp {
public static void main(String[] args) throws IOException {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://ip.taobao.com/").build();
IPService service = retrofit.create(IPService.class);
// 这里是调用方, 仅仅能看到execute()了一下
Call<ResponseBody> repos = service.getip("119.75.213.61");
ResponseBody repo = repos.execute().body();
System.out.println(repo.string());
}
}
运行结果
{"code":0,"data":{"ip":"119.75.213.61","country":"中国","area":"","region":"北京","city":"北京","county":"XX","isp":"电信","country_id":"CN","area_id":"","region_id":"110000","city_id":"110100","county_id":"xx","isp_id":"100017"}}
在本例中,通过接口注解,调用 java 方法,Retrofit 自动将其转化为 http 请求参数,去请求服务器,并返回结果。
本例主要演示参数的封装。
封装运行结果
上面的例子中,返回的结果为 ResponseBody,还需要自己处理。在本例中,通过定义一个 IP 类,来直接返回 IP 类对象。具体如下:
IPService2.java
package testRetrofit.ip2;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface IPService2 {
@GET("service/getIpInfo2.php")
Call<Result<IP>> getip(@Query("ip") String ip);
}
Result.java
package testRetrofit.ip2;
public class Result<T> {
public int code;
public T data;
@Override
public String toString() {
return "Result [code=" + code + ", data=" + data + "]";
}
}
IP.java
package testRetrofit.ip2;
public class IP {
public String ip;
public String country;
public String area;
public String region;
public String city;
public String county;
public String isp;
public String country_id;
public String area_id;
public String region_id;
public String city_id;
public String county_id;
public String isp_id;
@Override
public String toString() {
return "IP [ip=" + ip + ", country=" + country + ", area=" + area + ", region=" + region + ", city=" + city
+ ", county=" + county + ", isp=" + isp + ", country_id=" + country_id + ", area_id=" + area_id
+ ", region_id=" + region_id + ", city_id=" + city_id + ", county_id=" + county_id + ", isp_id="
+ isp_id + "]";
}
}
MainIp2.java
package testRetrofit.ip2;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MainIp2 {
public static void main(String[] args) throws IOException {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://ip.taobao.com/")
.addConverterFactory(GsonConverterFactory.create()).build();
IPService2 service = retrofit.create(IPService2.class);
// 这里是调用方, 仅仅能看到execute()了一下,返回了IP对象结果。
Call<Result<IP>> repos = service.getip("119.75.213.61");
Result<IP> repo = repos.execute().body();
System.out.println(repo.toString());
}
}
运行结果
Result [code=0, data=IP [ip=119.75.213.61, country=中国, area=, region=北京, city=北京, county=XX, isp=电信, country_id=CN, area_id=, region_id=110000, city_id=110100, county_id=xx, isp_id=100017]]
到此为止,完成了一个入门使用说明。但是实际情况中,对于输入和输出可能有更复杂的情况。这时,怎么用 retrofit 来处理呢?
输入和输出
输入和输出可能有多种情况。
输入
输入参数的指定方法,可以通过 url,可以通过 body,可以通过表单、分块表单,可以通过 Header 等。
这里仅罗列一下,需要的时候可以去[1]中来找示例,都不复杂。
请求方法:
@GET
@POST
@PUT
@DELETE
@PATCH
@HEAD
@OPTIONS
@HTTP
请求标记:
@FormUrlEncoded
: 表单中的 field@Multipart
: 分传上传@Streaming
: 流形式返回
请求参数:
@Headers
: Header 控制@Header
: Header 控制@HeaderMap
: Header 控制@Body
: HTTP 请求 body@Field
: 表单中的 field@FieldMap
: 表单中的 field@Part
: 分传上传@PartMap
: 分传上传@Path
: 通过路径传递参数,如"users/{user}/repos"
@Query
:@QueryMap
:@Url
:
输出
输出的结果可以是 Json 格式的,可以是 Protobuf 格式的,可以是 XML 格式的,可以是 Scalars 格式的,也可以是用户自定义格式的。
前面的例子即是 Json 格式的。
- Json 适配器:提供了 Gson, Jackson 和 Moshi 三种。
- Protobuf 适配器:提供了 Protobuf 和 Wire 两种。
- XML 适配器:Simple XML
自定义 Converter
如果返回的结果不是标准的 Json 格式,而是自定义的格式,又想返回成 Java 对象直接使用,怎么办呢?
这时可以通过自定义 Converter,来实现定制输出结果类。
下面举个自定义格式的例子:
通过 IPResultConverter
和 IPResultConverterFactory
来解析返回的 IP Json,输出{国家}{省}{市}
IPResultConverter.java
package testRetrofit.ip3;
import java.io.IOException;
import java.util.Map;
import com.google.gson.Gson;
import okhttp3.ResponseBody;
import retrofit2.Converter;
public class IPResultConverter implements Converter<ResponseBody, String> {
@Override
public String convert(ResponseBody value) throws IOException {
// 将responseBody转换为了string
Map result = new Gson().fromJson(value.string(), Map.class);
if (result.containsKey("data")) {
Map dataMap = (Map) result.get("data");
return dataMap.get("country") + "_" + dataMap.get("region") + "_" + dataMap.get("city");
}
return null;
}
}
IPResultConverterFactory.java
package testRetrofit.ip3;
import java.io.IOException;
import java.util.Map;
import com.google.gson.Gson;
import okhttp3.ResponseBody;
import retrofit2.Converter;
public class IPResultConverter implements Converter<ResponseBody, String> {
@Override
public String convert(ResponseBody value) throws IOException {
Map result = new Gson().fromJson(value.string(), Map.class);
if (result.containsKey("data")) {
Map dataMap = (Map) result.get("data");
return dataMap.get("country") + "_" + dataMap.get("region") + "_" + dataMap.get("city");
}
return null;
}
}
IPService3.java
package testRetrofit.ip3;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface IPService3 {
@GET("service/getIpInfo2.php")
Call<String> getip(@Query("ip") String ip);
}
MainIp3.java
package testRetrofit.ip3;
import java.io.IOException;
import retrofit2.Call;
import retrofit2.Retrofit;
public class MainIp3 {
public static void main(String[] args) throws IOException {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://ip.taobao.com/")
.addConverterFactory(new IPResultConverterFactory()).build();
IPService3 service = retrofit.create(IPService3.class);
Call<String> repos = service.getip("119.75.213.61");
String repo = repos.execute().body();
System.out.println(repo);
}
}
结果
中国_北京_北京
自定义 CallAdapter
自定义 CallAdapter 是做什么的呢?
默认情况下,我们返回的是一个 Call<?>
的结果。如果我们不想要 Call<?>
,而是想要 Observable<?>
,或者想要 LiveData<?>
呢?
retrofit 允许通过自定义 CallAdapter,来对 Call<?>
再做一次转换。
如果看了前面的反应式宣言的话,这里这么做的目的就很好理解了。
同样举个例子,其中没有详细来写,仅做示例:
IPService4.java
package testRetrofit.ip4;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Query;
import rx.Observable;
public interface IPService4 {
@GET("service/getIpInfo2.php")
Observable<ResponseBody> getip(@Query("ip") String ip);
}
MainIp4.java
package testRetrofit.ip4;
import java.io.IOException;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import rx.Observable;
import rx.Subscriber;
import rx.schedulers.Schedulers;
public class MainIp4 {
public static void main(String[] args) throws IOException {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://ip.taobao.com/")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
IPService4 service = retrofit.create(IPService4.class);
Observable<ResponseBody> repos = service.getip("119.75.213.61");
repos.observeOn(Schedulers.io()).subscribe(new Subscriber<ResponseBody>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
}
@Override
public void onNext(ResponseBody body) {
try {
System.out.println(body.string());
} catch (IOException e) {
e.printStackTrace();
}
}
});
try {
Thread.sleep(10000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果
{"code":0,"data":{"ip":"119.75.213.61","country":"中国","area":"","region":"北京","city":"北京","county":"XX","isp":"电信","country_id":"CN","area_id":"","region_id":"110000","city_id":"110100","county_id":"xx","isp_id":"100017"}}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于