Retrofit+RxJava+OKHttp接入指南
前面學(xué)習(xí)了Retrofit和OKHttp,就嘗試在自己的項(xiàng)目中使用了一下。本文介紹了接入Retrofit的一些問(wèn)題蚁孔,可供想使用Retrofit的同學(xué)作為參考睬魂。
api請(qǐng)求添加公共的Headers和params
如果通過(guò)注解的方式模庐,在每個(gè)請(qǐng)求接口上添加公共headers和params,代碼會(huì)太繁瑣和冗余酪惭。所以可通過(guò)攔截器添加共的headers和params希痴。
我定義的攔截器CommonInterceptor的攔截方法如下:
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request oldRequest = chain.request();
// 添加公共的請(qǐng)求參數(shù)
HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host())
.addQueryParameter("from", "android")
.addQueryParameter("version", Config.SDK_VERSION);
// 添加公共的請(qǐng)求headers
Request newRequest = oldRequest.newBuilder()
.addHeader("User-Agent", "android_" + Config.SDK_VERSION +
";" + Config.UNICOM_USERAGENT)
.method(oldRequest.method(), oldRequest.body())
.url(authorizedUrlBuilder.build())
.build();
return chain.proceed(newRequest);
}
把攔截器通過(guò)OKHttpClent設(shè)置給Retrofit:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(commonInterceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(WebConfig.ONLINE_URL_PRE)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
查看請(qǐng)求和響應(yīng)Log日志
使用Retrofit查看請(qǐng)求和響應(yīng)日志也是通過(guò)攔截器實(shí)現(xiàn),HttpLoggingInterceptor是OKHttp提供的Log攔截器可以直接使用:
@Override
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
//設(shè)置日志級(jí)別
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
把攔截器通過(guò)OKHttpClent設(shè)置給Retrofit撞蚕,同上不贅述润梯。
使用OKHttp數(shù)據(jù)緩存功能
OKHttp本身就支持?jǐn)?shù)據(jù)緩存功能过牙,只要在請(qǐng)求接口定義緩存標(biāo)簽:
/**
* 獲取鑒權(quán)接口
*/
@Headers("Cache-Control:public ,max-age=2592000")
@GET("oauth/token")
Observable<Oauth.Result> getToken();
但有個(gè)問(wèn)題甥厦,如果服務(wù)端不支持緩存,會(huì)返回一些干擾信息寇钉,會(huì)影響緩存功能刀疙,所以要使用攔截器清除干擾信息,攔截方法如下:
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (NetworkUtils.isNetworkConnected(PlayerManager.getAppContext())) {
String cacheControl = request.cacheControl().toString();
return response.newBuilder()
//清除頭信息
.removeHeader("Pragma")
.header("Cache-Control", cacheControl)
.build();
}
return response;
}
把攔截器通過(guò)OKHttpClent設(shè)置給Retrofit:
CacheInterceptor cacheInterceptor = new CacheInterceptor();
//設(shè)置緩存目錄扫倡、緩存大小
File httpCacheDirectory = new File(EnvironmentUtilities.getHttpCachePath());
int cacheSize = 10 * 1024 * 1024;
Cache cache = new Cache(httpCacheDirectory, cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(cacheInterceptor)
.cache(cache)
.build();
......
提示:該緩存是api級(jí)別的對(duì)數(shù)據(jù)整體緩存谦秧,整讀整存。如果緩存數(shù)據(jù)跟業(yè)務(wù)管理密切需要部分更新數(shù)據(jù)撵溃,需要自己用數(shù)據(jù)庫(kù)實(shí)現(xiàn)緩存功能疚鲤。
統(tǒng)一處理網(wǎng)絡(luò)請(qǐng)求狀態(tài)、服務(wù)端返回狀態(tài)碼ErrorCode
每個(gè)接口在處理業(yè)務(wù)數(shù)據(jù)之前缘挑,都要處理網(wǎng)絡(luò)請(qǐng)求狀態(tài)集歇、服務(wù)端返回狀態(tài)碼ErrorCode,這應(yīng)該一個(gè)統(tǒng)一的地方處理语淘。
構(gòu)建DefaultObserver處理服務(wù)器響應(yīng)數(shù)據(jù)诲宇。定義DefaultObserver類(lèi)繼承Observer,并重寫(xiě)相應(yīng)的方法惶翻。
1姑蓝、在onError()方法里
@Override
public void onError(Throwable e) {
LogUtils.e("Retrofit", e.getMessage());
mBaseImpl.dismissProgress();
// HTTP錯(cuò)誤
if (e instanceof HttpException) {
onException(ExceptionReason.BAD_NETWORK);
// 連接錯(cuò)誤
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) {
onException(CONNECT_ERROR);
// 連接超時(shí)
} else if (e instanceof InterruptedIOException) {
onException(CONNECT_TIMEOUT);
// 解析錯(cuò)誤
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
onException(PARSE_ERROR);
} else {
onException(UNKNOWN_ERROR);
}
}
onException方法中做各種異常處理,比如Toast提示吕粗。
2纺荧、在onNext統(tǒng)一處理服務(wù)器返回狀態(tài)碼
@Override
public void onNext(T response) {
mBaseImpl.dismissProgress();
if (!response.isError()) {
onSuccess(response);
} else {
onFail(response);
}
}
onFail方法處理,異常返回碼的各種狀態(tài)碼颅筋,onSuccess為抽象方法宙暇,用戶(hù)實(shí)現(xiàn)觀察者時(shí)候?qū)崿F(xiàn)該方法,真正的處理正常返回的業(yè)務(wù)數(shù)據(jù)垃沦。
Rxjava生命周期管理
異步處理客给,如果控制不好生命周期,會(huì)導(dǎo)致內(nèi)存泄露和一些莫名其妙的問(wèn)題肢簿。
封裝BaseActivityRxjava生命周期管理靶剑。
public class BaseRxActivity extends AppCompatActivity implements BaseImpl {
private CustomProgressDialog mProgressDialog;
private CompositeDisposable disposables2Stop;// 管理Stop取消訂閱者者
private CompositeDisposable disposables2Destroy;// 管理Destroy取消訂閱者者
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (disposables2Destroy != null) {
throw new IllegalStateException("onCreate called multiple times");
}
disposables2Destroy = new CompositeDisposable();
}
public boolean addRxStop(Disposable disposable) {
if (disposables2Stop == null) {
throw new IllegalStateException(
"addUtilStop should be called between onStart and onStop");
}
disposables2Stop.add(disposable);
return true;
}
public boolean addRxDestroy(Disposable disposable) {
if (disposables2Destroy == null) {
throw new IllegalStateException(
"addUtilDestroy should be called between onCreate and onDestroy");
}
disposables2Destroy.add(disposable);
return true;
}
public void remove(Disposable disposable) {
if (disposables2Stop == null && disposables2Destroy == null) {
throw new IllegalStateException("remove should not be called after onDestroy");
}
if (disposables2Stop != null) {
disposables2Stop.remove(disposable);
}
if (disposables2Destroy != null) {
disposables2Destroy.remove(disposable);
}
}
public void onStart() {
super.onStart();
if (disposables2Stop != null) {
throw new IllegalStateException("onStart called multiple times");
}
disposables2Stop = new CompositeDisposable();
}
public void onStop() {
super.onStop();
if (disposables2Stop == null) {
throw new IllegalStateException("onStop called multiple times or onStart not called");
}
disposables2Stop.dispose();
disposables2Stop = null;
}
public void onDestroy() {
super.onDestroy();
if (disposables2Destroy == null) {
throw new IllegalStateException(
"onDestroy called multiple times or onCreate not called");
}
disposables2Destroy.dispose();
disposables2Destroy = null;
}
}
生命周期管理是通過(guò)任務(wù)管理器CompositeDisposable 來(lái)管理的蜻拨,就是在執(zhí)行請(qǐng)求時(shí)候?qū)isposable添加到一個(gè)CompositeDisposable ,在頁(yè)面至后臺(tái)或者銷(xiāo)毀時(shí)候調(diào)用dispose桩引,取消Rxjava生命周期缎讼。
Disposable添加是在Observer 的onSubscribe調(diào)用的
@Override
public void onSubscribe(Disposable d) {
// 在onStop中取消訂閱
if (isAddInStop) {
mBaseImpl.addRxStop(d);
// 在onDestroy中取消訂閱
} else {
mBaseImpl.addRxDestroy(d);
}
}
自定義Convert響應(yīng)數(shù)據(jù)轉(zhuǎn)換器
一般Retrofit會(huì)配套使用Gson使用,但是如果改造老項(xiàng)目坑匠,把json解析從手動(dòng)解析換成Gson解析可能工作量巨大血崭,還是個(gè)容易出錯(cuò)的體力話(huà)。特別的接口協(xié)議設(shè)計(jì)不規(guī)范厘灼,沒(méi)有面相對(duì)象的思想的時(shí)候夹纫,你會(huì)改到想吐。所以這種辦法是自定義Convert數(shù)據(jù)轉(zhuǎn)換器设凹,復(fù)用原來(lái)的json數(shù)據(jù)解析舰讹。
@Override
public T convert(ResponseBody responseBody) throws IOException {
T t;
try {
t = ReflectUtil.getTClassInstance(type);
t.parse(responseBody.string());
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (InstantiationException e) {
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
return t;
}
parse方法是我們項(xiàng)目自己手動(dòng)解析的方法。
Retrofit直接請(qǐng)求完整的Url
我們老項(xiàng)目中闪朱,沒(méi)有使用Param來(lái)組裝請(qǐng)求月匣,而是直接用字符串拼裝完整的url,然后請(qǐng)求api奋姿。所以去改造成params的話(huà)锄开,又是一個(gè)體力話(huà),所以我們這種直接使用完整的url請(qǐng)求称诗。幸好Retrofit提供了這樣的標(biāo)簽萍悴。
@GET()
Observable<ResponseBody> getFile(@Url String url);
這種情況還適用于,url是個(gè)動(dòng)態(tài)的地址粪狼,比如是服務(wù)端返回的地址退腥。
OKHttp鏈接Https不校驗(yàn)身份
跟HttpcCient和HttpURLConnection一樣需要重寫(xiě)SSLSocketFactory繞過(guò)ssl身份驗(yàn)證,重寫(xiě)HostNameVerifier并且不做主機(jī)名驗(yàn)證再榄。
//自定義TrustManager狡刘,繞過(guò)ssl身份驗(yàn)證
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}};
SSLContext sc = null;
try {
sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
OkHttpClient client = new OkHttpClient.Builder()
//重寫(xiě)HostNameVerifier,主機(jī)名驗(yàn)證直接返回true
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.sslSocketFactory(sc.getSocketFactory())
.build();
以上為我在接入Retrofit和OKHttp遇到的問(wèn)題以及解決方案,大家還有其他問(wèn)題可以留言困鸥,大家探討后我再補(bǔ)充嗅蔬。