retrofit+rxjava+okhttp網(wǎng)絡(luò)框架之二次封裝

retrofit+rxjava的是這幾年很流行的一種網(wǎng)絡(luò)框架,開發(fā)者也提供了豐富的方法棍丐。
之所以進(jìn)行二次封裝渠牲,就是因?yàn)閞etrofit+rxjava的鏈?zhǔn)秸{(diào)用太方便了,不符合單一性原則泌参,
管理起來(lái)比較麻煩竭业。主要目的是二次封裝后,和項(xiàng)目有很高的契合度更高及舍。

說(shuō)一下封裝思路未辆,由于其本身調(diào)用方便,具體方法就不做封裝了锯玛。

第一 retrofit對(duì)象封裝咐柜。

第二 封裝okhttp攔截器兼蜈,用于添加頭參數(shù),攔截錯(cuò)誤日志拙友。

第三 響應(yīng)處理分發(fā)封裝为狸,對(duì)鏈接失敗,鏈接錯(cuò)誤遗契,請(qǐng)求錯(cuò)誤辐棒,請(qǐng)求成功對(duì)應(yīng)處理。

下面直接上代碼:

先看一下封裝后的使用牍蜂,具體的封裝步驟漾根,后面會(huì)有。

RetrofitHelper.getRetrofitInstance(null)
.create(Api.class)
.login()
.compose(RxJavaUtils.setThread())
.subscribe(new BaseObserver(context) {
@Override
public void onSuccess(BaseBean response) {
Log.d("nade", "onSuccess: 成功處理");
}
});
二次封裝后鲫竞,使用非常簡(jiǎn)單辐怕。

下面是具體步驟:
一 retrofit封裝

1 retrofit對(duì)象封裝

public class RetrofitHelper {

/**
 * retrofit 請(qǐng)求助手
 *
 * @param
 * @return retrofit 對(duì)象
 *
 */

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 我們還需要一個(gè)OkClient

public class OkClient {
private OkHttpClient httpClient;
private static OkClient okClient;
private OkClient(Interceptor interceptor){
OkHttpClient.Builder okBuilder = new OkHttpClient.Builder()
.addInterceptor(interceptor) // 頭參數(shù)
.addInterceptor(new RetryInterceptor(RetryInterceptor.COUNT)) // 重連機(jī)制
.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()) // 日志打印 用于請(qǐng)求失敗分析
.addInterceptor(new ErrorStatuInterceptor()); // 錯(cuò)誤狀態(tài)攔截,用于錯(cuò)誤狀態(tài)app內(nèi)部轉(zhuǎn)換并處理后續(xù)動(dòng)作
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 對(duì)象
public OkHttpClient getHttpClient() {
return httpClient;
}

第二 封裝okhttp攔截器从绘,用于添加頭參數(shù)寄疏,攔截錯(cuò)誤日志。

頭參數(shù)攔截器

public class HeadsInterceptor implements Interceptor {
// 用于添加頭參數(shù) 開放請(qǐng)求體僵井,可設(shè)置請(qǐng)求頭參數(shù)
private Request.Builder request;

/**
 * 請(qǐng)求頭參數(shù) 可以為空 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; // 默認(rèn)為2(請(qǐng)求總量3)
private static final String TAG = "RetryInterceptor";

private int maxRetry = 3;//最大重試次數(shù)

//    延遲
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;//假如設(shè)置為3次重試的話陕截,則最大可能請(qǐng)求5次(默認(rèn)1次+3次重試 + 最后一次默認(rèn))
    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 {

/**
 * 打印日志 各種日志 請(qǐng)求參數(shù) 等
 */

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

}

錯(cuò)誤狀態(tài)攔截器
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);
    }

}

}

第三 響應(yīng)處理分發(fā)封裝,對(duì)鏈接失敗批什,鏈接錯(cuò)誤艘策,請(qǐng)求錯(cuò)誤,請(qǐng)求成功對(duì)應(yīng)處理渊季。

public abstract class BaseObserver<T extends BaseBean> implements Observer<T> {
private static final String CONNECT_ERROR = "網(wǎng)絡(luò)連接失敗,請(qǐng)檢查網(wǎng)絡(luò)";
private static final String CONNECT_TIMEOUT = "連接超時(shí),請(qǐng)稍后再試";
private static final String BAD_NETWORK = "服務(wù)器異常";
private static final String PARSE_ERROR = "解析服務(wù)器響應(yīng)數(shù)據(jù)失敗";
private static final String UNKNOWN_ERROR = "未知錯(cuò)誤";
private static final String RESPONSE_RETURN_ERROR = "服務(wù)器返回?cái)?shù)據(jù)失敗";

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錯(cuò)誤
        onException(ExceptionReason.BAD_NETWORK);
    } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
        //連接錯(cuò)誤
        onException(ExceptionReason.CONNECT_ERROR);
    } else if (e instanceof InterruptedIOException) {
        //連接超時(shí)
        onException(ExceptionReason.CONNECT_TIMEOUT);
    } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
        //解析錯(cuò)誤
        onException(ExceptionReason.PARSE_ERROR);
    } else {
        //其他錯(cuò)誤
        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;
}

// 請(qǐng)求成功
public abstract void onSuccess(T response);


// 請(qǐng)求失敗
public void onFailed(BaseBean bean){

};

// 展示進(jìn)度
protected void showProgress(){
    load = new ProDialog.Builder(context).createLoad();
    load.showLoading();


}
// 關(guān)閉進(jìn)度
protected void hideProgress(){
    if (load != null) {
        load.closeLoading();
    }
}
/**
 * 網(wǎng)絡(luò)請(qǐng)求失敗原因
 */
public enum ExceptionReason {
    /**
     * 解析數(shù)據(jù)失敗
     */
    PARSE_ERROR,
    /**
     * 網(wǎng)絡(luò)問題
     */
    BAD_NETWORK,
    /**
     * 連接錯(cuò)誤
     */
    CONNECT_ERROR,
    /**
     * 連接超時(shí)
     */
    CONNECT_TIMEOUT,
    /**
     * 未知錯(cuò)誤
     */
    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;
    }
}


// 取消請(qǐng)求
public void cancelRequest(){
    if (dis != null && !dis.isDisposed()) {
        dis.dispose();
    }
}

// 請(qǐng)求成功后朋蔫,資源釋放。
public void onDestory(){
    cancelRequest();
}

}

RxJavaUtils

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

到此處就完結(jié)了却汉。剩余一些零星點(diǎn)點(diǎn)的參數(shù)和敞亮驯妄,自己設(shè)置就好了。
需要源碼可以私信我或者qq加我合砂。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末青扔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子翩伪,更是在濱河造成了極大的恐慌微猖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缘屹,死亡現(xiàn)場(chǎng)離奇詭異凛剥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)轻姿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門犁珠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逻炊,“玉大人,你說(shuō)我怎么就攤上這事犁享∮嗨兀” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵炊昆,是天一觀的道長(zhǎng)桨吊。 經(jīng)常有香客問我,道長(zhǎng)凤巨,這世上最難降的妖魔是什么视乐? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮磅甩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姥卢。我一直安慰自己卷要,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布独榴。 她就那樣靜靜地躺著僧叉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棺榔。 梳的紋絲不亂的頭發(fā)上瓶堕,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音症歇,去河邊找鬼郎笆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛忘晤,可吹牛的內(nèi)容都是我干的宛蚓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼设塔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼凄吏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起闰蛔,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痕钢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后序六,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體任连,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锨推。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔽氨,死狀恐怖暮刃,靈堂內(nèi)的尸體忽然破棺而出跨算,到底是詐尸還是另有隱情,我是刑警寧澤椭懊,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布诸蚕,位于F島的核電站,受9級(jí)特大地震影響氧猬,放射性物質(zhì)發(fā)生泄漏背犯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一盅抚、第九天 我趴在偏房一處隱蔽的房頂上張望漠魏。 院中可真熱鬧,春花似錦妄均、人聲如沸柱锹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)禁熏。三九已至,卻和暖如春邑彪,著一層夾襖步出監(jiān)牢的瞬間瞧毙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工寄症, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宙彪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓有巧,卻偏偏與公主長(zhǎng)得像您访,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剪决,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容