本篇文章主要介紹Rxjava與Retrofit結合使用,對Rxjava和Retrofit不熟悉的能夠去看我以前的兩篇介紹java
public interface GetRequest_Interface { @POST("/app/auth/school/list") Observable<School> postSchool(@Body RequestBody route);//根據學校名獲取學校 } 複製代碼
GetRequest_Interface request = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(GetRequest_Interface.class);
複製代碼
這裏以請求體爲Json 字符串爲準git
HashMap<String, Object> map = new HashMap<>(); map.put(key, value); Gson gson=new Gson(); String strEntity = gson.toJson(map); KLog.d("22222222RequestBody"+strEntity); RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json;charset=UTF-8"),strEntity); 複製代碼
Observable<School> observable = request.postSchool(body); observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<School>() { @Override public void onSubscribe(Disposable d) { //此處作一些請求開始時的初始化事件,例如彈出一個dialog } @Override public void onNext(School school) { //此到處理請求成功業務(code == 200 ) } @Override public void onError(Throwable e) { //此到處理請求失敗業務(code != 200 ) } @Override public void onComplete() { //請求完成處理業務,關閉請求隊列,關閉dialog等 } }); 複製代碼
至此,Rxjava 與 Retrofit 結合基本使用就結束了,基於以上就能夠愉快的完成網絡請求工做了,是否是很方便簡潔.編程
固然了,對於咱們的業務來講,不可能只有一次網絡請求,基本到處都須要進去網絡請求,並且也不可能如上面同樣,如此簡單. 通常咱們的業務中都須要配置一些其餘的參數信息,若是每一次網絡請求都像上面那樣寫的話,固然是能夠的,可是你的代碼就太low了,也不符合編程規範.json
因此基於你會熟練的使用了的前提下,咱們就須要將以上代碼進行簡單封裝.segmentfault
關於封裝我想多說一句api
對於封裝,不少人都認爲封裝就是使代碼足夠簡潔,邏輯足夠清晰,符合開閉原則等,的確是這樣的,可是須要使狀況而定,若是你寫的代碼是服務廣大人羣,也就是開源項目,那就要考慮不少因素,作到足夠開放.數組
但若是隻是用到本身的項目裏,那咱們必需要明確一點,那就是定製化的前提是符合本身業務的需求,而不要過於封裝.因此也就有爲何咱們經常須要對別人的開源框架作二次封裝,就是這個道理,沒有最好的封裝,只有最合適的封裝.安全
public interface API { //此處存放全部的BaseUrl String BASE_URL = "http://xx.xxx.xx.225:8080"; //核心後臺API String BASE_SCHOOL_URL = "http://xx.xxx.xx.225:8081"; //學校API } 複製代碼
public interface GetRequest_Interface { /*-------------------------------------全部網絡請求 API-------------------------------------------------------*/ @POST("/app/auth/captcha") Observable<Phone> postPhone(@Body RequestBody route); //獲取驗證碼 @POST("/app/auth/login") Observable<RegistLogin> postRegist(@Body RequestBody route);//登陸註冊 } 複製代碼
根據本身業務需求初始化OkHttpClient OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS) //l 鏈接超時時間 .writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS) //讀寫超時 .readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS) //讀取超時 .retryOnConnectionFailure(true) //失敗重連 .addNetworkInterceptor(tokenInterceptor) //添加網絡攔截器 .addInterceptor(tokenRespInterceptor) //.authenticator(authenticator) //受權認證 .build(); 複製代碼
這裏須要用到OkHttp3的攔截器相關內容,不熟悉的能夠先去了解bash
Interceptor tokenInterceptor = new Interceptor() { //全局攔截器, @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request();//獲取原始請求 Request.Builder requestBuilder = originalRequest.newBuilder() //創建新的請求 .addHeader("Accept", "application/json") .addHeader("Content-Type", "application/json; charset=utf-8") .removeHeader("User-Agent") .addHeader("User-Agent",BaseUtils.getUserAgent()) .method(originalRequest.method(),originalRequest.body()); return chain.proceed(requestBuilder.build()); //從新請求 複製代碼
Interceptor tokenInterceptor = new Interceptor() { //全局攔截器,往請求頭部添加 token 字段,實現了全局添加 token @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request();//獲取原始請求 Request.Builder requestBuilder = originalRequest.newBuilder() //創建新的請求 .addHeader("Accept", "application/json") .addHeader("Content-Type", "application/json; charset=utf-8") .removeHeader("User-Agent") .addHeader("User-Agent",BaseUtils.getUserAgent()) .method(originalRequest.method(),originalRequest.body()); // Log.e("----------------",originalRequest.body().toString()); Request tokenRequest = null; //帶有token的請求 if (StringUtils.isEmpty(App.mmkv.decodeString(BaseConfig.TOKEN,null))){ return chain.proceed(requestBuilder.build()); } tokenRequest = requestBuilder .header("Authorization","Bearer "+App.mmkv.decodeString(BaseConfig.TOKEN,null)) .build(); return chain.proceed(tokenRequest); } }; 複製代碼
這裏使用了騰訊的MMKV框架進去本地存儲Token,由於咱們一開始是沒有拿到Token的,因此須要進行動態添加markdown
在進行網絡交互的時候,服務端簽發的Token是有時效性的並且通常比較短,過了有效期就須要從新請求,而這個過程咱們不能讓用戶察覺到,因此須要實現用戶無感知的狀況下刷新請求新的Token.
Interceptor tokenRespInterceptor = new Interceptor() { //攔截返回體 判斷token是否過時.過時重寫拉取新的token @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); // KLog.d( response.body().string()); if (isTokenExpired(response)){ KLog.d( "自動刷新Token,而後從新請求數據"); //同步請求方式,獲取最新的Token String newToken = getNewToken(); if (newToken != null){ //使用新的Token,建立新的請求 Request newRequest = chain.request() .newBuilder() .header("Authorization", "Bearer " + newToken) .build(); //從新請求 return chain.proceed(newRequest); } } return response.newBuilder().body(ResponseBody.create(MediaType.parse("UTF-8"),body)).build(); } }; 複製代碼
這裏須要根據服務端約定好的過時規則進去判斷,這裏簡單示範一下
/** * 根據Response,判斷Token是否失效 * * @param response * @return */ private boolean isTokenExpired(Response response) { try { body = response.body().string(); JSONObject object = new JSONObject(body); message = object.getString("Message"); code = object.getInt("Code"); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } if ("Token is expired".equals(message)&& code == 1) { return true; } return false; } 複製代碼
獲取新的Token
/** * 同步請求方式,獲取最新的Token * * @return */ private String getNewToken() { // 經過獲取token的接口,同步請求接口 GetRequest_Interface request = new Retrofit.Builder() .baseUrl(API.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build().create(GetRequest_Interface.class); // KLog.e(Remember.getString("refresh_token",null)); RequestBody body = BaseUtils.convertJson(BaseUtils.paramsMap("refresh_token",App.mmkv.decodeString(BaseConfig.REFRESH_TOKEN,null))); Call<RefreshToken> call = request.postRefreshToken(body); try { response = call.execute(); } catch (IOException e) { e.printStackTrace(); } KLog.e(response.body().getCode()+response.body().getMessage()); if (response.code() == 200 &&response.body().getCode() ==0){ newToken = response.body().getData().getToken(); KLog.e("---------------"+newToken); App.mmkv.encode(BaseConfig.TOKEN,newToken); App.mmkv.encode(BaseConfig.SCHOOL_TOKEN,response.body().getData().getSchool_token()); App.mmkv.encode(BaseConfig.EXPIRE_IN,response.body().getData().getExpire_in()); } return newToken; } 複製代碼
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
複製代碼
至此,關於網絡請求的相關參數信息就基本配置完成
將上述配置步驟進行封裝
/** * Created by darryrzhong * * * 統一的Retrofit入口 */ public class RetrofitHelper { private static final int DEFAULT_TIME_OUT = 5;//超時時間 5s private static final int DEFAULT_READ_TIME_OUT = 10; private final Retrofit retrofit; private String body; private retrofit2.Response<RefreshToken> response; private String newToken; private String message; private int code; private RetrofitHelper(){ Interceptor tokenInterceptor = new Interceptor() { //全局攔截器,往請求頭部添加 token 字段,實現了全局添加 token @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request();//獲取原始請求 Request.Builder requestBuilder = originalRequest.newBuilder() //創建新的請求 .addHeader("Accept", "application/json") .addHeader("Content-Type", "application/json; charset=utf-8") .removeHeader("User-Agent") .addHeader("User-Agent",BaseUtils.getUserAgent()) .method(originalRequest.method(),originalRequest.body()); // Log.e("----------------",originalRequest.body().toString()); Request tokenRequest = null; //帶有token的請求 if (StringUtils.isEmpty(App.mmkv.decodeString(BaseConfig.TOKEN,null))){ return chain.proceed(requestBuilder.build()); } tokenRequest = requestBuilder .header("Authorization","Bearer "+App.mmkv.decodeString(BaseConfig.TOKEN,null)) .build(); return chain.proceed(tokenRequest); } }; Interceptor tokenRespInterceptor = new Interceptor() { //攔截返回體 判斷token是否過時.過時重寫拉取新的token @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); // KLog.d( response.body().string()); if (isTokenExpired(response)){ KLog.d( "自動刷新Token,而後從新請求數據"); //同步請求方式,獲取最新的Token String newToken = getNewToken(); if (newToken != null){ //使用新的Token,建立新的請求 Request newRequest = chain.request() .newBuilder() .header("Authorization", "Bearer " + newToken) .build(); //從新請求 return chain.proceed(newRequest); } } return response.newBuilder().body(ResponseBody.create(MediaType.parse("UTF-8"),body)).build(); } }; OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS) //l 鏈接超時時間 .writeTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS) //讀寫超時 .readTimeout(DEFAULT_READ_TIME_OUT,TimeUnit.SECONDS) //讀取超時 .retryOnConnectionFailure(true) //失敗重連 .addNetworkInterceptor(tokenInterceptor) //添加網絡攔截器 .addInterceptor(tokenRespInterceptor) //.authenticator(authenticator) //受權認證 .build(); retrofit = new Retrofit.Builder() .client(client) .baseUrl(API.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } /** * 同步請求方式,獲取最新的Token * * @return */ private String getNewToken() { // 經過獲取token的接口,同步請求接口 GetRequest_Interface request = new Retrofit.Builder() .baseUrl(API.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build().create(GetRequest_Interface.class); RequestBody body = BaseUtils.convertJson(BaseUtils.paramsMap("refresh_token",App.mmkv.decodeString(BaseConfig.REFRESH_TOKEN,null))); Call<RefreshToken> call = request.postRefreshToken(body); try { response = call.execute(); } catch (IOException e) { e.printStackTrace(); } KLog.e(response.body().getCode()+response.body().getMessage()); if (response.code() == 200 &&response.body().getCode() ==0){ newToken = response.body().getData().getToken(); KLog.e("---------------"+newToken); App.mmkv.encode(BaseConfig.TOKEN,newToken); App.mmkv.encode(BaseConfig.SCHOOL_TOKEN,response.body().getData().getSchool_token()); App.mmkv.encode(BaseConfig.EXPIRE_IN,response.body().getData().getExpire_in()); } return newToken; } /** * 根據Response,判斷Token是否失效 * * @param response * @return */ private boolean isTokenExpired(Response response) { try { body = response.body().string(); JSONObject object = new JSONObject(body); message = object.getString("Message"); code = object.getInt("Code"); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } if ("Token is expired".equals(message)&& code == 1) { return true; } return false; } private static class SingletonHolder{ private static final RetrofitHelper INSTANCE = new RetrofitHelper(); } /** * 獲取RetrofitServiceManager * @return */ public static RetrofitHelper getInstance(){ return SingletonHolder.INSTANCE; } /** * 獲取對應的Service * @param service Service 的 class * @param <T> * @return */ public <T> T create(Class<T> service){ return retrofit.create(service); } } 複製代碼
若是業務中有多個BaseUrl的話,能夠直接寫個方法暴露出去就行了.
public interface ResponseCallBack<T> {
void onSuccess(T t);
void onFault(String errorMsg);
}
複製代碼
public interface ProgressListener {
void startProgress();
void cancelProgress();
}
複製代碼
建立BaseObserver,在回調中進行業務處理
public class BaseObserver<T> implements Observer<T> {
private ResponseCallBack responseCallBack;
private ProgressListener progressListener;
private Disposable disposable;
public BaseObserver(ResponseCallBack responseCallBack,ProgressListener progressListener){
this.responseCallBack = responseCallBack;
this.progressListener = progressListener;
}
}
複製代碼
在 onSubscribe () 方法中進行請求開始時的初始化操做
@Override public void onSubscribe(Disposable d) { this.disposable = d; if (progressListener != null){ progressListener.startProgress(); } } 複製代碼
在 onNext () 方法中處理請求成功業務
@Override
public void onNext(T t) {
responseCallBack.onSuccess(t);
}
複製代碼
在onError () 方法中統一處理請求失敗信息
@Override public void onError(Throwable e) { KLog.d(e.getMessage()); try { if (e instanceof SocketTimeoutException) {//請求超時 responseCallBack.onFault("請求超時,請稍後再試"); } else if (e instanceof ConnectException) {//網絡鏈接超時 responseCallBack.onFault("網絡鏈接超時,請檢查網絡狀態"); } else if (e instanceof SSLHandshakeException) {//安全證書異常 responseCallBack.onFault("安全證書異常"); } else if (e instanceof HttpException) {//請求的地址不存在 int code = ((HttpException) e).code(); if (code == 504) { responseCallBack.onFault("網絡異常,請檢查您的網絡狀態"); } else if (code == 404) { responseCallBack.onFault("請求的地址不存在"); } else { responseCallBack.onFault("請求失敗"); } } else if (e instanceof UnknownHostException) {//域名解析失敗 responseCallBack.onFault("域名解析失敗"); } else { responseCallBack.onFault("error:" + e.getMessage()); } } catch (Exception e2) { e2.printStackTrace(); } finally { Log.e("OnSuccessAndFaultSub", "error:" + e.getMessage()); if (disposable !=null && !disposable.isDisposed()){ //事件完成取消訂閱 disposable.dispose(); } if (progressListener!=null){ progressListener.cancelProgress(); } } } 複製代碼
在 onComplete () 中處理請求成功結束後的業務
@Override public void onComplete() { if (disposable !=null && !disposable.isDisposed()){ //事件完成取消訂閱 disposable.dispose(); } if (progressListener!=null){ progressListener.cancelProgress(); } } 複製代碼
代碼以下:
/** * Created by darryrzhong * */ public class BaseObserver<T> implements Observer<T> { private ResponseCallBack responseCallBack; private ProgressListener progressListener; private Disposable disposable; public BaseObserver(ResponseCallBack responseCallBack,ProgressListener progressListener){ this.responseCallBack = responseCallBack; this.progressListener = progressListener; } @Override public void onSubscribe(Disposable d) { this.disposable = d; if (progressListener != null){ progressListener.startProgress(); } } @Override public void onNext(T t) { responseCallBack.onSuccess(t); } @Override public void onError(Throwable e) { KLog.d(e.getMessage()); try { if (e instanceof SocketTimeoutException) {//請求超時 responseCallBack.onFault("請求超時,請稍後再試"); } else if (e instanceof ConnectException) {//網絡鏈接超時 responseCallBack.onFault("網絡鏈接超時,請檢查網絡狀態"); } else if (e instanceof SSLHandshakeException) {//安全證書異常 responseCallBack.onFault("安全證書異常"); } else if (e instanceof HttpException) {//請求的地址不存在 int code = ((HttpException) e).code(); if (code == 504) { responseCallBack.onFault("網絡異常,請檢查您的網絡狀態"); } else if (code == 404) { responseCallBack.onFault("請求的地址不存在"); } else { responseCallBack.onFault("請求失敗"); } } else if (e instanceof UnknownHostException) {//域名解析失敗 responseCallBack.onFault("域名解析失敗"); } else { responseCallBack.onFault("error:" + e.getMessage()); } } catch (Exception e2) { e2.printStackTrace(); } finally { Log.e("OnSuccessAndFaultSub", "error:" + e.getMessage()); if (disposable !=null && !disposable.isDisposed()){ //事件完成取消訂閱 disposable.dispose(); } if (progressListener!=null){ progressListener.cancelProgress(); } } } @Override public void onComplete() { if (disposable !=null && !disposable.isDisposed()){ //事件完成取消訂閱 disposable.dispose(); } if (progressListener!=null){ progressListener.cancelProgress(); } } } 複製代碼
至此,統一處理結果的BaseObserver封裝完畢
這裏根據服務端接收數據不一樣而有不一樣方式,想要了解更多傳參方式,能夠自行去了解Retrofit的註解,這裏只介紹向服務端傳遞Json數據.
返回體: { "Code": 0, "Data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC*********", "refresh_token": "c9ced011-***************************", "expire_in": 1560330991, "student_id": 33 }, "Message": "登陸成功" } 請求體: {"phone":"13145214436","id":"12345"} 複製代碼
返回體的數據解析就不說了,說說請求體怎麼傳遞
首先咱們把最外面的{ } json 對象當作是一個 map 對象,這樣是否是一會兒就知道怎麼轉化了,對的,就是你想的那樣.
HashMap<String, Object> map = new HashMap<>(); map.put("phone", "13145214436"); map.put("id", "12345"); 複製代碼
而後把map對象轉化成json字符串,傳給服務端就好了
Gson gson=new Gson(); String strEntity = gson.toJson(map); RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json;charset=UTF-8"),strEntity); 複製代碼
至於更復雜的請求體也是同樣的作法
{ "school_id":1, "student_id":23, "start_time":"2019-05-10 15:04:05", "end_time":"2019-06-10 15:04:05", "points": [ { "longitude": 0, "latitude": 0 }, { "longitude": 0, "latitude": 0 }, { "longitude": 0, "latitude": 0 }, { "longitude": 0, "latitude": 0 } ] } 複製代碼
上面的請求體中有個json數組,數組裏面又嵌套了json對象,仍是同樣的作法 把json數組當作是一個list,對的,有和上面同樣的套路了是否是很簡單,
這裏以登陸業務作個簡單示範
GetRequest_Interface request = RetrofitHelper.getInstance().create(GetRequest_Interface.class); //request請求入口 HashMap<String,Object> params = new HashMap(); params.put("phone",phone); params.put("id",id); RequestBody body = BaseUtils.convertJson(params); Observable<RegistLogin> observable= request.postRegist(body); observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new BaseObserver<RegistLogin>(new ResponseCallBack<RegistLogin>() { @Override public void onSuccess(RegistLogin registLogin) { //此到處理 code == 200 } @Override public void onFault(String errorMsg) { BaseUtils.showToast(mContext,errorMsg); } }, new ProgressListener() { @Override public void startProgress() { dialog = BaseUtils.showSpotsDialog(mContext,"登陸中"); dialog.show(); } @Override public void cancelProgress() { dialog.dismiss(); } })); 複製代碼
這樣一來,是否是代碼明瞭簡潔,代碼質量明顯提升了一個層次
至此,Rxjava 和 Retrofit 結合使用 與封裝就基本完成了,再次說明一下,沒有最完美的封裝,只有最適合本身業務的封裝,因此,若是須要請進行本身的業務定製,這裏只提供思路.
歡迎關注做者darryrzhong,更多幹貨等你來拿喲.
更多精彩文章請關注