retrofit+rxjava+okhttp網絡框架之二次封裝


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();
    }
}


RxJavaUtils

public class RxJavaUtils {
    public static <T> ObservableTransformer<T, T> setThread() {
        return upstream -> upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers
                .mainThread());
    }
}


到此處就完結了。剩餘一些零星點點的參數和敞亮,自己設置就好了。
需要源碼可以私信我或者qq加我。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章