深入淺出RxJava+Retrofit+OkHttp網(wǎng)絡(luò)請(qǐng)求
淺談RxJava+Retrofit+OkHttp 封裝使用 之前發(fā)出后收到很多朋友的關(guān)注,原本只是自己學(xué)習(xí)后的一些經(jīng)驗(yàn)總結(jié),但是有同學(xué)運(yùn)用到實(shí)戰(zhàn)當(dāng)中,這讓我很惶恐,所有后續(xù)一直更新了很多次版本,有些地方難免有所變動(dòng)導(dǎo)致之前的博客有所出入,正好最近受到掘金邀請(qǐng)內(nèi)測博客,所以決定重新寫一版,按照最后迭代完成的封裝詳細(xì)的講述一遍,歡迎大家關(guān)注!
注意:由于本章的特殊性,后續(xù)文章比較長而且復(fù)雜,涉及內(nèi)容也很多,所以大家準(zhǔn)備好茶水,前方高能預(yù)警。
簡介:
Retrofit: Retrofit是Square 公司開發(fā)的一款正對(duì)Android 網(wǎng)絡(luò)請(qǐng)求的框架。底層基于OkHttp 實(shí)現(xiàn),OkHttp 已經(jīng)得到了google 官方的認(rèn)可。
OkHttp: 也是Square 開源的網(wǎng)絡(luò)請(qǐng)求庫
RxJava:RxJava 在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個(gè)在 Java VM 上使用可觀測的序列來組成異步的、基于事件的程序的庫)。這就是 RxJava ,概括得非常精準(zhǔn)??傊褪亲尞惒讲僮髯兊梅浅:唵?。
各自的職責(zé):Retrofit 負(fù)責(zé)請(qǐng)求的數(shù)據(jù)和請(qǐng)求的結(jié)果,使用接口的方式呈現(xiàn),OkHttp 負(fù)責(zé)請(qǐng)求的過程,RxJava 負(fù)責(zé)異步,各種線程之間的切換。
RxJava + Retrofit + okHttp 已成為當(dāng)前Android 網(wǎng)絡(luò)請(qǐng)求最流行的方式。
封裝成果
封裝完以后,具有如下功能:
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管理生命周期,防止泄露
實(shí)現(xiàn)效果:
具體使用
封裝后http請(qǐng)求代碼如下
// 完美封裝簡化版 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()); } };
是不是很簡單?你可能說這還簡單,好咱們對(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("無封裝:\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è)方法呀?。。?!反正我是忍受不了),而且以上處理還沒有做過多的判斷和錯(cuò)誤校驗(yàn)就如此復(fù)雜!~好了介紹完了,開始咱們的優(yōu)化之路吧!
項(xiàng)目結(jié)構(gòu):
Retrofit
咱家今天的主角來了,咱們也深入淺出一下了解下Retrofit使用,前方高能,如果你是深度Retrofit選手請(qǐng)直接跳過本節(jié)?。?!
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è)測試接口文檔,后面的博客中我們都是用這個(gè)接口調(diào)試
/** * @api videoLink 50音圖視頻鏈接 * @url http://www.izaodao.com/Api/AppFiftyToneGraph/videoLink * @method post * @param once_no bool(選填,ture無鏈接) 一次性獲取下載地址 * @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()); } });
這就是簡單的Retrofit使用步驟,接下來我們結(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)通過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("無封裝:\n" + retrofitEntity.getData().toString()); } } );
簡單的RxJava集合Retrofit的使用就介紹完了,同樣的可以發(fā)現(xiàn)使用起來很多重復(fù)性的代碼,而且使用也不是那么簡單,所以才有了下面的封裝
ReTrofit+Rxjava進(jìn)階封裝之路
先來一張流程圖壓壓驚
請(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ǎ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);
通過子類也即是我們的具體api接口,通過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()); } }
通過傳入的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é)果處理鏈接起來使用 @Override public T call(BaseResultEntity<T> httpResult) { if (httpResult.getRet() == 0) { throw new HttpTimeException(httpResult.getMsg()); } return httpResult.getData(); }
由于測試接口,也是當(dāng)前我們公司接口都是有統(tǒng)一規(guī)則的,想必大家都有這樣的接口規(guī)則,所以才有這里的統(tǒng)一判斷,規(guī)則如下:
* ret:1成功,2失敗 * msg:信息 * data:{ * name:視頻名稱 * title:標(biāo)題 * }
其實(shí)上面的接口文檔中就介紹了,統(tǒng)一先通過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ù),傳遞的過程根節(jié)點(diǎn)是通過定義service方法是給定的,例如:
public interface HttpPostService { @POST("AppFiftyToneGraph/videoLink") Call<RetrofitEntity> getAllVedio(@Body boolean once_no); }
其中的RetrofitEntity就是用戶關(guān)心的數(shù)據(jù)類,通過泛型傳遞給最后的接口。
6.強(qiáng)調(diào)
很多兄弟通過QQ群反饋給我說,使用一個(gè)接口需要寫一個(gè)對(duì)應(yīng)的api類繼承BaseApi是不是很麻煩,我這里強(qiáng)調(diào)一下,這樣封裝是為了將一個(gè)Api接口作為一個(gè)對(duì)象去封裝,個(gè)人覺得有必要封裝成一個(gè)類,在日后工程日益增加接口隨著增加的同時(shí),對(duì)象的做法更加有利于查找接口和修改接口有利于迭代。
操作類封裝
1初始對(duì)象
首先初始化一個(gè)單利方便HttpManager請(qǐng)求;這里用了volatile的對(duì)象
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); }
首先通過api接口類BaseApi的實(shí)現(xiàn)類中數(shù)據(jù)初始化OkHttpClient和Retrofit對(duì)象,其中包含了url,超時(shí)等,接著通過BaseApi的抽象方法getObservable得到Observable對(duì)象,得到Observable對(duì)象以后,我們就能隨意的切換現(xiàn)成來處理,整個(gè)請(qǐng)求通過compose設(shè)定的rxlifecycle來管理生命周期,所以不會(huì)溢出和泄露無需任何擔(dān)心,最后再服務(wù)器數(shù)據(jù)返回時(shí),通過map判斷結(jié)果,剔除錯(cuò)誤信息,成功以后返回到自定義的ProgressSubscriber對(duì)象中,所以接下來封裝ProgressSubscriber對(duì)象。
ProgressSubscriber封裝
ProgressSubscriber其實(shí)是繼承于Subscriber,封裝的方法無非是對(duì)Subscriber的回調(diào)方法的封裝
- onStart():開始
- onCompleted():結(jié)束
- onError(Throwable e):錯(cuò)誤
- onNext(T t):成功
1.請(qǐng)求加載框
http請(qǐng)求都伴隨著加載框的使用,所以這里需要在onStart()使用前初始一個(gè)加載框,這里簡單的用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ā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é)果,通過自定義的接口返回給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ì)問,Retrofit有自帶的retry處理呀,的確Retrofit有自帶的retry處理,但是有很多的局限,先看下使用
OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.retryOnConnectionFailure(true);
使用起來還是很方便,只需要調(diào)用一個(gè)方法即可,但是它是不可控的,也就是沒有辦法設(shè)置retry時(shí)間次數(shù),所以不太靈活,既然如此還不如自己封裝一下,因?yàn)橛肦xJava實(shí)現(xiàn)這個(gè)簡直小菜,無形中好像已經(jīng)給RxJava打了廣告,中毒太深。
很簡單直接上代碼:
/** * 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ì)早就看明白了使用方式,無非是創(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ì)象
/** * 測試數(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)
// 完美封裝簡化版 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ù)
到這里,封裝功能中很多功能還沒涉及和講解,后續(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ù)庫(greenDao)兩種緩存機(jī)制
11.異常統(tǒng)一處理
來個(gè)圖壓壓驚:
源碼:
RxRetrofit-終極封裝-深入淺出&網(wǎng)絡(luò)請(qǐng)求-GitHub
我不會(huì)告訴你其實(shí)我還有個(gè)更加簡單的版本
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android如何動(dòng)態(tài)調(diào)整應(yīng)用字體大小詳解
這篇文章主要給大家介紹了關(guān)于Android如何動(dòng)態(tài)調(diào)整應(yīng)用字體大小的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05Android實(shí)現(xiàn)收到新短信后自動(dòng)發(fā)郵件功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)收到新短信后自動(dòng)發(fā)郵件功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Android系統(tǒng)的五種數(shù)據(jù)存儲(chǔ)形式實(shí)例(二)
Android系統(tǒng)有五種數(shù)據(jù)存儲(chǔ)形式,分別是文件存儲(chǔ)、SP存儲(chǔ)、數(shù)據(jù)庫存儲(chǔ)、contentprovider 內(nèi)容提供者、網(wǎng)絡(luò)存儲(chǔ)。本文介紹了Android系統(tǒng)的五種數(shù)據(jù)存儲(chǔ)形式,有興趣的可以了解一下。2016-12-12Android Canvas和Bitmap結(jié)合繪圖詳解流程
在 Android Canvas 上繪圖非常難,在繪圖時(shí)需要理解許多不同的類和概念。這篇文章中,將介紹 Android 框架中可用的一些類,它們可以讓畫布使用時(shí)更輕松2021-11-11Flutter如何輕松實(shí)現(xiàn)動(dòng)態(tài)更新ListView淺析
在Android中通常都會(huì)用到listview.那么flutter里面怎么用呢?下面這篇文章主要給大家介紹了關(guān)于Flutter如何輕松實(shí)現(xiàn)動(dòng)態(tài)更新ListView的相關(guān)資料,需要的朋友可以參考下2022-02-02Intent傳遞對(duì)象之Serializable和Parcelable的區(qū)別
Intent在不同的組件中傳遞對(duì)象數(shù)據(jù)的應(yīng)用非常普遍,大家都知道在intent傳遞對(duì)象的方法有兩種:1、實(shí)現(xiàn)Serializable接口、2、實(shí)現(xiàn)Parcelable接口,接下來通過本文給大家介紹Intent傳遞對(duì)象之Serializable和Parcelable的區(qū)別,感興趣的朋友一起學(xué)習(xí)吧2016-01-01android輸入框內(nèi)容改變的監(jiān)聽事件實(shí)例
下面小編就為大家分享一篇android輸入框內(nèi)容改變的監(jiān)聽事件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02Android Studio使用教程(一):下載與安裝及創(chuàng)建HelloWorld項(xiàng)目
這篇文章主要介紹了Android Studio使用教程(一):下載與安裝及創(chuàng)建HelloWorld項(xiàng)目,本文用詳細(xì)的圖文說明講解了Android Studio初步使用,需要的朋友可以參考下2015-05-05Android中使用RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
RecyclerView 是Android L版本中新添加的一個(gè)用來取代ListView的SDK,它的靈活性與可替代性比listview更好。這篇文章主要介紹了Android中使用RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載的相關(guān)資料,需要的朋友可以參考下2016-03-03Android編程中Handler原理及用法實(shí)例分析
這篇文章主要介紹了Android編程中Handler用法,結(jié)合實(shí)例形式分析了Handler的功能,原理及使用技巧,需要的朋友可以參考下2016-01-01