第一次寫有點小激動 算是總結吧!
最近一直聽群友說Retrofit + Rxjava + OkHttp有多牛逼多好用多強大....讓我產生了很大興趣....然后去網絡上搜索了一翻....很多資料....但是對于基礎不是很好的我....很不友好....(資質太低不要嘲笑我)....看完基本還是沒明白....
首先推薦一篇看過覺得好的文章
給初學者的RxJava2.0教程 (作者Season_zlc)
閱讀以下內容 請先把Retrofit + RxJava + OkHttp先了解一下 這樣看下面的東西 才能對你理解起來有幫助
OkHttp
OkHttp是時下最火的HTTP請求框架指厌,別問我為什么诞丽,Google都放棄了httpclient,用OkHttp來替代他顷啼,你還要問么? so....學習一個新的請求框架荐虐,適應形勢七兜,還是非常有必要的!
Retrofit
Retrofit + OkHttp我之前學習的時候,看別人的代碼一直不知道Retrofit和OkHttp二者是有什么區(qū)別? 查閱資料后了解到福扬,OkHttp與HTTPClient腕铸、HTTPConnection是一個級別的,是系統(tǒng)提供的較為原裝的網絡請求庫铛碑。而Retrofit則是基于OkHttp的再次封裝過狠裹,與Volley這種已經被封裝過的網絡庫是一個級別的。其實就是套了一層把很多的坑都給你門填好了汽烦,而且最主要的是涛菠,做了很多安全上的改進,還有能更好的支持RxJava。
Rxjava
看完[Season_zlc]的講解后俗冻,讓我了解到了RxJava 是通過實現了線程調度器讓開發(fā)者更方便且自由的調度線程礁叔,被觀察者和訂閱者的方式和強大的操作符,還有引以為傲的鏈式調用迄薄,滿足者開發(fā)者各種需求琅关。
那為什么要RxJava + Retrofit + OkHttp要一起使用呢?
其實原因很簡單Retrofit用過的人都知道,Retrofit 負責請求讥蔽,OkHttp 負責請求過程死姚,Retrofit 和 OkHttp 2者結合都沒有實現結果成功后在UI線程回調,而是在IO線程回調的勤篮。那你肯定是不能更新控件,然道你要打算去寫handle來通知UI線程更新么色罚,你不覺得很low么? 而且代碼邏輯很亂么? 而使用Rxjava不僅讓你的代碼看起來更加優(yōu)雅易懂碰缔,并且Retrofit本身也是支持RxJava的。個人想法戳护,也許作者最初的目的本來就是為了和RxJava配套使用金抡。
以下代碼均取自Fazhi項目wanggang老師的教學代碼 最后我會提供代碼供大家下載 不用擔心!
一、首先我門定義API類
public interface Iapi {
@POST("userLogin")
Observable<BaseResponse<UserBean>> userLogin(@Body RequestBody params);
}
二腌且、初始化Retrofit + OkHttp的請求類 (已經按步驟在代碼里注釋好了)
這個類的代碼很明顯梗肝,我門先將繁瑣的Retrofit和OkHttp初始化,然后通過retrofit.create(Iapi.class)拿到最先定義的API類的實例類對象铺董,有了實例類對象后就可以將RequestBody請求體傳入發(fā)起請求了巫击。
public class NetRequest {
private static final String TAG = "===NET REQUEST===";
private static NetRequest instance;
private OkHttpClient client;
private Retrofit retrofit;
private Iapi iapi;
// 第一步 構造RequestBody請求體
public static RequestBody generateReqBody(HashMap<String, Object> map){
JSONObject params = new JSONObject();
params.putAll(map);
return RequestBody.create(NetConfig.TypeJSON, params.toJSONString());
}
// 第二步 構建單例模式
public static NetRequest getInstance() {
if (instance == null) {
synchronized (NetRequest.class) {
if (instance == null) {
instance = new NetRequest();
}
}
}
return instance;
}
// 第二步 1.獲得單例模式時會初始化該類 初始化時就構造好OkHttp和Retrofit2個對象 并且設置好參數
public NetRequest() {
client = getClient();
retrofit = getRetrofit();
}
// 第二步 2.初始化OkHttp的參數 并拿到Client請求對象
private OkHttpClient getClient() {
// HttpLoggingInterceptor是Log攔截器
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i(TAG, message);
}
});
// 設置Log攔截器的攔截等級
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.addInterceptor(interceptor)// 攔截器設置給它
.connectTimeout(20, TimeUnit.SECONDS)// 請求超時時間
.readTimeout(20, TimeUnit.SECONDS)// 讀取超時時間
.writeTimeout(20, TimeUnit.SECONDS)// 寫入超時時間
.build();
}
// 第二步 3.初始化Retrofit的參數 并將Client設置給它
private Retrofit getRetrofit() {
return new Retrofit.Builder()
.client(client)// 傳入OKhttp的client對象
.addConverterFactory(GsonConverterFactory.create())// 添加gson解析的支持
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())// 添加支持observable類型
.baseUrl(NetConfig.BASE_URL)// 傳入URL
.build();
}
// 第四步 通過retrofit創(chuàng)建Iapi的實現類對象(retrofit已經幫你實現好了 不需要自己去實現 只要調用方法)
// 待會model要用來調你寫好的Iapi里的接口 接口里定義好了方法為userLogin(放第一步獲取的請求體)
public Iapi getApi() {
if (iapi == null) {
iapi = retrofit.create(Iapi.class);
}
return iapi;
}
}
三、重寫了Subscriber訂閱者類
封裝了一些每次回調可以不變的代碼精续,并將onNext這個成功的回調丟給后面的繼承者
另外:
1.該類是一個抽象類坝锰,繼承Subscriber但并沒有重寫onNext()方法,因為后面要留給Presenter的實現類去回調成功后做各種操作重付,因此它并不屬于重復而又不變的代碼顷级,所以在該類里并沒有去實現它。
2.onError() 中我門做了統(tǒng)一的服務器錯誤代碼處理确垫,將錯誤代碼區(qū)分開來弓颈,這里分二種情況:
- 請求服務器失敗了,通常為網絡連接失敗404 500之類的
- 網絡通暢請求成功了删掀,但是給你返回了約定好的錯誤代碼翔冀,通常1000 1001之類的,比如帳號不存在 未注冊等等....
public abstract class NetSubscriber<T> extends Subscriber<T> {
private BaseView view;
@Override
public void onStart() {
super.onStart();
view = getView();
view.showLoading();
}
@Override
public void onCompleted() {
view.hideLoading();
}
@Override
public void onError(Throwable e) {
view.hideLoading();
ExceptionHandle.ResponeThrowable throwable;
//后臺邏輯執(zhí)行失敗披泪,拋出的異常
if(e instanceof ExceptionHandle.ResponeThrowable){
throwable = (ExceptionHandle.ResponeThrowable) e;
} else {
//網絡操作執(zhí)行的異常
throwable = ExceptionHandle.handleException(e);
}
view.showError(throwable.message);
}
public abstract BaseView getView();
}
四橘蜜、封裝RxJava的請求參數
1.Presenter中RxJava每次都要寫初始化參數,比如設置請求時在IO線程,請求完后在UI線程计福。
2.設置攔截器好比Rxjava發(fā)了一個消息下來跌捆,中間被你攔截起來,按你設定的過濾規(guī)則決定讓消息走哪邊象颖。
例如我門下面代碼中佩厚,規(guī)則是:
如果服務器返回為true,我門放行讓訂閱者在onNext()接受到成功的json數據
如果服務器返回為false说订,我門讓他走另一條路exception抄瓦,讓他進入統(tǒng)一錯誤代碼處理中,然后回調訂閱者的onErrer()提示用戶陶冷,失敗了的原因钙姊。
public class RxHelper {
// 設置請求時為IO線程,處理結果時在UI線程
public static Observable.Transformer schedulers() {
return new Observable.Transformer() {
@Override
public Object call(Object observable) {
return ((Observable)observable)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
// 本方法為設置攔截器
// 1.用了Map操作符 來攔截每一次的消息 過濾正確的放行給訂閱者 失敗的丟給錯誤碼統(tǒng)一處理類
// 2.onErrorResumeNext設置如果是請求上的錯誤 比如網絡錯誤/連接超時之類的 直接丟給錯誤碼統(tǒng)一處理類
public static <T> Observable.Transformer transform() {
return new Observable.Transformer() {
@Override
public Object call(Object observable) {
return ((Observable)observable)
.map(new TransformerFun())// 設置攔截服務器連接成功后返回的內容 然后篩選
.onErrorResumeNext(new ServerExceptionFun());// 設置請求直接失敗了的攔截
}
};
}
/**
* 請求失敗 將錯誤代碼的回調轉接給統(tǒng)一處理類
* @param <T>
*/
static class ServerExceptionFun<T> implements Func1<Throwable,Observable<T>>{
@Override
public Observable<T> call(Throwable throwable) {
return Observable.error(ExceptionHandle.handleException(throwable));
}
}
/**
* 攔截器的具體邏輯
* @param <T>
*/
static class TransformerFun<T> implements Func1<BaseResponse<T>,T>{
@Override
public T call(BaseResponse<T> baseResponse) {
if(baseResponse.is_success()){
return baseResponse.getData();// 正確返回json
}
// 如果不正確 丟給錯誤代碼統(tǒng)一處理類
ExceptionHandle.ServerException exception = new ExceptionHandle.ServerException();
exception.message = baseResponse.getError_content();
throw exception;
}
}
}
五埂伦、錯誤代碼統(tǒng)一處理類
public class ExceptionHandle {
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int REQUEST_TIMEOUT = 408;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504;
public static ResponeThrowable handleException(Throwable e) {
ResponeThrowable ex;
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
switch (httpException.code()) {
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
ex.message = "網絡錯誤";
break;
}
return ex;
} else if (e instanceof ServerException) {
ServerException resultException = (ServerException) e;
ex = new ResponeThrowable(resultException, resultException.code);
ex.message = resultException.message;
return ex;
} else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
ex.message = "解析錯誤";
return ex;
} else if (e instanceof ConnectException) {
ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
ex.message = "連接失敗";
return ex;
} else if (e instanceof javax.net.ssl.SSLHandshakeException) {
ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
ex.message = "證書驗證失敗";
return ex;
} else if (e instanceof ConnectTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = "連接超時";
return ex;
} else if (e instanceof java.net.SocketTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = "連接超時";
return ex;
} else {
ex = new ResponeThrowable(e, ERROR.UNKNOWN);
ex.message = "未知錯誤";
return ex;
}
}
/**
* 約定異常
*/
class ERROR {
/**
* 未知錯誤
*/
public static final int UNKNOWN = 1000;
/**
* 解析錯誤
*/
public static final int PARSE_ERROR = 1001;
/**
* 網絡錯誤
*/
public static final int NETWORD_ERROR = 1002;
/**
* 協議出錯
*/
public static final int HTTP_ERROR = 1003;
/**
* 證書出錯
*/
public static final int SSL_ERROR = 1005;
/**
* 連接超時
*/
public static final int TIMEOUT_ERROR = 1006;
}
public static class ResponeThrowable extends Exception {
public int code;
public String message;
public ResponeThrowable(Throwable throwable, int code) {
super(throwable);
this.code = code;
}
}
public static class ServerException extends RuntimeException {
public int code;
public String message;
}
}
六煞额、定義我門自己的BaseResponse成功響應的接收類
該類可有可無,主要用來替代Retrofit自帶的ResponseBody沾谜,因為我門做了過濾膊毁,要用到is_success,當然如果你不做攔截也可以用他原來的ResponseBody基跑。
public class BaseResponse<T> {
private boolean is_success;
private String error_content;
private T data;
public boolean is_success() {
return is_success;
}
public void setIs_success(boolean is_success) {
this.is_success = is_success;
}
public String getError_content() {
return error_content;
}
public void setError_content(String error_content) {
this.error_content = error_content;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
最后調用一下看看
HashMap<String, Object> map = new HashMap<>();
map.put("username", username);
map.put("password", passwrod);
RequestBody requestBody = NetRequest.generateReqBody(map);
Observable<BaseResponse<UserBean>> observable = NetRequest.getInstance().getApi() .userLogin(requestBody);
observable.compose(RxHelper.schedulers())
.compose(RxHelper.transform())
.subscribe(new NetSubscriber<UserBean>() {
@Override
public BaseView getView() { return view; }
@Override
public void onNext(UserBean userBean) {
// 成功的回調
}
});
對~老鐵....就是這樣....復制+粘貼....第一次....就這樣沒了
續(xù) 結合本封裝的MVP項目架構 讓你的Activity腰圍瘦下來
FaZhi項目框架 Git下載 (向作者wanggang老師致敬)