封裝成果
封裝完以后驼鞭,具有如下功能:
1.Retrofit+Rxjava+okhttp基本使用方法
2.統(tǒng)一處理請(qǐng)求數(shù)據(jù)格式
3.統(tǒng)一的ProgressDialog和回調(diào)Subscriber處理
4.取消http請(qǐng)求
5.預(yù)處理http請(qǐng)求
6.返回?cái)?shù)據(jù)的統(tǒng)一判斷
7.失敗后的retry封裝處理
8.RxLifecycle管理生命周期皮获,防止泄露
效果:
具體使用:
封裝后http請(qǐng)求代碼如下
// 完美封裝簡(jiǎn)化版
private void simpleDo() {
SubjectPost postEntity = new SubjectPost(simpleOnNextListener,this);
postEntity.setAll(true);
HttpManager manager = HttpManager.getInstance();
manager.doHttpDeal(postEntity);
}
// 回調(diào)一一對(duì)應(yīng)
HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<Subject>>() {
@Override
public void onNext(List<Subject> subjects) {
tvMsg.setText("已封裝:\n" + subjects.toString());
}
/*用戶主動(dòng)調(diào)用,默認(rèn)是不需要覆寫該方法*/
@Override
public void onError(Throwable e) {
super.onError(e);
tvMsg.setText("失敗:\n" + e.toString());
}
};
是不是很簡(jiǎn)單初家?你可能說(shuō)這還簡(jiǎn)單,好咱們對(duì)比一下正常使用Retrofit的方法
/**
* Retrofit加入rxjava實(shí)現(xiàn)http請(qǐng)求
*/
private void onButton9Click() {
//手動(dòng)創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間
okhttp3.OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(5, TimeUnit.SECONDS);
Retrofit retrofit = new Retrofit.Builder()
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(HttpManager.BASE_URL)
.build();
/ 加載框
final ProgressDialog pd = new ProgressDialog(this);
HttpService apiService = retrofit.create(HttpService.class);
Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);
observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<RetrofitEntity>() {
@Override
public void onCompleted() {
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
}
@Override
public void onError(Throwable e) {
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
}
@Override
public void onNext(RetrofitEntity retrofitEntity) {
tvMsg.setText("無(wú)封裝:\n" + retrofitEntity.getData().toString());
}
@Override
public void onStart() {
super.onStart();
pd.show();
}
}
);
}
可能你發(fā)現(xiàn)確是代碼有點(diǎn)多乌助,但是更加可怕的是溜在,如果你一個(gè)activity或者fragment中多次需要http請(qǐng)求,你需要多次重復(fù)的寫回調(diào)處理(一個(gè)回到就有4個(gè)方法呀K小R蠢摺!赏参!反正我是忍受不了)志笼,而且以上處理還沒(méi)有做過(guò)多的判斷和錯(cuò)誤校驗(yàn)就如此復(fù)雜沿盅!~好了介紹完了,開始咱們的優(yōu)化之路吧籽腕!
項(xiàng)目結(jié)構(gòu):
RxJava
如果你對(duì)RxJava不了解嗡呼,好吧騷年趕快學(xué)學(xué)吧,不然真會(huì)out了,下面給出博主當(dāng)初學(xué)習(xí)RxJava的一些資源:
Retrofit
咱家今天的主角來(lái)了皇耗,咱們也深入淺出一下了解下Retrofit使用南窗,前方高能,如果你是深度Retrofit選手請(qǐng)直接跳過(guò)本節(jié)@陕ァM蛏恕!
1.首先確保在AndroidManifest.xml中請(qǐng)求了網(wǎng)絡(luò)權(quán)限
<uses-permission android:name="android.permission.INTERNET"/>
2.在app/build.gradle添加引用
/*rx-android-java*/
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.trello:rxlifecycle:1.0'
compile 'com.trello:rxlifecycle-components:1.0'
/*rotrofit*/
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.google.code.gson:gson:2.8.0'
3.常用注解
這里介紹一些常用的注解的使用
-
@Query
呜袁、@QueryMap
:用于Http Get
請(qǐng)求傳遞參數(shù) -
@Field
:用于Post方式傳遞參數(shù),需要在請(qǐng)求接口方法上添加@FormUrlEncoded
,即以表單的方式傳遞參數(shù) -
@Body
:用于Post
,根據(jù)轉(zhuǎn)換方式將實(shí)例對(duì)象轉(zhuǎn)化為對(duì)應(yīng)字符串傳遞參數(shù).比如Retrofit
添加GsonConverterFactory
則是將body
轉(zhuǎn)化為gson
字符串進(jìn)行傳遞 -
@Path
:用于URL
上占位符 -
@Part
:配合@Multipart
使用,一般用于文件上傳 -
@Header
:添加http header
-
@Headers
:跟@Header
作用一樣,只是使用方式不一樣,@Header
是作為請(qǐng)求方法的參數(shù)傳入,@Headers
是以固定方式直接添加到請(qǐng)求方法上
ReTrofit基本使用:
首先給定一個(gè)測(cè)試接口文檔敌买,后面的博客中我們都是用這個(gè)接口調(diào)試
/**
* @api videoLink 50音圖視頻鏈接
* @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink
* @method post
* @param once_no bool(選填,ture無(wú)鏈接) 一次性獲取下載地址
* @return json array(
* ret:1成功,2失敗
* msg:信息
* data:{
* name:視頻名稱
* title:標(biāo)題
* }
*)
**/
1.初始化retrofit
要向一個(gè)api發(fā)送我們的網(wǎng)絡(luò)請(qǐng)求 ,我們需要使用Retrofit builder類并指定service的base URL(通常情況下就是域名)阶界。
String BASE_URL = " http://www.izaodao.com/Api/"
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
2.設(shè)置接口service
注意到每個(gè)endpoint
都指定了一個(gè)關(guān)于HTTP(GET, POST, 等等虹钮。) 方法的注解以及用于分發(fā)網(wǎng)絡(luò)調(diào)用的方法。而且這些方法的參數(shù)也可以有特殊的注解膘融。
/**
* 接口地址
* Created by WZG on 2016/7/16.
*/
public interface MyApiEndpointInterface {
@POST("AppFiftyToneGraph/videoLink")
Call<RetrofitEntity> getAllVedio(@Body boolean once_no)
}
3.得到call然后同步處理處理回調(diào):
MyApiEndpointInterface apiService = retrofit.create(MyApiEndpointInterface.class);
Call<RetrofitEntity> call = apiService.getAllVedio(true);
call.enqueue(new Callback<RetrofitEntity>() {
@Override
public void onResponse(Response<RetrofitEntity> response, Retrofit retrofit) {
RetrofitEntity entity = response.body();
Log.i("tag", "onResponse----->" + entity.getMsg());
}
@Override
public void onFailure(Throwable t) {
Log.i("tag", "onFailure----->" + t.toString());
}
});
這就是簡(jiǎn)單的Retrofit使用步驟芙粱,接下來(lái)我們結(jié)合RxJava講述
ReTrofit+Rxjava基本使用
對(duì)比之前的Retrofit使用
1.在于我們需要修改service接口返回信息我們需要返回一個(gè)Observable對(duì)象
@POST("AppFiftyToneGraph/videoLink")
Observable<RetrofitEntity> getAllVedioBy(@Body boolean once_no);
2.然后初始化Retrofit需要添加對(duì)Rxjava的適配,注意一定要retrofit2才有這個(gè)功能哦
Retrofit retrofit = new Retrofit.Builder()
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(HttpManager.BASE_URL)
.build();
3.回調(diào)通過(guò)RxJava處理
HttpService apiService = retrofit.create(HttpService.class);
Observable<RetrofitEntity> observable = apiService.getAllVedioBy(true);
observable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Subscriber<RetrofitEntity>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(RetrofitEntity retrofitEntity) {
tvMsg.setText("無(wú)封裝:\n" + retrofitEntity.getData().toString());
}
}
);
簡(jiǎn)單的RxJava集合Retrofit的使用就介紹完了氧映,同樣的可以發(fā)現(xiàn)使用起來(lái)很多重復(fù)性的代碼春畔,而且使用也不是那么簡(jiǎn)單,所以才有了下面的封裝
ReTrofit+Rxjava進(jìn)階封裝之路
先來(lái)一張流程圖壓壓驚
請(qǐng)求數(shù)據(jù)封裝
1.參數(shù)
首先需要封裝的使我們的數(shù)據(jù)類岛都,在數(shù)據(jù)類中需要封裝請(qǐng)求中用到的相關(guān)數(shù)據(jù)的設(shè)置律姨,比如請(qǐng)求參數(shù)、方法臼疫、加載框顯示設(shè)置等等
public abstract class BaseApi<T> implements Func1<BaseResultEntity<T>, T> {
//rx生命周期管理
private SoftReference<RxAppCompatActivity> rxAppCompatActivity;
/*回調(diào)*/
private SoftReference<HttpOnNextListener> listener;
/*是否能取消加載框*/
private boolean cancel;
/*是否顯示加載框*/
private boolean showProgress;
/*是否需要緩存處理*/
private boolean cache;
/*基礎(chǔ)url*/
private String baseUrl="http://www.izaodao.com/Api/";
/*方法-如果需要緩存必須設(shè)置這個(gè)參數(shù)择份;不需要不用設(shè)置*/
private String mothed;
/*超時(shí)時(shí)間-默認(rèn)6秒*/
private int connectionTime = 6;
/*有網(wǎng)情況下的本地緩存時(shí)間默認(rèn)60秒*/
private int cookieNetWorkTime=60;
/*無(wú)網(wǎng)絡(luò)的情況下本地緩存時(shí)間默認(rèn)30天*/
private int cookieNoNetWorkTime=24*60*60*30;
}
注釋很詳細(xì),這里不具體描述了烫堤,由于這里是最后封裝完成以后的代碼缓淹,所以有些內(nèi)容本章還會(huì)部分不會(huì)涉及,因?yàn)楣δ芴嗨樱€是按照一開始的博客章節(jié)講解。
2.抽象api接口
/**
* 設(shè)置參數(shù)
*
* @param retrofit
* @return
*/
public abstract Observable getObservable(Retrofit retrofit);
通過(guò)子類也即是我們的具體api接口料仗,通過(guò)getObservable實(shí)現(xiàn)service中定義的接口方法湾盗,例如:
public class SubjectPostApi extends BaseApi {
xxxxxxx
xxxxxxx
@Override
public Observable getObservable(Retrofit retrofit) {
HttpPostService service = retrofit.create(HttpPostService.class);
return service.getAllVedioBys(isAll());
}
}
通過(guò)傳入的Retrofit對(duì)象,可以隨意切換挑選Service對(duì)象立轧,得到定義的注解方法格粪,初始完成以后返回Observable對(duì)象躏吊。
3.結(jié)果判斷
這里結(jié)合RxJava的map方法在服務(wù)器返回?cái)?shù)據(jù)中,統(tǒng)一處理數(shù)據(jù)處理帐萎,所以BaseApi<T> implements Func1<BaseResultEntity<T>, T>比伏,后邊結(jié)合結(jié)果處理鏈接起來(lái)使用
@Override
public T call(BaseResultEntity<T> httpResult) {
if (httpResult.getRet() == 0) {
throw new HttpTimeException(httpResult.getMsg());
}
return httpResult.getData();
}
由于測(cè)試接口,也是當(dāng)前我們公司接口都是有統(tǒng)一規(guī)則的疆导,想必大家都有這樣的接口規(guī)則赁项,所以才有這里的統(tǒng)一判斷,規(guī)則如下:
* ret:1成功,2失敗
* msg:信息
* data:{
* name:視頻名稱
* title:標(biāo)題
* }
其實(shí)上面的接口文檔中就介紹了,統(tǒng)一先通過(guò)ret
判斷澈段,失敗顯示msg
信息,data
是成功后的數(shù)據(jù)也就是用戶關(guān)心的數(shù)據(jù)悠菜,所以可封裝一個(gè)結(jié)果對(duì)象BaseResultEntity
.
4.結(jié)果數(shù)據(jù)
/**
* 回調(diào)信息統(tǒng)一封裝類
* Created by WZG on 2016/7/16.
*/
public class BaseResultEntity<T> {
// 判斷標(biāo)示
private int ret;
// 提示信息
private String msg;
//顯示數(shù)據(jù)(用戶需要關(guān)心的數(shù)據(jù))
private T data;
xxxxx get-set xxxxx
}
這里結(jié)合BaseApi的Func1判斷,失敗直接拋出一個(gè)異常败富,交個(gè)RxJava的onError處理悔醋,成功則將用戶關(guān)心的數(shù)據(jù)傳給Gson解析返回
5.泛型傳遞
BaseResultEntity<T>
中的泛型T也就是我們所關(guān)心的回調(diào)數(shù)據(jù),同樣也是Gson最后解析返回的數(shù)據(jù)兽叮,傳遞的過(guò)程根節(jié)點(diǎn)是通過(guò)定義service方法是給定的芬骄,例如:
public interface HttpPostService {
@POST("AppFiftyToneGraph/videoLink")
Call<RetrofitEntity> getAllVedio(@Body boolean once_no);
}
其中的RetrofitEntity就是用戶關(guān)心的數(shù)據(jù)類,通過(guò)泛型傳遞給最后的接口鹦聪。
6.強(qiáng)調(diào)
很多兄弟通過(guò)QQ群反饋給我說(shuō)账阻,使用一個(gè)接口需要寫一個(gè)對(duì)應(yīng)的api類繼承BaseApi是不是很麻煩,我這里強(qiáng)調(diào)一下椎麦,這樣封裝是為了將一個(gè)Api接口作為一個(gè)對(duì)象去封裝宰僧,個(gè)人覺(jué)得有必要封裝成一個(gè)類,在日后工程日益增加接口隨著增加的同時(shí)观挎,對(duì)象的做法更加有利于查找接口和修改接口有利于迭代琴儿。
操作類封裝
1初始對(duì)象
首先初始化一個(gè)單利方便HttpManager請(qǐng)求;這里用了volatile的對(duì)象嘁捷,不懂的同學(xué)可以參考我的另一篇博客
private volatile static HttpManager INSTANCE;
//構(gòu)造方法私有
private HttpManager() {
}
//獲取單例
public static HttpManager getInstance() {
if (INSTANCE == null) {
synchronized (HttpManager.class) {
if (INSTANCE == null) {
INSTANCE = new HttpManager();
}
}
}
return INSTANCE;
}
2接口處理和回調(diào)處理:
/**
* 處理http請(qǐng)求
*
* @param basePar 封裝的請(qǐng)求數(shù)據(jù)
*/
public void doHttpDeal(BaseApi basePar) {
//手動(dòng)創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間緩存等設(shè)置
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(basePar.getConnectionTime(), TimeUnit.SECONDS);
builder.addInterceptor(new CookieInterceptor(basePar.isCache()));
/*創(chuàng)建retrofit對(duì)象*/
Retrofit retrofit = new Retrofit.Builder()
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(basePar.getBaseUrl())
.build();
/*rx處理*/
ProgressSubscriber subscriber = new ProgressSubscriber(basePar);
Observable observable = basePar.getObservable(retrofit)
/*失敗后的retry配置*/
.retryWhen(new RetryWhenNetworkException())
/*生命周期管理*/
.compose(basePar.getRxAppCompatActivity().bindToLifecycle())
/*http請(qǐng)求線程*/
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
/*回調(diào)線程*/
.observeOn(AndroidSchedulers.mainThread())
/*結(jié)果判斷*/
.map(basePar);
/*數(shù)據(jù)回調(diào)*/
observable.subscribe(subscriber);
}
首先通過(guò)api接口類BaseApi的實(shí)現(xiàn)類中數(shù)據(jù)初始化OkHttpClient和Retrofit對(duì)象,其中包含了url雄嚣,超時(shí)等晒屎,接著通過(guò)BaseApi的抽象方法getObservable得到Observable對(duì)象,得到Observable對(duì)象以后缓升,我們就能隨意的切換現(xiàn)成來(lái)處理鼓鲁,整個(gè)請(qǐng)求通過(guò)compose設(shè)定的rxlifecycle來(lái)管理生命周期,所以不會(huì)溢出和泄露無(wú)需任何擔(dān)心港谊,最后再服務(wù)器數(shù)據(jù)返回時(shí)骇吭,通過(guò)map判斷結(jié)果,剔除錯(cuò)誤信息歧寺,成功以后返回到自定義的ProgressSubscriber對(duì)象中燥狰,所以接下來(lái)封裝ProgressSubscriber對(duì)象棘脐。
ProgressSubscriber
封裝
- onStart():開始
- onCompleted():結(jié)束
- onError(Throwable e):錯(cuò)誤
- onNext(T t):成功
1.請(qǐng)求加載框
http請(qǐng)求都伴隨著加載框的使用,所以這里需要在onStart()使用前初始一個(gè)加載框龙致,這里簡(jiǎn)單的用ProgressDialog代替
/**
* 用于在Http請(qǐng)求開始時(shí)蛀缝,自動(dòng)顯示一個(gè)ProgressDialog
* 在Http請(qǐng)求結(jié)束是,關(guān)閉ProgressDialog
* 調(diào)用者自己對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行處理
* Created by WZG on 2016/7/16.
*/
public class ProgressSubscriber<T> extends Subscriber<T> {
/*是否彈框*/
private boolean showPorgress = true;
/* 軟引用回調(diào)接口*/
private SoftReference<HttpOnNextListener> mSubscriberOnNextListener;
/*軟引用反正內(nèi)存泄露*/
private SoftReference<RxAppCompatActivity> mActivity;
/*加載框可自己定義*/
private ProgressDialog pd;
/*請(qǐng)求數(shù)據(jù)*/
private BaseApi api;
/**
* 構(gòu)造
*
* @param api
*/
public ProgressSubscriber(BaseApi api) {
this.api = api;
this.mSubscriberOnNextListener = api.getListener();
this.mActivity = new SoftReference<>(api.getRxAppCompatActivity());
setShowPorgress(api.isShowProgress());
if (api.isShowProgress()) {
initProgressDialog(api.isCancel());
}
}
/**
* 初始化加載框
*/
private void initProgressDialog(boolean cancel) {
Context context = mActivity.get();
if (pd == null && context != null) {
pd = new ProgressDialog(context);
pd.setCancelable(cancel);
if (cancel) {
pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
onCancelProgress();
}
});
}
}
}
/**
* 顯示加載框
*/
private void showProgressDialog() {
if (!isShowPorgress()) return;
Context context = mActivity.get();
if (pd == null || context == null) return;
if (!pd.isShowing()) {
pd.show();
}
}
/**
* 隱藏
*/
private void dismissProgressDialog() {
if (!isShowPorgress()) return;
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
}
}
由于progress
的特殊性目代,需要指定content
而且不能是Application
所以這里傳遞一個(gè)RxAppCompatActivity
,而同時(shí)上面的HttpManager同樣需要屈梁,所以這里統(tǒng)一還是按照BaseApi傳遞過(guò)來(lái),使用軟引用的方式避免泄露像啼。剩下的無(wú)非是初始化俘闯,顯示和關(guān)閉方法,可以詳細(xì)看代碼忽冻。
2.onStart()
實(shí)現(xiàn)
在onStart()中需要調(diào)用加載框真朗,然后這里還有網(wǎng)絡(luò)緩存的邏輯,后面會(huì)單獨(dú)講解僧诚,現(xiàn)在先忽略它的存在遮婶。
/**
* 訂閱開始時(shí)調(diào)用
* 顯示ProgressDialog
*/
@Override
public void onStart() {
showProgressDialog();
/*緩存并且有網(wǎng)*/
if (api.isCache() && AppUtil.isNetworkAvailable(RxRetrofitApp.getApplication())) {
/*獲取緩存數(shù)據(jù)*/
CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(api.getUrl());
if (cookieResulte != null) {
long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
if (time < api.getCookieNetWorkTime()) {
if (mSubscriberOnNextListener.get() != null) {
mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
}
onCompleted();
unsubscribe();
}
}
}
}
3.onCompleted()
實(shí)現(xiàn)
/**
* 完成,隱藏ProgressDialog
*/
@Override
public void onCompleted() {
dismissProgressDialog();
}
4.onError(Throwable e)
實(shí)現(xiàn)
在onError(Throwable e)是對(duì)錯(cuò)誤信息的處理和緩存讀取的處理湖笨,后續(xù)會(huì)講解旗扑,先忽略。
/**
* 對(duì)錯(cuò)誤進(jìn)行統(tǒng)一處理
* 隱藏ProgressDialog
*
* @param e
*/
@Override
public void onError(Throwable e) {
dismissProgressDialog();
/*需要緩存并且本地有緩存才返回*/
if (api.isCache()) {
Observable.just(api.getUrl()).subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
errorDo(e);
}
@Override
public void onNext(String s) {
/*獲取緩存數(shù)據(jù)*/
CookieResulte cookieResulte = CookieDbUtil.getInstance().queryCookieBy(s);
if (cookieResulte == null) {
throw new HttpTimeException("網(wǎng)絡(luò)錯(cuò)誤");
}
long time = (System.currentTimeMillis() - cookieResulte.getTime()) / 1000;
if (time < api.getCookieNoNetWorkTime()) {
if (mSubscriberOnNextListener.get() != null) {
mSubscriberOnNextListener.get().onCacheNext(cookieResulte.getResulte());
}
} else {
CookieDbUtil.getInstance().deleteCookie(cookieResulte);
throw new HttpTimeException("網(wǎng)絡(luò)錯(cuò)誤");
}
}
});
} else {
errorDo(e);
}
}
/*錯(cuò)誤統(tǒng)一處理*/
private void errorDo(Throwable e) {
Context context = mActivity.get();
if (context == null) return;
if (e instanceof SocketTimeoutException) {
Toast.makeText(context, "網(wǎng)絡(luò)中斷慈省,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
} else if (e instanceof ConnectException) {
Toast.makeText(context, "網(wǎng)絡(luò)中斷臀防,請(qǐng)檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "錯(cuò)誤" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
if (mSubscriberOnNextListener.get() != null) {
mSubscriberOnNextListener.get().onError(e);
}
}
5.onNext(T t)
實(shí)現(xiàn)
/**
* 將onNext方法中的返回結(jié)果交給Activity或Fragment自己處理
*
* @param t 創(chuàng)建Subscriber時(shí)的泛型類型
*/
@Override
public void onNext(T t) {
if (mSubscriberOnNextListener.get() != null) {
mSubscriberOnNextListener.get().onNext(t);
}
}
主要是是將得到的結(jié)果,通過(guò)自定義的接口返回給view界面边败,其中的軟引用對(duì)象mSubscriberOnNextListener是自定義的接口回調(diào)類HttpOnNextListener.
6.HttpOnNextListener
封裝
現(xiàn)在只需關(guān)心onNext(T t)
和onError(Throwable e)
接口即可袱衷,回調(diào)的觸發(fā)點(diǎn)都是在上面的ProgressSubscriber
中調(diào)用
/**
* 成功回調(diào)處理
* Created by WZG on 2016/7/16.
*/
public abstract class HttpOnNextListener<T> {
/**
* 成功后回調(diào)方法
* @param t
*/
public abstract void onNext(T t);
/**
* 緩存回調(diào)結(jié)果
* @param string
*/
public void onCacheNext(String string){
}
/**
* 失敗或者錯(cuò)誤方法
* 主動(dòng)調(diào)用,更加靈活
* @param e
*/
public void onError(Throwable e){
}
/**
* 取消回調(diào)
*/
public void onCancel(){
}
}
失敗后的retry處理
這里你可能會(huì)問(wèn)笑窜,Retrofit有自帶的retry處理呀致燥,的確Retrofit有自帶的retry處理,但是有很多的局限排截,先看下使用
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.retryOnConnectionFailure(true);
使用起來(lái)還是很方便嫌蚤,只需要調(diào)用一個(gè)方法即可,但是它是不可控的断傲,也就是沒(méi)有辦法設(shè)置retry時(shí)間次數(shù)脱吱,所以不太靈活,既然如此還不如自己封裝一下认罩,因?yàn)橛肦xJava實(shí)現(xiàn)這個(gè)簡(jiǎn)直小菜箱蝠,無(wú)形中好像已經(jīng)給RxJava打了廣告,中毒太深。
很簡(jiǎn)單直接上代碼:
/**
* retry條件
* Created by WZG on 2016/10/17.
*/
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
// retry次數(shù)
private int count = 3;
// 延遲
private long delay = 3000;
// 疊加延遲
private long increaseDelay = 3000;
public RetryWhenNetworkException() {
}
public RetryWhenNetworkException(int count, long delay) {
this.count = count;
this.delay = delay;
}
public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
this.count = count;
this.delay = delay;
this.increaseDelay = increaseDelay;
}
@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
return observable
.zipWith(Observable.range(1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
@Override
public Wrapper call(Throwable throwable, Integer integer) {
return new Wrapper(throwable, integer);
}
}).flatMap(new Func1<Wrapper, Observable<?>>() {
@Override
public Observable<?> call(Wrapper wrapper) {
if ((wrapper.throwable instanceof ConnectException
|| wrapper.throwable instanceof SocketTimeoutException
|| wrapper.throwable instanceof TimeoutException)
&& wrapper.index < count + 1) { //如果超出重試次數(shù)也拋出錯(cuò)誤抡锈,否則默認(rèn)是會(huì)進(jìn)入onCompleted
return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);
}
return Observable.error(wrapper.throwable);
}
});
}
private class Wrapper {
private int index;
private Throwable throwable;
public Wrapper(Throwable throwable, int index) {
this.index = index;
this.throwable = throwable;
}
}
}
使用
到這里,我們第一步封裝已經(jīng)完成了乔外,下面講解下如何使用床三,已經(jīng)看明白的各位看官,估計(jì)早就看明白了使用方式杨幼,無(wú)非是創(chuàng)建一個(gè)api對(duì)象繼承BaseApi初始接口信息撇簿,然后調(diào)用HttpManager對(duì)象的doHttpDeal(BaseApi basePar)方法,最后靜靜的等待回調(diào)類HttpOnNextListener<T>類返回的onNext(T t)成功數(shù)據(jù)或者onError(Throwable e)數(shù)據(jù)差购。
其實(shí)代碼就是這樣:
api
接口對(duì)象
/**
* 測(cè)試數(shù)據(jù)
* Created by WZG on 2016/7/16.
*/
public class SubjectPostApi extends BaseApi {
// 接口需要傳入的參數(shù) 可自定義不同類型
private boolean all;
/*任何你先要傳遞的參數(shù)*/
// String xxxxx;
/**
* 默認(rèn)初始化需要給定回調(diào)和rx周期類
* 可以額外設(shè)置請(qǐng)求設(shè)置加載框顯示四瘫,回調(diào)等(可擴(kuò)展)
* @param listener
* @param rxAppCompatActivity
*/
public SubjectPostApi(HttpOnNextListener listener, RxAppCompatActivity rxAppCompatActivity) {
super(listener,rxAppCompatActivity);
setShowProgress(true);
setCancel(true);
setCache(true);
setMothed("AppFiftyToneGraph/videoLink");
setCookieNetWorkTime(60);
setCookieNoNetWorkTime(24*60*60);
}
public boolean isAll() {
return all;
}
public void setAll(boolean all) {
this.all = all;
}
@Override
public Observable getObservable(Retrofit retrofit) {
HttpPostService service = retrofit.create(HttpPostService.class);
return service.getAllVedioBys(isAll());
}
}
請(qǐng)求回調(diào)
// 完美封裝簡(jiǎn)化版
private void simpleDo() {
SubjectPostApi postEntity = new SubjectPostApi(simpleOnNextListener,this);
postEntity.setAll(true);
HttpManager manager = HttpManager.getInstance();
manager.doHttpDeal(postEntity);
}
// 回調(diào)一一對(duì)應(yīng)
HttpOnNextListener simpleOnNextListener = new HttpOnNextListener<List<SubjectResulte>>() {
@Override
public void onNext(List<SubjectResulte> subjects) {
tvMsg.setText("網(wǎng)絡(luò)返回:\n" + subjects.toString());
}
@Override
public void onCacheNext(String cache) {
/*緩存回調(diào)*/
Gson gson=new Gson();
java.lang.reflect.Type type = new TypeToken<BaseResultEntity<List<SubjectResulte>>>() {}.getType();
BaseResultEntity resultEntity= gson.fromJson(cache, type);
tvMsg.setText("緩存返回:\n"+resultEntity.getData().toString() );
}
/*用戶主動(dòng)調(diào)用,默認(rèn)是不需要覆寫該方法*/
@Override
public void onError(Throwable e) {
super.onError(e);
tvMsg.setText("失斢印:\n" + e.toString());
}
/*用戶主動(dòng)調(diào)用找蜜,默認(rèn)是不需要覆寫該方法*/
@Override
public void onCancel() {
super.onCancel();
tvMsg.setText("取消請(qǐng)求");
}
};
后續(xù)
到這里,封裝功能中很多功能還沒(méi)涉及和講解稳析,后續(xù)會(huì)陸續(xù)更新洗做!
先給大家看看為師的完全體功能:
1.Retrofit+Rxjava+okhttp基本使用方法
2.統(tǒng)一處理請(qǐng)求數(shù)據(jù)格式
3.統(tǒng)一的ProgressDialog和回調(diào)Subscriber處理
4.取消http請(qǐng)求
5.預(yù)處理http請(qǐng)求
6.返回?cái)?shù)據(jù)的統(tǒng)一判斷
7.失敗后的retry處理
8.RxLifecycle管理生命周期,防止泄露
9.文件上傳下載(支持多文件彰居,斷點(diǎn)續(xù)傳)
10.Cache數(shù)據(jù)持久化和數(shù)據(jù)庫(kù)(greenDao)兩種緩存機(jī)制
11.異常統(tǒng)一處理
來(lái)個(gè)圖壓壓驚:
迫不及待的小伙伴可以看這里:
RxJava+Retrofit+OkHttp深入淺出-終極封裝專欄
源碼:
RxRetrofit-終極封裝-深入淺出&網(wǎng)絡(luò)請(qǐng)求-GitHub
其實(shí)我還有一個(gè)兄弟版本-傳送門
我會(huì)告訴你其實(shí)我還有個(gè)更加簡(jiǎn)單的版本