XHttp2 一個功能強悍的網(wǎng)絡(luò)請求庫,使用RxJava2 + Retrofit2 + OKHttp進(jìn)行組裝

XHttp2

項目地址

一個功能強悍的網(wǎng)絡(luò)請求庫女器,使用RxJava2 + Retrofit2 + OKHttp組合進(jìn)行封裝酸役。還不趕緊點擊使用說明文檔,體驗一下吧驾胆!

特征

  • 支持默認(rèn)涣澡、全局、局部三個層次的配置功能丧诺。
  • 支持動態(tài)配置和自定義底層框架Okhttpclient入桂、Retrofit.
  • 加入基礎(chǔ)ApiService,減少Api冗余驳阎。
  • 支持多種方式訪問網(wǎng)絡(luò)GET抗愁、POST馁蒂、PUT、DELETE等請求協(xié)議蜘腌。
  • 支持網(wǎng)絡(luò)緩存,六種緩存策略可選,涵蓋大多數(shù)業(yè)務(wù)場景沫屡。
  • 支持固定添加header和動態(tài)添加header。
  • 支持添加全局參數(shù)和動態(tài)添加局部參數(shù)撮珠。
  • 支持文件下載沮脖、多文件上傳和表單提交數(shù)據(jù)。
  • 支持文件請求芯急、上傳勺届、下載的進(jìn)度回調(diào)、錯誤回調(diào)志于,也可以自定義回調(diào)涮因。
  • 支持任意數(shù)據(jù)結(jié)構(gòu)的自動解析废睦。
  • 支持添加動態(tài)參數(shù)例如timeStamp時間戳伺绽、token、簽名sign嗜湃。
  • 支持自定義的擴展API奈应。
  • 支持多個請求合并。
  • 支持Cookie管理购披。
  • 支持異步杖挣、同步請求阵苇。
  • 支持Https酪术、自簽名網(wǎng)站Https的訪問、雙向驗證冒版。
  • 支持失敗重試機制筐乳,可以指定重試次數(shù)歌殃、重試間隔時間。
  • 支持根據(jù)key刪除網(wǎng)絡(luò)緩存和清空網(wǎng)絡(luò)緩存蝙云。
  • 提供默認(rèn)的標(biāo)準(zhǔn)ApiResult(遵循OpenApi格式)解析和回調(diào)氓皱,并且可自定義ApiResult。
  • 支持取消數(shù)據(jù)請求勃刨,取消訂閱波材,帶有對話框的請求不需要手動取消請求,對話框消失會自動取消請求身隐。
  • 支持請求數(shù)據(jù)結(jié)果采用回調(diào)和訂閱兩種方式廷区。
  • 提供"默認(rèn)API"、"接口協(xié)議"以及"統(tǒng)一請求實體"三種方式進(jìn)行網(wǎng)絡(luò)請求贾铝,支持自定義網(wǎng)絡(luò)請求協(xié)議躲因。
  • 返回結(jié)果和異常統(tǒng)一處理早敬,支持自定義異常處理。
  • 結(jié)合RxJava大脉,線程切換靈活搞监。
  • 請求實體支持注解配置,配置網(wǎng)絡(luò)請求接口的url镰矿、是否需要驗證token以及請求參數(shù)的key琐驴。
  • 擁有統(tǒng)一的網(wǎng)絡(luò)請求取消機制。

1秤标、演示(請star支持)

1.1绝淡、Demo演示動畫

demo.gif

1.2、Demo下載

download.png

1.3苍姜、api服務(wù)安裝

服務(wù)端的搭建詳細(xì)請點擊查看

2牢酵、如何使用

目前支持主流開發(fā)工具AndroidStudio的使用,直接配置build.gradle衙猪,增加依賴即可.

2.1馍乙、Android Studio導(dǎo)入方法,添加Gradle依賴

1.先在項目根目錄的 build.gradle 的 repositories 添加:

allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

2.然后在dependencies添加:

dependencies {
  ...
  implementation 'com.github.xuexiangjys:XHttp2:1.0.0'
  implementation 'com.google.code.gson:gson:2.8.2'
  implementation 'com.squareup.okhttp3:okhttp:3.10.0'
  implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
  implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
}

3.在Application中初始化XHttpSDK

XHttpSDK.init(this);   //初始化網(wǎng)絡(luò)請求框架垫释,必須首先執(zhí)行
XHttpSDK.debug("XHttp");  //需要調(diào)試的時候執(zhí)行
XHttpSDK.setBaseUrl(SettingSPUtils.getInstance().getApiURL());  //設(shè)置網(wǎng)絡(luò)請求的基礎(chǔ)地址

4.全局初始化配置(非必要)

除了上述的操作以外丝格,你還可以使用XHttp.getInstance()對網(wǎng)絡(luò)請求框架進(jìn)行全局性參數(shù)配置,配置一些公用默認(rèn)的參數(shù)棵譬,這樣我們就不需要為每個請求都進(jìn)行設(shè)置显蝌。方法如下:

方法名 備注
debug 設(shè)置日志的打印模式
setBaseUrl 設(shè)置全局baseUrl
setSubUrl 設(shè)置全局subUrl
setReadTimeOut 設(shè)置全局讀取超時時間
setWriteTimeOut 設(shè)置全局寫入超時時間
setConnectTimeout 設(shè)置全局連接超時時間
setTimeout 設(shè)置全局超時時間
setRetryCount 設(shè)置全局超時重試次數(shù)
setRetryDelay 設(shè)置全局超時重試延遲時間
setRetryIncreaseDelay 設(shè)置全局超時重試延遲疊加時間
setCacheMode 設(shè)置全局的緩存模式
setIsDiskCache 設(shè)置是否是磁盤緩存
setMemoryMaxSize 設(shè)置內(nèi)存緩存的最大數(shù)量
setCacheTime 設(shè)置全局的緩存過期時間
setCacheMaxSize 設(shè)置全局的磁盤緩存大小,默認(rèn)50M
setCacheDirectory 設(shè)置全局緩存的路徑,默認(rèn)是應(yīng)用包下面的緩存
setCacheDiskConverter 設(shè)置全局緩存的轉(zhuǎn)換器
addCommonParams 添加全局公共請求參數(shù)
addCommonHeaders 添加全局公共請求參數(shù)
addInterceptor 添加全局?jǐn)r截器
addNetworkInterceptor 添加全局網(wǎng)絡(luò)攔截器
setOkproxy 全局設(shè)置OkHttpClient的代理
setOkconnectionPool 設(shè)置全局OkHttpClient的請求連接池
setOkclient 全局為Retrofit設(shè)置自定義的OkHttpClient
addConverterFactory 設(shè)置全局Converter.Factory,默認(rèn)GsonConverterFactory.create()
addCallAdapterFactory 設(shè)置全局CallAdapter.Factory,默認(rèn)RxJavaCallAdapterFactory.create()
setHostnameVerifier 設(shè)置https的全局訪問規(guī)則
setCertificates 設(shè)置https的全局自簽名證書
setCookieStore 設(shè)置全局cookie存取規(guī)則

如何進(jìn)行網(wǎng)絡(luò)請求

1订咸、使用XHttp默認(rèn)api進(jìn)行請求

1.使用XHttp.post曼尊、XHttp.get、XHttp.delete脏嚷、XHttp.put骆撇、XHttp.downLoad構(gòu)建請求。

2.修改request的請求參數(shù)然眼。

方法名 類型 默認(rèn)值 備注
baseUrl String 設(shè)置該請求的baseUrl
timeOut long 10000 設(shè)置超時時間
accessToken boolean false 是否需要驗證token
threadType String 設(shè)置請求的線程調(diào)度類型
syncRequest boolean false 設(shè)置是否是同步請求(不開子線程)
onMainThread boolean true 請求完成后是否回到主線程
upJson String "" 上傳Json格式的數(shù)據(jù)請求
keepJson boolean false 返回保持json的形式
retryCount int 設(shè)置超時重試的次數(shù)
retryDelay int 設(shè)置超時重試的延遲時間
retryIncreaseDelay int 設(shè)置超時重試疊加延時
headers HttpHeaders 添加頭信息
params HttpParams 設(shè)置表單請求參數(shù)
cacheMode CacheMode CacheMode.NO_CACHE 設(shè)置緩存的模式

3.調(diào)用execute方法執(zhí)行請求艾船。execute一般有如下兩種方式:

  • execute(CallBack callBack): 直接回調(diào)結(jié)果。

  • execute(Class clazz)和execute(Type type): 回調(diào)Observable<T>對象高每,可通過訂閱獲取到結(jié)果屿岂。

4.請求使用演示

XHttp.get("/user/getAllUser")
        .syncRequest(false) //異步請求
        .onMainThread(true) //回到主線程
        .execute(new SimpleCallBack<List<User>>() {
            @Override
            public void onSuccess(List<User> response) {
                refreshLayout.finishRefresh(true);
                if (response != null && response.size() > 0) {
                    mUserAdapter.refresh(response);
                    mLlStateful.showContent();
                } else {
                    mLlStateful.showEmpty();
                }
            }
            @Override
            public void onError(ApiException e) {
                refreshLayout.finishRefresh(false);
                mLlStateful.showError(e.getMessage(), null);
            }

        });
XHttp.post("/user/deleteUser")
        .params("userId", item.getUserId())
        .execute(Boolean.class)
        .subscribeWith(new TipRequestSubscriber<Boolean>() {
            @Override
            protected void onSuccess(Boolean aBoolean) {
                ToastUtils.toast("刪除成功!");
                setFragmentResult(RESULT_OK, null);
                popToBack();
            }
        });


2鲸匿、使用XHttpRequest封裝的統(tǒng)一請求實體進(jìn)行請求

在使用它之前爷怀,需要下載/定義對應(yīng)的實體協(xié)議,如下:

@RequestParams(url = "/user/addUser", accessToken = false)
public static class UserService_AddUser extends XHttpRequest {

    /**
     *
     */
    public User request;

    @Override
    protected Boolean getResponseEntityType() {
        return null;
    }
}

1.注解說明

  • @RequestParams
注解參數(shù) 類型 默認(rèn)值 備注
baseUrl String "" 設(shè)置該請求的baseUrl
url String "" 請求網(wǎng)絡(luò)接口地址
timeout long 15000 設(shè)置超時時間
accessToken boolean true 設(shè)置是否需要驗證token
cacheMode CacheMode CacheMode.NO_CACHE 設(shè)置請求的緩存模式
  • @ParamKey
注解參數(shù) 類型 默認(rèn)值 備注
key String / 請求參數(shù)的key

2.使用XHttpSDK進(jìn)行請求带欢。

  • post(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread):
    獲取PostRequest請求(使用實體參數(shù)名作為請求Key)运授。

  • postToMain(XHttpRequest xHttpRequest):
    獲取PostRequest請求(主線程->主線程)烤惊。

  • postToIO(XHttpRequest xHttpRequest):
    獲取PostRequest請求(主線程->子線程)。

  • postInThread(XHttpRequest xHttpRequest):
    獲取PostRequest請求(子線程->子線程)吁朦。

  • execute(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread) :
    執(zhí)行PostRequest請求柒室,返回observable對象(使用實體參數(shù)名作為請求Key)。

  • executeToMain(XHttpRequest xHttpRequest):
    執(zhí)行post請求逗宜,返回observable對象(主線程->主線程)

  • executeToMain(XHttpRequest xHttpRequest雄右,BaseSubscriber<T> subscriber):
    執(zhí)行post請求并進(jìn)行訂閱,返回訂閱信息(主線程->主線程)

3.請求使用演示纺讲。

XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser());
XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber<Boolean>(mIProgressLoader) {
    @Override
    public void onSuccess(Boolean aBoolean) {
        ToastUtils.toast("用戶添加成功擂仍!");
        mRefreshLayout.autoRefresh();
    }
});

3、使用XHttpProxy代理進(jìn)行請求

在使用它之前熬甚,需要下載/定義對應(yīng)的接口協(xié)議逢渔,如下:

/**
 * 訂單
 */
public interface IOrder {
    /**
     * 購買書
     *
     * @param bookId 用戶名
     * @param userId 密碼
     */
    @NetMethod(ParameterNames = {"bookId", "userId", "number"}, Url = "/order/addOrder/")
    Observable<Boolean> buyBook(int bookId, int userId, int number);
}

1.注解說明

  • @NetMethod
注解參數(shù) 類型 默認(rèn)值 備注
ParameterNames String[] {} 參數(shù)名集合
BaseUrl String "" 設(shè)置該請求的baseUrl
Url String "" 請求網(wǎng)絡(luò)接口地址
Timeout long 10000 設(shè)置超時時間
AccessToken boolean true 設(shè)置是否需要驗證token
CacheMode CacheMode CacheMode.NO_CACHE 設(shè)置請求的緩存模式

2.使用XHttpProxy進(jìn)行請求。

構(gòu)建一個XHttpProxy乡括,將定義的api接口傳入后肃廓,直接調(diào)用接口進(jìn)行請求。

構(gòu)造XHttpProxy需要傳入ThreadType,默認(rèn)是ThreadType.TO_MAIN粟判。

  • TO_MAIN: executeToMain(main -> io -> main)

【注意】請確保網(wǎng)絡(luò)請求在主線程中【實質(zhì)是異步請求(切換到io線程)亿昏,且響應(yīng)的線程又切換至主線程】

  • TO_IO: executeToIO(main -> io -> io)

【注意】請確保網(wǎng)絡(luò)請求在主線程中【實質(zhì)是異步請求(切換到io線程)峦剔,不過響應(yīng)的線程不變档礁,還是之前請求的那個io線程】

  • IN_THREAD: executeInThread(io -> io -> io)

【注意】請確保網(wǎng)絡(luò)請求在子線程中才可以使用該類型【實質(zhì)是不做任何線程調(diào)度的同步請求】

3.請求使用演示。

//使用XHttpProxy進(jìn)行接口代理請求
XHttpProxy.proxy(TestApi.IOrder.class)
        .buyBook(mBookAdapter.getItem(position).getBookId(), UserManager.getInstance().getUser().getUserId(), 1)
        .subscribeWith(new TipRequestSubscriber<Boolean>() {
            @Override
            public void onSuccess(Boolean aBoolean) {
                ToastUtils.toast("圖書購買" + (aBoolean ? "成功" : "失敗") + "吝沫!");
                mRefreshLayout.autoRefresh();
            }
        });

4呻澜、文件上傳和下載

1.文件上傳【multipart/form-data】

使用post的文件表單上傳。使用XHttp.post,然后使用params傳遞附帶的參數(shù)惨险,使用uploadFile傳遞需要上傳的文件羹幸,使用示例如下:

mIProgressLoader.updateMessage("上傳中...");
XHttp.post("/book/uploadBookPicture")
        .params("bookId", book.getBookId())
        .uploadFile("file", FileUtils.getFileByPath(mPicturePath), new IProgressResponseCallBack() {
            @Override
            public void onResponseProgress(long bytesWritten, long contentLength, boolean done) {

            }
        }).execute(Boolean.class)
        .compose(RxLifecycle.with(this).<Boolean>bindToLifecycle())
        .subscribeWith(new ProgressLoadingSubscriber<Boolean>(mIProgressLoader) {
            @Override
            public void onSuccess(Boolean aBoolean) {
                mIsEditSuccess = true;
                ToastUtils.toast("圖片上傳" + (aBoolean ? "成功" : "失敗") + "!");
            }
        });

2.文件下載

使用XHttp.downLoad辫愉,傳入下載的地址url栅受、保存文件的路徑以及文件名即可完成文件的下載,使用示例如下:

XHttp.downLoad(BookAdapter.getBookImgUrl(book))
        .savePath(PathUtils.getExtPicturesPath())
        .execute(new DownloadProgressCallBack<String>() {
            @Override
            public void onStart() {
                HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "圖片下載中...", true);
            }

            @Override
            public void onError(ApiException e) {
                ToastUtils.toast(e.getMessage());
                HProgressDialogUtils.cancel();
            }

            @Override
            public void update(long bytesRead, long contentLength, boolean done) {
                HProgressDialogUtils.onLoading(contentLength, bytesRead); //更新進(jìn)度條
            }

            @Override
            public void onComplete(String path) {
                ToastUtils.toast("圖片下載成功, 保存路徑:" + path);
                HProgressDialogUtils.cancel();
            }
        });

高階網(wǎng)絡(luò)請求操作

請求生命周期綁定

1.請求loading加載和請求生命周期綁定

在請求時恭朗,訂閱ProgressLoadingSubscriber或者ProgressLoadingCallBack屏镊,傳入請求消息加載者IProgressLoader,即可完成生命周期的綁定痰腮。示例如下:

XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser());
    XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber<Boolean>(mIProgressLoader) {
        @Override
        public void onSuccess(Boolean aBoolean) {
            ToastUtils.toast("用戶添加成功而芥!");
            mRefreshLayout.autoRefresh();
        }
    });

2.網(wǎng)絡(luò)請求生命周期和Activity/Fragment生命周期綁定

(1)這里需要依賴一下RxUtil2

implementation 'com.github.xuexiangjys:rxutil2:1.1.2'

(2)在所在的Activity的onCreate()下鎖定Activity.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    RxLifecycle.injectRxLifecycle(this);
}

(3)然后在請求中使用RxJava的compose的操作符進(jìn)行綁定。

.compose(RxLifecycle.with(this).<Boolean>bindToLifecycle())

攔截器

日志攔截器

(1)框架默認(rèn)提供一個實現(xiàn)好的日志攔截器HttpLoggingInterceptor,通過XHttpSDK.debug("XHttp");就可以設(shè)置進(jìn)去膀值,它有5種打印模式

  • NONE: 不打印log

  • BASIC: 只打印"請求首行"和"響應(yīng)首行"棍丐。

  • HEADERS: 打印請求和響應(yīng)的所有 Header

  • PARAM: 只打印請求和響應(yīng)參數(shù)

  • BODY: 打印所有數(shù)據(jù)(默認(rèn)是這種)

(2)如果需要對網(wǎng)絡(luò)請求的相關(guān)參數(shù)進(jìn)行自定義記錄的話误辑,可以繼承HttpLoggingInterceptor實現(xiàn)一個自己的網(wǎng)絡(luò)請求日志攔截器,重寫logForRequestlogForResponse兩個方法即可歌逢。

(3)設(shè)置自定義的日志攔截器.

XHttpSDK.debug(new CustomLoggingInterceptor());

動態(tài)參數(shù)添加攔截器

有時候巾钉,我們需要對所有請求添加一些固定的請求參數(shù),但是這些參數(shù)的值又是變化的秘案,這個時候我們就需要動態(tài)添加請求參數(shù)【例如睛琳,請求的token、時間戳以及簽名等】

(1)繼承BaseDynamicInterceptor踏烙,實現(xiàn)updateDynamicParams方法师骗,如下:

@Override
protected TreeMap<String, Object> updateDynamicParams(TreeMap<String, Object> dynamicMap) {
    if (isAccessToken()) {//是否添加token
        dynamicMap.put("token", TokenManager.getInstance().getToken());
    }
    if (isSign()) {//是否添加簽名
        dynamicMap.put("sign", TokenManager.getInstance().getSign());
    }
    if (isTimeStamp()) {//是否添加請求時間戳
        dynamicMap.put("timeStamp", DateUtils.getNowMills());
    }
    return dynamicMap;//dynamicMap:是原有的全局參數(shù)+局部參數(shù)+新增的動態(tài)參數(shù)
}

(2)設(shè)置動態(tài)參數(shù)添加攔截器。

XHttpSDK.addInterceptor(new CustomDynamicInterceptor()); //設(shè)置動態(tài)參數(shù)添加攔截器

失效請求校驗攔截器

當(dāng)服務(wù)端返回一些獨特的錯誤碼(一般是token校驗錯誤讨惩、失效辟癌,請求過于頻繁等),需要我們進(jìn)行全局性的攔截捕獲荐捻,并作出相應(yīng)的響應(yīng)時黍少,我們就需要定義一個特殊的攔截器求處理這些請求。

(1)繼承BaseExpiredInterceptor处面,實現(xiàn)isResponseExpiredresponseExpired方法厂置,如下:

/**
 * 判斷是否是失效的響應(yīng)
 *
 * @param oldResponse
 * @param bodyString
 * @return {@code true} : 失效 <br>  {@code false} : 有效
 */
@Override
protected ExpiredInfo isResponseExpired(Response oldResponse, String bodyString) {
    int code = JSONUtils.getInt(bodyString, ApiResult.CODE, 0);
    ExpiredInfo expiredInfo = new ExpiredInfo(code);
    switch (code) {
        case TOKEN_INVALID:
        case TOKEN_MISSING:
            expiredInfo.setExpiredType(KEY_TOKEN_EXPIRED)
                    .setBodyString(bodyString);
            break;
        case AUTH_ERROR:
            expiredInfo.setExpiredType(KEY_UNREGISTERED_USER)
                    .setBodyString(bodyString);
            break;
        default:
            break;
    }
    return expiredInfo;
}

/**
 * 失效響應(yīng)的處理
 *
 * @return 獲取新的有效請求響應(yīng)
 */
@Override
protected Response responseExpired(Response oldResponse, Chain chain, ExpiredInfo expiredInfo) {
    switch(expiredInfo.getExpiredType()) {
        case KEY_TOKEN_EXPIRED:
            User user = TokenManager.getInstance().getLoginUser();
            if (user != null) {
                final boolean[] isGetNewToken = {false};
                HttpLog.e("正在重新獲取token...");
                XHttpProxy.proxy(ThreadType.IN_THREAD, TestApi.IAuthorization.class)
                        .login(user.getLoginName(), user.getPassword())
                        .subscribeWith(new NoTipRequestSubscriber<LoginInfo>() {
                            @Override
                            protected void onSuccess(LoginInfo loginInfo) {
                                TokenManager.getInstance()
                                        .setToken(loginInfo.getToken())
                                        .setLoginUser(loginInfo.getUser());
                                isGetNewToken[0] = true;
                                HttpLog.e("重新獲取token成功:" + loginInfo.getToken());
                            }
                        });
                if (isGetNewToken[0]) {
                    try {
                        HttpLog.e("使用新的token重新進(jìn)行請求...");
                        return chain.proceed(HttpUtils.updateUrlParams(chain.request(), "token", TokenManager.getInstance().getToken()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                XRouter.getInstance().build("/xhttp/login").navigation();
                return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), "請先進(jìn)行登錄!");
            }
            break;
        case KEY_UNREGISTERED_USER:
            return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), "非法用戶登錄魂角!");
        default:
            break;
    }
    return null;
}

(2)設(shè)置失效請求校驗攔截器昵济。

XHttpSDK.addInterceptor(new CustomExpiredInterceptor()); //請求失效校驗攔截器

自定義API請求

自定義請求響應(yīng)的API結(jié)構(gòu)

如果你不想使用默認(rèn)的ApiResult實體作為統(tǒng)一的服務(wù)端響應(yīng)實體,比如說你想要下面的響應(yīng)實體:

private int errorCode; //請求的錯誤碼
private String errorInfo; //請求錯誤的原因描述
private T result; //請求的結(jié)果
private long timeStamp; //服務(wù)端返回的時間戳

(1)首先野揪,繼承ApiResult實體访忿,重寫其getCodegetMsg斯稳、isSuccessgetData方法海铆。

public class CustomApiResult<T> extends ApiResult<T> {

    private int errorCode;
    private String errorInfo;
    private T result;
    private long timeStamp;

    public int getErrorCode() {
        return errorCode;
    }

    public CustomApiResult<T> setErrorCode(int errorCode) {
        this.errorCode = errorCode;
        return this;
    }

    public String getErrorInfo() {
        return errorInfo;
    }

    public CustomApiResult<T> setErrorInfo(String errorInfo) {
        this.errorInfo = errorInfo;
        return this;
    }

    public T getResult() {
        return result;
    }

    public CustomApiResult<T> setResult(T result) {
        this.result = result;
        return this;
    }

    public long getTimeStamp() {
        return timeStamp;
    }

    public CustomApiResult<T> setTimeStamp(long timeStamp) {
        this.timeStamp = timeStamp;
        return this;
    }

    @Override
    public int getCode() {
        return errorCode;
    }

    @Override
    public String getMsg() {
        return errorInfo;
    }

    @Override
    public boolean isSuccess() {
        return errorCode == 0;
    }

    @Override
    public T getData() {
        return result;
    }

    @Override
    public String toString() {
        return "ApiResult{" +
                "errorCode='" + errorCode + '\'' +
                ", errorInfo='" + errorInfo + '\'' +
                ", timeStamp='" + timeStamp + '\'' +
                ", result=" + result +
                '}';
    }
}

(2)進(jìn)行請求的時候使用execute(CallBackProxy)或者execute(CallClazzProxy方法進(jìn)行請求

XHttp.get("/test/testCustomResult")
            .execute(new CallBackProxy<CustomApiResult<Boolean>, Boolean>(new TipRequestCallBack<Boolean>() {
                @Override
                public void onSuccess(Boolean response) throws Throwable {
                    ToastUtils.toast("請求成功:" + response);
                }
            }){});

如果你覺得寫一長串比較麻煩,你可以自定義請求繼承你需要的請求方式挣惰,例如這里是get請求卧斟,我們可以這樣寫:

public class CustomGetRequest extends GetRequest {

    public CustomGetRequest(String url) {
        super(url);
    }

    @Override
    public <T> Observable<T> execute(Type type) {
        return execute(new CallClazzProxy<CustomApiResult<T>, T>(type) {
        });
    }

    @Override
    public <T> Disposable execute(CallBack<T> callBack) {
        return execute(new CallBackProxy<CustomApiResult<T>, T>(callBack) {
        });
    }
}

然后我們就可以用自定義的CustomGetRequest進(jìn)行請求了,是不是簡化了很多呢。

new CustomGetRequest("/test/testCustomResult")
        .execute(new TipRequestCallBack<Boolean>() {
            @Override
            public void onSuccess(Boolean response) throws Throwable {
                ToastUtils.toast("請求成功:" + response);
            }
        });

使用自定義的retrofit接口

如果你對retrofit接口情有獨鐘憎茂,我也提供了相應(yīng)的api方便調(diào)用.

1.定義retrofit接口珍语。例如我定義一個用戶添加的接口:

/**
 * 使用的是retrofit的接口定義
 */
public interface UserService {
    @POST("/user/registerUser/")
    @Headers({"Content-Type: application/json", "Accept: application/json"})
    Observable<ApiResult<Boolean>> registerUser(@Body RequestBody jsonBody);


    @POST("/user/registerUser/")
    @Headers({"Content-Type: application/json", "Accept: application/json"})
    Observable<ApiResult> register(@Body RequestBody jsonBody);
}

2.使用XHttp.custom()構(gòu)建的CustomRequest進(jìn)行請求,你可以使用apiCallcall進(jìn)行請求唇辨。

  • apiCall: 針對的是retrofit定義的接口廊酣,返回的是Observable<ApiResult<T>>的情況。對于上面定義的第一個接口registerUser赏枚。

  • call: 針對的是retrofit定義的接口亡驰,返回的是Observable<T>的情況晓猛。對于上面定義的第二個接口register

使用示例如下:

CustomRequest request = XHttp.custom();
request.apiCall(request.create(TestApi.UserService.class)
        .registerUser(HttpUtils.getJsonRequestBody(UserManager.getInstance().getRandomUser())))
        .subscribeWith(new TipRequestSubscriber<Boolean>() {
            @Override
            protected void onSuccess(Boolean aBoolean) {
                ToastUtils.toast("添加用戶成功!");
            }
        });
CustomRequest request = XHttp.custom();
request.call(request.create(TestApi.UserService.class)
        .register(HttpUtils.getJsonRequestBody(UserManager.getInstance().getRandomUser())))
        .subscribeWith(new TipRequestSubscriber<ApiResult>() {
            @Override
            protected void onSuccess(ApiResult apiResult) {
                ToastUtils.toast("添加用戶成功!");
                showResult(JsonUtil.toJson(apiResult));
            }
        });

緩存策略

目前框架提供了如下8種緩存策略:

  • NO_CACHE: 不使用緩存(默認(rèn)方式)

  • DEFAULT: 完全按照HTTP協(xié)議的默認(rèn)緩存規(guī)則凡辱,走OKhttp的Cache緩存

  • FIRST_REMOTE: 先請求網(wǎng)絡(luò)戒职,請求網(wǎng)絡(luò)失敗后再加載緩存

  • FIRST_CACHE: 先加載緩存,緩存沒有再去請求網(wǎng)絡(luò)

  • ONLY_REMOTE: 僅加載網(wǎng)絡(luò)透乾,但數(shù)據(jù)依然會被緩存

  • ONLY_CACHE: 只讀取緩存

  • CACHE_REMOTE: 先使用緩存洪燥,不管是否存在,仍然請求網(wǎng)絡(luò)乳乌,會回調(diào)兩次

  • CACHE_REMOTE_DISTINCT: 先使用緩存捧韵,不管是否存在,仍然請求網(wǎng)絡(luò)汉操,會先把緩存回調(diào)給你再来,等網(wǎng)絡(luò)請求回來發(fā)現(xiàn)數(shù)據(jù)是一樣的就不會再返回,否則再返回(這樣做的目的是防止數(shù)據(jù)是一樣的你也需要刷新界面)

對于緩存的實現(xiàn)磷瘤,提供了磁盤緩存LruDiskCache和內(nèi)存緩存LruMemoryCache兩種實現(xiàn)芒篷,默認(rèn)使用的是磁盤緩存。

(1)可以先進(jìn)行緩存的全局性配置采缚,配置緩存的有效期针炉、緩存大小,緩存路徑扳抽、序列化器等篡帕。

XHttp.getInstance()
        .setIsDiskCache(true) //設(shè)置使用磁盤緩存
        .setCacheTime(60 * 1000) //設(shè)置全局緩存有效期為一分鐘
        .setCacheVersion(1) //設(shè)置全局緩存的版本
        .setCacheDirectory(Utils.getDiskCacheDir(this, "XHttp")) //設(shè)置全局緩存保存的目錄路徑
        .setCacheMode(CacheMode.NO_CACHE) //設(shè)置全局的緩存策略
        .setCacheDiskConverter(new GsonDiskConverter())//默認(rèn)緩存使用序列化轉(zhuǎn)化
        .setCacheMaxSize(50 * 1024 * 1024);//設(shè)置緩存大小為50M

(2)在進(jìn)行請求的時候,設(shè)置緩存模式和緩存的key即可摔蓝。如下:

XHttp.get("/book/getAllBook")
        .timeOut(10 * 1000)//測試局部超時10s
        .cacheMode(mCacheMode)
        .cacheKey(CACHE_KEY)//緩存key
        .retryCount(5)//重試次數(shù)
        .cacheTime(5 * 60)//緩存時間300s赂苗,默認(rèn)-1永久緩存  okhttp和自定義緩存都起作用
        .cacheDiskConverter(new GsonDiskConverter())//默認(rèn)使用的是 new SerializableDiskConverter();
        .timeStamp(true)
        .execute(new ProgressLoadingCallBack<CacheResult<List<Book>>>(mIProgressLoader) {
            @Override
            public void onSuccess(CacheResult<List<Book>> cacheResult) {
                ToastUtils.toast("請求成功!");
                String from;
                if (cacheResult.isFromCache) {
                    from = "我來自緩存";
                } else {
                    from = "我來自遠(yuǎn)程網(wǎng)絡(luò)";
                }
                showResult(from + "\n" + JsonUtil.toJson(cacheResult.data));
            }

            @Override
            public void onError(ApiException e) {
                super.onError(e);
                ToastUtils.toast(e.getDisplayMessage());
            }
        });

混淆配置

#XHttp2
-keep class com.xuexiang.xhttp2.model.** { *; }
-keep class com.xuexiang.xhttp2.cache.model.** { *; }
-keep class com.xuexiang.xhttp2.cache.stategy.**{*;}
-keep class com.xuexiang.xhttp2.annotation.** { *; }

#okhttp
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault
-dontwarn javax.annotation.**

# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions

# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
    long producerIndex;
    long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

#如果用到Gson解析包的愉耙,直接添加下面這幾行就能成功混淆贮尉,不然會報錯
-keepattributes Signature
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
-keep class org.xz_sale.entity.**{*;}
-keep class com.google.gson.** {*;}
-keep class com.google.**{*;}
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }

特別感謝

https://github.com/zhou-you/RxEasyHttp

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市朴沿,隨后出現(xiàn)的幾起案子猜谚,更是在濱河造成了極大的恐慌,老刑警劉巖赌渣,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魏铅,死亡現(xiàn)場離奇詭異,居然都是意外死亡坚芜,警方通過查閱死者的電腦和手機览芳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸿竖,“玉大人沧竟,你說我怎么就攤上這事铸敏。” “怎么了悟泵?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵杈笔,是天一觀的道長。 經(jīng)常有香客問我糕非,道長蒙具,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任朽肥,我火速辦了婚禮禁筏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘衡招。我一直安慰自己融师,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布蚁吝。 她就那樣靜靜地躺著旱爆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窘茁。 梳的紋絲不亂的頭發(fā)上怀伦,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音山林,去河邊找鬼房待。 笑死,一個胖子當(dāng)著我的面吹牛驼抹,可吹牛的內(nèi)容都是我干的桑孩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼框冀,長吁一口氣:“原來是場噩夢啊……” “哼流椒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起明也,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤宣虾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后温数,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绣硝,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年撑刺,在試婚紗的時候發(fā)現(xiàn)自己被綠了鹉胖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甫菠,靈堂內(nèi)的尸體忽然破棺而出败许,到底是詐尸還是另有隱情,我是刑警寧澤淑蔚,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布市殷,位于F島的核電站,受9級特大地震影響刹衫,放射性物質(zhì)發(fā)生泄漏醋寝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一带迟、第九天 我趴在偏房一處隱蔽的房頂上張望音羞。 院中可真熱鬧,春花似錦仓犬、人聲如沸嗅绰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窘面。三九已至,卻和暖如春叽躯,著一層夾襖步出監(jiān)牢的瞬間财边,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工点骑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酣难,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓黑滴,卻偏偏與公主長得像憨募,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子袁辈,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

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

  • 創(chuàng)業(yè)就是像獅子和老虎鉆火圈那樣吵瞻,跳過一個個看起來挺美妙的甜甜圈葛菇。
    舟記_閱讀 762評論 0 1
  • 剛剛看了Dday,劇中醫(yī)生反復(fù)強度要讓患者活著橡羞,生命的最低限是活著,會呼吸有心跳……這樣就夠了嗎济舆?如果遇到地震卿泽,如...
    游游游游上天的魚閱讀 132評論 0 0
  • 其實今天是全程懵逼的一天,但我還是要取個浪漫的標(biāo)題。我們的目標(biāo)是不走回頭路签夭,今天走的最多的卻是回頭路齐邦,也是很無奈。...
    晚紅舒閱讀 151評論 0 0