前言
最近鳖悠,有不少小伙伴都在問Retrofit怎么用榜掌,近期筆者的幾個(gè)項(xiàng)目上都用到了這個(gè)框架,所以乘综,為了防止宇宙被破壞憎账,為了維護(hù)世界的和平(認(rèn)真臉)!我決定卡辰,還是寫一篇關(guān)于Retrofit的使用說明吧胞皱。
準(zhǔn)備工作
工欲善其事必先利其器,要想使用Retrofit九妈,當(dāng)然首先你得在你的項(xiàng)目里面添加相關(guān)的依賴反砌,下面是筆者項(xiàng)目里添加的依賴:
dependencies{
compile'com.android.support:appcompat-v7:23.4.0' // V7包支持
compile'com.squareup.retrofit2:retrofit:2.0.2' // 這個(gè)是Retrofit的依賴
compile'com.squareup.retrofit2:converter-gson:2.0.2' // 如果要是用Gson轉(zhuǎn)換的話,需要添加這個(gè)依賴
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
}
這是筆者使用的版本萌朱,當(dāng)然宴树,你也可以直接到Github中查找他的最新版本,這里貼上Github地址:
https://github.com/square/retrofit
https://github.com/ReactiveX/RxJava
請(qǐng)求封裝
一個(gè)網(wǎng)絡(luò)框架接入后嚷兔,當(dāng)然不能直接就這么用了森渐,一點(diǎn)簡(jiǎn)單的封裝是必要的做入。首先,當(dāng)然是單例模式同衣,然后就是相關(guān)的屬性設(shè)置竟块,不多說,上代碼:
RXClientGenerator.java
public class RXClientGenerator {
private static volatile RXApi rxApi;
private static Retrofit retrofit;
private String getBaseUrl() {
return "http://www.weather.com.cn/"; // 服務(wù)器地址耐齐,筆者寫例子用的是一個(gè)天氣的接口
}
private RXClientGenerator() {
// Retrofit是對(duì)OkHttp的封裝浪秘,所以,在初始化之前埠况,要首先初始化OkHttp
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new NetInterceptor()) // 這行代碼添加了一個(gè)攔截器耸携,我們后面說
.build();
// 好了,這里開始初始化Retrofit
retrofit = new Retrofit.Builder()
.baseUrl(getBaseUrl()) // 這里設(shè)置了服務(wù)器的地址辕翰,可以解釋為所有網(wǎng)絡(luò)請(qǐng)求地址前面的公共部分
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 你要使用RxJava嗎夺衍?加上這個(gè)吧
.addConverterFactory(GsonConverterFactory.create()) // 如果你的項(xiàng)目中返回的數(shù)據(jù)是json格式,那么可以添加這行代碼喜命,在獲取服務(wù)器返回?cái)?shù)據(jù)的時(shí)候直接轉(zhuǎn)換成相關(guān)的Bean對(duì)象
.client(client) // 把之前初始化好的OkHttp對(duì)象添加進(jìn)去
.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;
}
}
接下來就是網(wǎng)絡(luò)請(qǐng)求接口的定義沟沙,直接上代碼
RXApi.java
public interface RXApi {
/**
* 獲取天氣數(shù)據(jù)
*/
@GET("data/sk/101010100.html") // 這里是接口的地址,會(huì)直接拼接到之前的baseUrl后面
Observable getWeather(); // 尖括號(hào)中的泛型是返回?cái)?shù)據(jù)的類型
/**
* 獲取天氣數(shù)據(jù)
*/
@GET("data/sk/101010100.html")
Observable getWeather2(@Query("uid") String uid, // 參數(shù)是基本類型時(shí)壁榕,使用@Query注解矛紫,括號(hào)中是參數(shù)名
@Body WeatherBean body // 參數(shù)是對(duì)象時(shí),使用@Body注解
);
}
數(shù)據(jù)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;
}
}
}
就這樣牌里,對(duì)于Retrofit的簡(jiǎn)單封裝就完成了
Retrofit的使用
對(duì)Retrofit的簡(jiǎn)單封裝后颊咬,就可以在你的項(xiàng)目中使用它進(jìn)行網(wǎng)絡(luò)請(qǐng)求了。
RXClientGenerator.getInstance().createClient() // 單例模式獲取請(qǐng)求對(duì)象
.getWeather() // 定義在RXApi中的接口方法
.subscribeOn(Schedulers.io()) // RxJava方法牡辽,控制請(qǐng)求執(zhí)行的線程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() { // 請(qǐng)求成功回調(diào)
@Override
public void call(WeatherBean weatherBean) {
onSuccess(weatherBean);
}
}, new Action1() { // 請(qǐng)求失敗回調(diào)
@Override
public void call(Throwable throwable) {
onFail(throwble);
}
});
當(dāng)然啦喳篇,你可能會(huì)覺得這還不夠簡(jiǎn)潔,那么我們可以加入JDK1.8的Lambda表達(dá)式來簡(jiǎn)化代碼的書寫态辛。
在Android Studio中使用Lambda表達(dá)式杭隙,需要JDK版本在1.8以上,并且在在Module下的build.gradle中進(jìn)行相關(guān)配置
android {
...
defaultConfig {
...
jackOptions {
enabled true
}
}
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
這樣因妙,就可以在代碼中使用Lambda表達(dá)式了,實(shí)現(xiàn)的效果如下:
RXClientGenerator.getInstance().createClient()
.getWeather("0")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(weatherBean -> onSuccess(weatherBean),
throwable -> onFail(throwble));
Interceptor
在前面票髓,我們提到了一個(gè)NetInterceptor攀涵,這個(gè)類是一個(gè)攔截器,用來簡(jiǎn)化網(wǎng)絡(luò)請(qǐng)求洽沟,并且可以打印出網(wǎng)絡(luò)請(qǐng)求的詳細(xì)信息以故,方便調(diào)試,不多說裆操,看代碼:
NetInterceptor.java
class NetInterceptor implements Interceptor { // 實(shí)現(xiàn)了OkHttp的Interceptor接口
private static final Charset UTF8 = Charset.forName("UTF-8");
private String versionName;
private String platform;
private String imei;
/**
* 構(gòu)造方法
*/
NetInterceptor() {
versionName = BuildConfig.VERSION_NAME; // 版本名
platform = "android"; // 應(yīng)用平臺(tái)
imei = ""; 設(shè)備IMEI
}
@Override
public Response intercept(Chain chain) throws IOException {
Request oldRequest = chain.request(); // 在這里獲取請(qǐng)求對(duì)象
// 添加新的參數(shù)
HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host())
.addQueryParameter("version", versionName) // 這些是每個(gè)接口都必須有的請(qǐng)求參數(shù)怒详,可以在這里添加
.addQueryParameter("platform", platform)
.addQueryParameter("imei", imei);
// 新的請(qǐng)求
Request request = oldRequest.newBuilder() // 添加完參數(shù)后炉媒,轉(zhuǎn)換成新的請(qǐng)求
.method(oldRequest.method(), oldRequest.body())
.url(authorizedUrlBuilder.build())
.build();
if (!BuildConfig.DEBUG) // 如果是正式環(huán)境,直接發(fā)送請(qǐng)求并返回
return chain.proceed(request);
// 獲取請(qǐng)求數(shù)據(jù)
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昆烁,將所有數(shù)據(jù)拼接起來
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();
// 注意吊骤,這里為了打印出返回的數(shù)據(jù),實(shí)際上是進(jìn)行了一次請(qǐng)求静尼,在開發(fā)中有的接口重復(fù)調(diào)用會(huì)出現(xiàn)數(shù)據(jù)重復(fù)問題白粉,注意判斷
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); // 發(fā)送請(qǐng)求并返回
}
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";
}
}
}
怎么樣,是不是又新學(xué)到了一招鼠渺,今天就講到這里了鸭巴,歡迎大家指正。