前言
最近,有不少小伙伴都在问Retrofit怎么用,近期笔者的几个项目上都用到了这个框架,所以,为了防止宇宙被破坏,为了维护世界的和平(认真脸)!我决定,还是写一篇关于Retrofit的使用说明吧。
准备工作
工欲善其事必先利其器,要想使用Retrofit,当然首先你得在你的项目里面添加相关的依赖,下面是笔者项目里添加的依赖:
dependencies{
compile'com.android.support:appcompat-v7:23.4.0' // V7包支持
compile'com.squareup.retrofit2:retrofit:2.0.2' // 这个是Retrofit的依赖
compile'com.squareup.retrofit2:converter-gson:2.0.2' // 如果要是用Gson转换的话,需要添加这个依赖
compile'com.squareup.retrofit2:adapter-rxjava:2.0.2' // 用于Retrofit支持RxJava
compile'io.reactivex:rxjava:1.1.0' // RxJava
compile'io.reactivex:rxandroid:1.1.0' // RxAndroid
}
这是笔者使用的版本,当然,你也可以直接到Github中查找他的最新版本,这里贴上Github地址:
https://github.com/square/retrofit
https://github.com/ReactiveX/RxJava
请求封装
一个网络框架接入后,当然不能直接就这么用了,一点简单的封装是必要的。首先,当然是单例模式,然后就是相关的属性设置,不多说,上代码:
RXClientGenerator.java
public class RXClientGenerator {
private static volatile RXApi rxApi;
private static Retrofit retrofit;
private String getBaseUrl() {
return "http://www.weather.com.cn/"; // 服务器地址,笔者写例子用的是一个天气的接口
}
private RXClientGenerator() {
// Retrofit是对OkHttp的封装,所以,在初始化之前,要首先初始化OkHttp
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new NetInterceptor()) // 这行代码添加了一个拦截器,我们后面说
.build();
// 好了,这里开始初始化Retrofit
retrofit = new Retrofit.Builder()
.baseUrl(getBaseUrl()) // 这里设置了服务器的地址,可以解释为所有网络请求地址前面的公共部分
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 你要使用RxJava吗?加上这个吧
.addConverterFactory(GsonConverterFactory.create()) // 如果你的项目中返回的数据是json格式,那么可以添加这行代码,在获取服务器返回数据的时候直接转换成相关的Bean对象
.client(client) // 把之前初始化好的OkHttp对象添加进去
.build();
}
private static class Helper {
static final RXClientGenerator INSTANCE = new RXClientGenerator();
}
public static RXClientGenerator getInstance() {
return Helper.INSTANCE;
}
public synchronized RXApi createClient() {
if (null == rxApi)
return rxApi = retrofit.create(RXApi.class);
else
return rxApi;
}
}
接下来就是网络请求接口的定义,直接上代码
RXApi.java
public interface RXApi {
/**
* 获取天气数据
*/
@GET("data/sk/101010100.html") // 这里是接口的地址,会直接拼接到之前的baseUrl后面
Observable getWeather(); // 尖括号中的泛型是返回数据的类型
/**
* 获取天气数据
*/
@GET("data/sk/101010100.html")
Observable getWeather2(@Query("uid") String uid, // 参数是基本类型时,使用@Query注解,括号中是参数名
@Body WeatherBean body // 参数是对象时,使用@Body注解
);
}
数据Bean
WeatherBean.java
public class WeatherBean {
private WeatherinfoBean weatherinfo;
public WeatherinfoBean getWeatherinfo() {
return weatherinfo;
}
public void setWeatherinfo(WeatherinfoBean weatherinfo) {
this.weatherinfo = weatherinfo;
}
public static class WeatherinfoBean {
private String city;
private String temp1;
private String temp2;
private String weather;
public String getCity() {
return city;
}
public String getTemp1() {
return temp1;
}
public String getTemp2() {
return temp2;
}
public String getWeather() {
return weather;
}
}
}
就这样,对于Retrofit的简单封装就完成了
Retrofit的使用
对Retrofit的简单封装后,就可以在你的项目中使用它进行网络请求了。
RXClientGenerator.getInstance().createClient() // 单例模式获取请求对象
.getWeather() // 定义在RXApi中的接口方法
.subscribeOn(Schedulers.io()) // RxJava方法,控制请求执行的线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() { // 请求成功回调
@Override
public void call(WeatherBean weatherBean) {
onSuccess(weatherBean);
}
}, new Action1() { // 请求失败回调
@Override
public void call(Throwable throwable) {
onFail(throwble);
}
});
当然啦,你可能会觉得这还不够简洁,那么我们可以加入JDK1.8的Lambda表达式来简化代码的书写。
在Android Studio中使用Lambda表达式,需要JDK版本在1.8以上,并且在在Module下的build.gradle中进行相关配置
android {
...
defaultConfig {
...
jackOptions {
enabled true
}
}
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
这样,就可以在代码中使用Lambda表达式了,实现的效果如下:
RXClientGenerator.getInstance().createClient()
.getWeather("0")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(weatherBean -> onSuccess(weatherBean),
throwable -> onFail(throwble));
Interceptor
在前面,我们提到了一个NetInterceptor,这个类是一个拦截器,用来简化网络请求,并且可以打印出网络请求的详细信息,方便调试,不多说,看代码:
NetInterceptor.java
class NetInterceptor implements Interceptor { // 实现了OkHttp的Interceptor接口
private static final Charset UTF8 = Charset.forName("UTF-8");
private String versionName;
private String platform;
private String imei;
/**
* 构造方法
*/
NetInterceptor() {
versionName = BuildConfig.VERSION_NAME; // 版本名
platform = "android"; // 应用平台
imei = ""; 设备IMEI
}
@Override
public Response intercept(Chain chain) throws IOException {
Request oldRequest = chain.request(); // 在这里获取请求对象
// 添加新的参数
HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host())
.addQueryParameter("version", versionName) // 这些是每个接口都必须有的请求参数,可以在这里添加
.addQueryParameter("platform", platform)
.addQueryParameter("imei", imei);
// 新的请求
Request request = oldRequest.newBuilder() // 添加完参数后,转换成新的请求
.method(oldRequest.method(), oldRequest.body())
.url(authorizedUrlBuilder.build())
.build();
if (!BuildConfig.DEBUG) // 如果是正式环境,直接发送请求并返回
return chain.proceed(request);
// 获取请求数据
RequestBody requestBody = request.body();
boolean hasRequestBody = requestBody != null;
Connection connection = chain.connection();
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
StringBuilder sb = new StringBuilder(); // 使用StringBuilder,将所有数据拼接起来
sb.append("\n--> ").append(request.method()).append(" ").append(request.url()).append(" ").append(protocol);
if (hasRequestBody) {
sb.append(" (").append(requestBody.contentLength()).append("-byte body");
}
sb.append("\n");
if (hasRequestBody) {
if (requestBody.contentType() != null) {
sb.append("Content-Type: ").append(requestBody.contentType()).append("\n");
}
if (requestBody.contentLength() != -1) {
sb.append("Content-Length: ").append(requestBody.contentLength()).append("\n");
}
}
Headers headers = request.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
String name = headers.name(i);
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
sb.append(name).append(": ").append(headers.value(i)).append("\n");
}
}
if (!hasRequestBody) {
sb.append("--> END ").append(request.method()).append("\n");
} else if (bodyEncoded(request.headers())) {
sb.append("--> END ").append(request.method()).append(" (encoded body omitted)").append("\n");
} else {
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
sb.append("\n");
sb.append(buffer.readString(charset)).append("\n");
sb.append("--> END ").append(request.method()).append(" (").append(requestBody.contentLength()).append("-byte body)").append("\n");
}
long startNs = System.nanoTime();
// 注意,这里为了打印出返回的数据,实际上是进行了一次请求,在开发中有的接口重复调用会出现数据重复问题,注意判断
Response response = chain.proceed(request);
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
ResponseBody responseBody = response.body();
long contentLength = responseBody.contentLength();
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
sb.append("<-- ").append(response.code()).append(" ").append(response.message()).append(" ")
.append(response.request().url()).append(" (").append(tookMs).append("ms, ")
.append(bodySize).append(" body)").append("\n");
Headers headers1 = response.headers();
for (int i = 0, count = headers1.size(); i < count; i++) {
sb.append(headers1.name(i)).append(": ").append(headers1.value(i)).append("\n");
}
if (!HttpEngine.hasBody(response)) {
sb.append("<-- END HTTP").append("\n");
} else if (bodyEncoded(response.headers())) {
sb.append("<-- END HTTP (encoded body omitted)").append("\n");
} else {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
try {
charset = contentType.charset(UTF8);
} catch (UnsupportedCharsetException e) {
sb.append("\n");
sb.append("Couldn't decode the response body; charset is likely malformed.").append("\n");
sb.append("<-- END HTTP").append("\n");
return response;
}
}
if (contentLength != 0) {
sb.append("\n");
String json = buffer.clone().readString(charset);
sb.append(json).append("\n\n");
String str = jsonFormat(json);
if (str.length() > 1200) {
String start = str.substring(0, 600);
String end = str.substring(str.length() - 600);
sb.append(start).append("\n")
.append("\nThe json was too long...\n\n")
.append(end).append("\n");
} else {
sb.append(str).append("\n");
}
}
sb.append("<-- END HTTP (").append(buffer.size()).append("-byte body)").append("\n");
}
Log.d("NET_INFO", sb.toString()); // 打印信息
return chain.proceed(request); // 发送请求并返回
}
private boolean bodyEncoded(Headers headers) {
String contentEncoding = headers.get("Content-Encoding");
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
}
/**
* 将json字符串格式化后返回
*
* @param json json字符串
* @return 格式化后的字符串
*/
private String jsonFormat(String json) {
if (TextUtils.isEmpty(json)) {
return "Empty/Null json content";
}
try {
json = json.trim();
String message;
if (json.startsWith("{")) {
JSONObject jsonObject = new JSONObject(json);
message = jsonObject.toString(2);
return message;
} else if (json.startsWith("[")) {
JSONArray jsonArray = new JSONArray(json);
message = jsonArray.toString(2);
return message;
} else {
message = "Invalid Json";
}
return message;
} catch (JSONException e) {
Log.e("JSON_ERROR", e.getMessage());
return "Invalid Json";
}
}
}
怎么样,是不是又新学到了一招,今天就讲到这里了,欢迎大家指正。