Home

Awesome

XHttp2

api I Star

一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。还不赶紧点击使用说明文档,体验一下吧!

在提issue前,请先阅读【提问的智慧】,并严格按照issue模板进行填写,节约大家的时间。

在使用前,请一定要仔细阅读使用说明文档,重要的事情说三遍!!!

在使用前,请一定要仔细阅读使用说明文档,重要的事情说三遍!!!

在使用前,请一定要仔细阅读使用说明文档,重要的事情说三遍!!!

关于我

公众号掘金知乎CSDN简书思否哔哩哔哩今日头条
我的Android开源之旅点我点我点我点我点我点我点我

X系列库快速集成

为了方便大家快速集成X系列框架库,我提供了一个空壳模版供大家参考使用: https://github.com/xuexiangjys/TemplateAppProject


特征

点击查看项目设计类图

Star趋势图

Stargazers over time


1、演示(请star支持)

1.1、Demo演示动画

1.2、Demo下载

downloads

1.3、api服务安装

服务端的搭建详细请点击查看

2、如何使用

目前支持主流开发工具AndroidStudio的使用,直接配置build.gradle,增加依赖即可.

2.1、Android Studio导入方法,添加Gradle依赖

1.先在项目根目录的 build.gradle 的 repositories 添加:

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

2.然后在应用项目(一般是app)的 build.gradle 的 dependencies 添加:

以下是版本说明,选择一个即可。

dependencies {
  ...
  implementation 'com.github.xuexiangjys:XHttp2:2.0.4'

  implementation 'com.google.code.gson:gson:2.8.5'
  implementation 'com.squareup.okhttp3:okhttp:3.10.0'
  implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
  implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
}
dependencies {
  ...
  implementation 'com.github.xuexiangjys:XHttp2:1.0.4'

  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);   //初始化网络请求框架,必须首先执行
XHttpSDK.debug("XHttp");  //需要调试的时候执行
XHttpSDK.setBaseUrl(SettingSPUtils.getInstance().getApiURL());  //设置网络请求的基础地址

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

除了上述的操作以外,你还可以使用XHttp.getInstance()对网络请求框架进行全局性参数配置,配置一些公用默认的参数,这样我们就不需要为每个请求都进行设置。方法如下:

方法名备注
debug设置日志的打印模式
setBaseUrl设置全局baseUrl
setSubUrl设置全局subUrl
setReadTimeOut设置全局读取超时时间
setWriteTimeOut设置全局写入超时时间
setConnectTimeout设置全局连接超时时间
setTimeout设置全局超时时间
setRetryCount设置全局超时重试次数
setRetryDelay设置全局超时重试延迟时间
setRetryIncreaseDelay设置全局超时重试延迟叠加时间
setCacheMode设置全局的缓存模式
setIsDiskCache设置是否是磁盘缓存
setMemoryMaxSize设置内存缓存的最大数量
setCacheTime设置全局的缓存过期时间
setCacheMaxSize设置全局的磁盘缓存大小,默认50M
setCacheDirectory设置全局缓存的路径,默认是应用包下面的缓存
setCacheDiskConverter设置全局缓存的转换器
addCommonParams添加全局公共请求参数
addCommonHeaders添加全局公共请求参数
addInterceptor添加全局拦截器
addNetworkInterceptor添加全局网络拦截器
setOkproxy全局设置OkHttpClient的代理
setOkconnectionPool设置全局OkHttpClient的请求连接池
setOkclient全局为Retrofit设置自定义的OkHttpClient
addConverterFactory设置全局Converter.Factory,默认GsonConverterFactory.create()
addCallAdapterFactory设置全局CallAdapter.Factory,默认RxJavaCallAdapterFactory.create()
setHostnameVerifier设置https的全局访问规则
setCertificates设置https的全局自签名证书
setCookieStore设置全局cookie存取规则
setStrictMode设置严格模式,在严格模式下,json返回的data数据不能为null

如何进行网络请求

需要注意的是,所以请求返回的结果必须要满足以下格式:

{
    "Code":0, //响应码,0为成功,否则失败
    "Msg":"", //请求失败的原因说明
    "Data":{} //返回的数据对象
}

其中CodeMsgData建议使用大写字母,当然使用小写字母也没有问题,否则无法解析成功。

【注意】这里Code为0才代表请求成功,如果你的Code码不是0代表成功,你可以使用XHttpSDK.setSuccessCode设置代表成功的值。

需要自定义返回的实体API请点击查看

1、使用XHttp默认api进行请求

1.使用XHttp.post、XHttp.get、XHttp.delete、XHttp.put、XHttp.downLoad构建请求。

2.修改request的请求参数。

方法名类型默认值备注
baseUrlString设置该请求的baseUrl
timeOutlong15000设置超时时间
accessTokenbooleanfalse是否需要验证token
threadTypeString设置请求的线程调度类型
syncRequestbooleanfalse设置是否是同步请求(不开子线程)
onMainThreadbooleantrue请求完成后是否回到主线程
upJsonString""上传Json格式的数据请求
keepJsonbooleanfalse返回保持json的形式
retryCountint设置超时重试的次数
retryDelayint设置超时重试的延迟时间
retryIncreaseDelayint设置超时重试叠加延时
headersHttpHeaders添加头信息
paramsHttpParams设置表单请求参数
cacheModeCacheModeCacheMode.NO_CACHE设置缓存的模式

3.调用execute方法执行请求。execute一般有如下两种方式:

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封装的统一请求实体进行请求【仅支持post请求】

在使用它之前,需要下载/定义对应的实体协议,如下:

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

    /**
     *
     */
    public User request;

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

1.注解说明

注解参数类型默认值备注
baseUrlString""设置该请求的baseUrl
urlString""请求网络接口地址
timeoutlong15000设置超时时间
keepJsonbooleanfalse是否保存json
accessTokenbooleantrue设置是否需要验证token
cacheModeCacheModeCacheMode.NO_CACHE设置请求的缓存模式
cacheTimelong-2(使用全局设置)设置缓存有效时间
注解参数类型默认值备注
keyString/请求参数的key

2.使用XHttpSDK进行请求。

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代理进行请求

在使用它之前,需要下载/定义对应的接口协议,如下:

/**
 * 图书管理
 */
public interface IBook {
    /**
     * 购买书
     *
     * @param bookId 图书ID
     * @param userId 用户ID
     * @param number 购买数量
     */
    @NetMethod(parameterNames = {"bookId", "userId", "number"}, url = "/order/addOrder/", accessToken = false)
    Observable<Boolean> buyBook(int bookId, int userId, int number);
    /**
     * 获取图书
     *
     * @param pageNum 第几页数
     * @param pageSize 每页的数量
     */
    @NetMethod(parameterNames = {"pageNum", "pageSize"}, paramType = FORM_BODY, url = "/book/findBooks/", accessToken = false)
    Observable<List<Book>> getBooks(int pageNum, int pageSize);

    /**
     * 获取所有图书
     *
     */
    @NetMethod(action = GET, url = "/book/getAllBook", accessToken = false)
    Observable<List<Book>> getAllBooks();
}

1.注解说明

注解参数类型默认值备注
parameterNamesString[]{}参数名集合
paramTypeintJSON=1param的类型
actionStringPOST="post"请求动作
baseUrlString""设置该请求的baseUrl
urlString""请求网络接口地址
timeoutlong15000设置超时时间
keepJsonbooleanfalse是否保存json
accessTokenbooleantrue设置是否需要验证token
cacheModeCacheModeCacheMode.NO_CACHE设置请求的缓存模式
cacheTimelong-2(使用全局设置)设置缓存有效时间
cacheKeyIndexint-1(所有参数)作为缓存key的请求参数索引

2.使用XHttpProxy进行请求。

构建一个XHttpProxy,将定义的api接口传入后,直接调用接口进行请求。

构造XHttpProxy可以传入ThreadType,默认是ThreadType.TO_MAIN

【注意】请确保网络请求在主线程中【实质是异步请求(切换到io线程),且响应的线程又切换至主线程】

【注意】请确保网络请求在主线程中【实质是异步请求(切换到io线程),不过响应的线程不变,还是之前请求的那个io线程】

【注意】请确保网络请求在子线程中才可以使用该类型【实质是不做任何线程调度的同步请求】

3.请求使用演示。

//使用XHttpProxy进行接口代理请求
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传递附带的参数,使用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); //更新进度条
            }

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

高阶网络请求操作

请求生命周期绑定

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.网络请求生命周期和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的操作符进行绑定。

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

拦截器

日志拦截器

(1)框架默认提供一个实现好的日志拦截器HttpLoggingInterceptor,通过XHttpSDK.debug("XHttp");就可以设置进去,它有5种打印模式

(2)如果需要对网络请求的相关参数进行自定义记录的话,可以继承HttpLoggingInterceptor实现一个自己的网络请求日志拦截器,重写logForRequestlogForResponse两个方法即可。

(3)设置自定义的日志拦截器.

XHttpSDK.debug(new CustomLoggingInterceptor());

动态参数添加拦截器

有时候,我们需要对所有请求添加一些固定的请求参数,但是这些参数的值又是变化的,这个时候我们就需要动态添加请求参数【例如,请求的token、时间戳以及签名等】

(1)继承BaseDynamicInterceptor,实现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:是原有的全局参数+局部参数+新增的动态参数
}

(2)设置动态参数添加拦截器。

XHttpSDK.addInterceptor(new CustomDynamicInterceptor()); //设置动态参数添加拦截器

失效请求校验拦截器

当服务端返回一些独特的错误码(一般是token校验错误、失效,请求过于频繁等),需要我们进行全局性的拦截捕获,并作出相应的响应时,我们就需要定义一个特殊的拦截器求处理这些请求。

(1)继承BaseExpiredInterceptor,实现isResponseExpiredresponseExpired方法,如下:

/**
 * 判断是否是失效的响应
 *
 * @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;
}

/**
 * 失效响应的处理
 *
 * @return 获取新的有效请求响应
 */
@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(TestApi.IAuthorization.class, ThreadType.IN_THREAD)
                        .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重新进行请求...");
                        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(), "请先进行登录!");
            }
            break;
        case KEY_UNREGISTERED_USER:
            return HttpUtils.getErrorResponse(oldResponse, expiredInfo.getCode(), "非法用户登录!");
        default:
            break;
    }
    return null;
}

(2)设置失效请求校验拦截器。

XHttpSDK.addInterceptor(new CustomExpiredInterceptor()); //请求失效校验拦截器

自定义API请求

自定义请求响应的API结构

如果你不想使用默认的ApiResult实体作为统一的服务端响应实体,比如说你想要下面的响应实体:

private int errorCode; //请求的错误码
private String errorInfo; //请求错误的原因描述
private T result; //请求的结果
private long timeStamp; //服务端返回的时间戳

(1)首先,继承ApiResult实体,重写其getCodegetMsgisSuccessgetDatasetData方法。

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 void setData(T data) {
        result = data;
    }

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

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

(2)进行请求的时候使用execute(CallBackProxy)或者execute(CallClazzProxy方法进行请求

XHttp.get("/test/testCustomResult")
            .execute(new CallBackProxy<CustomApiResult<Boolean>, Boolean>(new TipRequestCallBack<Boolean>() {
                @Override
                public void onSuccess(Boolean response) throws Throwable {
                    ToastUtils.toast("请求成功:" + response);
                }
            }){});  //千万注意,这里的{}一定不能去掉,否则解析错误

【注意】上面提示的{}一定不能去掉,否则解析错误, 会报"ApiResult.class.isAssignableFrom(cls) err!!"的错误。

如果你觉得写一长串比较麻烦,你可以自定义请求继承你需要的请求方式,例如这里是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进行请求了,是不是简化了很多呢。

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

使用自定义的retrofit接口

如果你对retrofit接口情有独钟,我也提供了相应的api方便调用.

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 User user);
}

2.使用XHttp.custom()构建的CustomRequest进行请求,你可以使用apiCallcall进行请求。

使用示例如下:

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种缓存策略:

对于缓存的实现,提供了磁盘缓存LruDiskCache和内存缓存LruMemoryCache两种实现,默认使用的是磁盘缓存。

(1)可以先进行缓存的全局性配置,配置缓存的有效期、缓存大小,缓存路径、序列化器等。

XHttp.getInstance()
        .setIsDiskCache(true) //设置使用磁盘缓存
        .setCacheTime(60 * 1000) //设置全局缓存有效期为一分钟
        .setCacheVersion(1) //设置全局缓存的版本
        .setCacheDirectory(Utils.getDiskCacheDir(this, "XHttp")) //设置全局缓存保存的目录路径
        .setCacheMode(CacheMode.NO_CACHE) //设置全局的缓存策略
        .setCacheDiskConverter(new GsonDiskConverter())//默认缓存使用序列化转化
        .setCacheMaxSize(50 * 1024 * 1024);//设置缓存大小为50M

(2)在进行请求的时候,设置缓存模式和缓存的key即可。如下:

XHttp.get("/book/getAllBook")
        .timeOut(10 * 1000)//测试局部超时10s
        .cacheMode(mCacheMode)
        .cacheKey(CACHE_KEY)//缓存key
        .retryCount(5)//重试次数
        .cacheTime(5 * 60)//缓存时间300s,默认-1永久缓存  okhttp和自定义缓存都起作用
        .cacheDiskConverter(new GsonDiskConverter())//默认使用的是 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 = "我来自远程网络";
                }
                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

如果觉得项目还不错,可以考虑打赏一波

你的打赏是我维护的动力,我将会列出所有打赏人员的清单在下方作为凭证,打赏前请留下打赏项目的备注!

pay.png

感谢下面小伙伴的打赏:

姓名金额方式
*声50¥微信
**东5¥支付宝

联系方式

更多资讯内容,欢迎扫描关注我的个人微信公众号:【我的Android开源之旅】