retrofit+rxjava的是這幾年很流行的一種網絡框架,開發者也提供了豐富的方法。
之所以進行二次封裝,就是因爲retrofit+rxjava的鏈式調用太方便了,不符合單一性原則,
管理起來比較麻煩。主要目的是二次封裝後,和項目有很高的契合度更高。
說一下封裝思路,由於其本身調用方便,具體方法就不做封裝了。
第一 retrofit對象封裝。
第二 封裝okhttp攔截器,用於添加頭參數,攔截錯誤日誌。
第三 響應處理分發封裝,對鏈接失敗,鏈接錯誤,請求錯誤,請求成功對應處理。
下面直接上代碼:
先看一下封裝後的使用,具體的封裝步驟,後面會有。
RetrofitHelper.getRetrofitInstance(null)
.create(Api.class)
.login()
.compose(RxJavaUtils.setThread())
.subscribe(new BaseObserver(context) {
@Override
public void onSuccess(BaseBean response) {
Log.d("nade", "onSuccess: 成功處理");
}
});
二次封裝後,使用非常簡單。
下面是具體步驟:
一 retrofit封裝
1 retrofit對象封裝
public class RetrofitHelper {
/**
* retrofit 請求助手
*
* @param
* @return retrofit 對象
*
*/
public static Retrofit getRetrofitInstance(@Nullable Request.Builder request){
Retrofit.Builder builder = new Retrofit.Builder();
Retrofit retrofit = builder.baseUrl(URL.host)
.client(OkClient.getOkClientInstance(new BaseInterceptor(request)).getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit;
}
}
2 我們還需要一個OkClient
public class OkClient {
private OkHttpClient httpClient;
private static OkClient okClient;
private OkClient(Interceptor interceptor){
OkHttpClient.Builder okBuilder = new OkHttpClient.Builder()
.addInterceptor(interceptor) // 頭參數
.addInterceptor(new RetryInterceptor(RetryInterceptor.COUNT)) // 重連機制
.writeTimeout(NetConstant.NET_TIME_OUT, TimeUnit.SECONDS)
.readTimeout(NetConstant.NET_TIME_OUT, TimeUnit.SECONDS)
.connectTimeout(NetConstant.NET_TIME_OUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(new PrintLogInterceptor()) // 日誌打印 用於請求失敗分析
.addInterceptor(new ErrorStatuInterceptor()); // 錯誤狀態攔截,用於錯誤狀態app內部轉換並處理後續動作
httpClient = okBuilder.build();
}
public static OkClient getOkClientInstance(Interceptor interceptor){
if (null == okClient) {
synchronized (OkClient.class){
if (null == okClient){
okClient = new OkClient(interceptor);
}
}
}
return okClient;
}
// 返回client 對象
public OkHttpClient getHttpClient() {
return httpClient;
}
第二 封裝okhttp攔截器,用於添加頭參數,攔截錯誤日誌。
頭參數攔截器
public class HeadsInterceptor implements Interceptor {
// 用於添加頭參數 開放請求體,可設置請求頭參數
private Request.Builder request;/**
* 請求頭參數 可以爲空 request.addHeader("key","value");
* @param request
*/
public HeadsInterceptor(@Nullable Request.Builder request) {
this.request = request;
}@Override
public Response intercept(Chain chain) throws IOException {
if (null != request) {
return chain.proceed(request.build());
}
return null;
}
}
重試攔截器
public class RetryInterceptor implements Interceptor {
public static final int COUNT = 2; // 默認爲2(請求總量3)
private static final String TAG = "RetryInterceptor";private int maxRetry = 3;//最大重試次數
// 延遲
private long delay = 500;
// 疊加延遲
private long increaseDelay = 3*1000;
public RetryInterceptor() {}
public RetryInterceptor(int maxRetry) {
this.maxRetry = maxRetry;
}public RetryInterceptor(int maxRetry, long delay) {
this.maxRetry = maxRetry;
this.delay = delay;
}public RetryInterceptor(int maxRetry, long delay, long increaseDelay) {
this.maxRetry = maxRetry;
this.delay = delay;
this.increaseDelay = increaseDelay;
}@Override
public Response intercept(Chain chain) throws IOException {RetryWrapper retryWrapper = proceed(chain);
while (retryWrapper.isNeedReTry()) {
retryWrapper.retryNum++;
try {
Thread.sleep(delay + (retryWrapper.retryNum - 1) * increaseDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
proceed(chain, retryWrapper.request, retryWrapper);
}
return retryWrapper.response == null ? chain.proceed(chain.request()) : retryWrapper.response;
}private RetryWrapper proceed(Chain chain) throws IOException {
Request request = chain.request();
RetryWrapper retryWrapper = new RetryWrapper(request, maxRetry);proceed(chain, request, retryWrapper);
return retryWrapper;
}private void proceed(Chain chain, Request request, RetryWrapper retryWrapper) throws IOException {
try {
Response response = chain.proceed(request);
retryWrapper.setResponse(response);
} catch (SocketException | SocketTimeoutException e) {
//e.printStackTrace();
}
}static class RetryWrapper {
volatile int retryNum = 0;//假如設置爲3次重試的話,則最大可能請求5次(默認1次+3次重試 + 最後一次默認)
Request request;
Response response;
private int maxRetry;public RetryWrapper(Request request, int maxRetry) {
this.request = request;
this.maxRetry = maxRetry;
}public void setResponse(Response response) {
this.response = response;
}Response response() {
return this.response;
}Request request() {
return this.request;
}public boolean isSuccessful() {
return response != null && response.isSuccessful();
}public boolean isNeedReTry() {
return !isSuccessful() && retryNum < maxRetry;
}public void setRetryNum(int retryNum) {
this.retryNum = retryNum;
}public void setMaxRetry(int maxRetry) {
this.maxRetry = maxRetry;
}
}
}
日誌打印攔截器
public class PrintLogInterceptor implements Interceptor {
/**
* 打印日誌 各種日誌 請求參數 等
*/String TAG = "nade";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
Log.d(TAG, "url = : " + request.url());
Log.d(TAG, "method = : " + request.method());
Log.d(TAG, "headers = : " + request.headers());
Log.d(TAG, "body = : " + request.body());
Log.d(TAG, "code = : " + response.code());
Log.d(TAG, "message = : " + response.message());
Log.d(TAG, "protocol = : " + response.protocol());
if (response.body() != null && response.body().contentType() != null) {
MediaType mediaType = response.body().contentType();
String string = response.body().string();
Log.d(TAG, "mediaType = : " + mediaType.toString());
Log.d(TAG, "string = : " + decode(string));
ResponseBody responseBody = ResponseBody.create(mediaType, string);
return response.newBuilder().body(responseBody).build();
} else {
return response;
}
}private String decode(String unicodeStr) {
if (unicodeStr == null) {
return null;
}
StringBuffer retBuf = new StringBuffer();
int maxLoop = unicodeStr.length();
for (int i = 0; i < maxLoop; i++) {
if (unicodeStr.charAt(i) == '\\') {
if ((i < maxLoop - 5) && ((unicodeStr.charAt(i + 1) == 'u') || (unicodeStr.charAt(i + 1) == 'U')))
try {
retBuf.append((char) Integer.parseInt(unicodeStr.substring(i + 2, i + 6), 16));
i += 5;
} catch (NumberFormatException localNumberFormatException) {
retBuf.append(unicodeStr.charAt(i));
}
else
retBuf.append(unicodeStr.charAt(i));
} else {
retBuf.append(unicodeStr.charAt(i));
}
}
return retBuf.toString();
}}
錯誤狀態攔截器
public class ErrorStatuInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.body() != null && response.body().contentType() != null) {
return response.newBuilder().body(errorResponse(response,request)).build();
} else {
return response;
}}
public ResponseBody errorResponse(Response response, Request request){
MediaType mediaType = response.body().contentType();
String s = null;
try {
s = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
BaseBean bean = GsonInstance.getInstance().fromJson(s, BaseBean.class);
if (bean != null && bean.getHead() != null && TextUtils.equals(bean.getCode(),"200")){// 成功
return ResponseBody.create(mediaType,s);
}else {// 成功
return ResponseBody.create(mediaType,s);
}}
}
第三 響應處理分發封裝,對鏈接失敗,鏈接錯誤,請求錯誤,請求成功對應處理。
public abstract class BaseObserver<T extends BaseBean> implements Observer<T> {
private static final String CONNECT_ERROR = "網絡連接失敗,請檢查網絡";
private static final String CONNECT_TIMEOUT = "連接超時,請稍後再試";
private static final String BAD_NETWORK = "服務器異常";
private static final String PARSE_ERROR = "解析服務器響應數據失敗";
private static final String UNKNOWN_ERROR = "未知錯誤";
private static final String RESPONSE_RETURN_ERROR = "服務器返回數據失敗";private Disposable dis;
private boolean isShowProgress = true;
private ProDialog load;@Override
public void onSubscribe(Disposable d) {
this.dis = d;
if (isShowProgress){
showProgress();
}
}@Override
public void onNext(T o) {
hideProgress();
onDestory();
if (TextUtils.equals(o.getCode(),"200")) {
onSuccess(o);
}else {
onFailed(o);
}
}@Override
public void onComplete() {
hideProgress();
}@Override
public void onError(Throwable e) {hideProgress();
if (e instanceof retrofit2.HttpException) {
//HTTP錯誤
onException(ExceptionReason.BAD_NETWORK);
} else if (e instanceof ConnectException || e instanceof UnknownHostException) {
//連接錯誤
onException(ExceptionReason.CONNECT_ERROR);
} else if (e instanceof InterruptedIOException) {
//連接超時
onException(ExceptionReason.CONNECT_TIMEOUT);
} else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
//解析錯誤
onException(ExceptionReason.PARSE_ERROR);
} else {
//其他錯誤
onException(ExceptionReason.UNKNOWN_ERROR);
}}
private Context context;
public BaseObserver(Context context) {
this.context = context;
}public BaseObserver(Context context, boolean isShowProgress) {
this.context = context;
this.isShowProgress = isShowProgress;
}public Context getContext(){
return context;
}// 請求成功
public abstract void onSuccess(T response);
// 請求失敗
public void onFailed(BaseBean bean){};
// 展示進度
protected void showProgress(){
load = new ProDialog.Builder(context).createLoad();
load.showLoading();
}
// 關閉進度
protected void hideProgress(){
if (load != null) {
load.closeLoading();
}
}
/**
* 網絡請求失敗原因
*/
public enum ExceptionReason {
/**
* 解析數據失敗
*/
PARSE_ERROR,
/**
* 網絡問題
*/
BAD_NETWORK,
/**
* 連接錯誤
*/
CONNECT_ERROR,
/**
* 連接超時
*/
CONNECT_TIMEOUT,
/**
* 未知錯誤
*/
UNKNOWN_ERROR
}
private void onException(ExceptionReason reason) {
switch (reason) {
case CONNECT_ERROR:
Toast.makeText(context, CONNECT_ERROR, Toast.LENGTH_SHORT).show();
break;case CONNECT_TIMEOUT:
Toast.makeText(context, CONNECT_TIMEOUT, Toast.LENGTH_SHORT).show();
break;case BAD_NETWORK:
Toast.makeText(context, BAD_NETWORK, Toast.LENGTH_SHORT).show();
break;case PARSE_ERROR:
Toast.makeText(context, PARSE_ERROR, Toast.LENGTH_SHORT).show();
break;case UNKNOWN_ERROR:
default:
Toast.makeText(context, UNKNOWN_ERROR, Toast.LENGTH_SHORT).show();
break;
}
}
// 取消請求
public void cancelRequest(){
if (dis != null && !dis.isDisposed()) {
dis.dispose();
}
}// 請求成功後,資源釋放。
public void onDestory(){
cancelRequest();
}
}
RxJavaUtilspublic class RxJavaUtils {
public static <T> ObservableTransformer<T, T> setThread() {
return upstream -> upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers
.mainThread());
}
}
到此處就完結了。剩餘一些零星點點的參數和敞亮,自己設置就好了。
需要源碼可以私信我或者qq加我。