一个功能强悍的网络请求库,使用RxJava2 + Retrofit2 + OKHttp组合进行封装。
服务端的搭建详细 请点击查看
目前支持主流开发工具AndroidStudio的使用,直接配置build.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); //初始化网络请求框架,必须首先执行 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存取规则 |
1.使用XHttp.post、XHttp.get、XHttp.delete、XHttp.put、XHttp.downLoad构建请求。
2.修改request的请求参数。
方法名 | 类型 | 默认值 | 备注 |
---|---|---|---|
baseUrl | String | / | 设置该请求的baseUrl |
timeOut | long | 10000 | 设置超时时间 |
accessToken | boolean | false | 是否需要验证token |
threadType | String | / | 设置请求的线程调度类型 |
syncRequest | boolean | false | 设置是否是同步请求(不开子线程) |
onMainThread | boolean | true | 请求完成后是否回到主线程 |
upJson | String | "" | 上传Json格式的数据请求 |
keepJson | boolean | false | 返回保持json的形式 |
retryCount | int | / | 设置超时重试的次数 |
retryDelay | int | / | 设置超时重试的延迟时间 |
retryIncreaseDelay | int | / | 设置超时重试叠加延时 |
headers | HttpHeaders | / | 添加头信息 |
params | HttpParams | / | 设置表单请求参数 |
cacheMode | CacheMode | CacheMode.NO_CACHE | 设置缓存的模式 |
3.调用 execute
方法执行请求。execute一般有如下两种方式:
execute(CallBack callBack): 直接回调结果。
execute(Class clazz)和execute(Type type): 回调Observable对象,可通过订阅获取到结果。
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(); } });
在使用它之前,需要下载/定义对应的实体协议,如下:
@RequestParams(url = "/user/addUser", accessToken = false) public static class UserService_AddUser extends XHttpRequest { /** * */ public User request; @Override protected Boolean getResponseEntityType() { return null; } }
1.注解说明
注解参数 | 类型 | 默认值 | 备注 |
---|---|---|---|
baseUrl | String | "" | 设置该请求的baseUrl |
url | String | "" | 请求网络接口地址 |
timeout | long | 15000 | 设置超时时间 |
accessToken | boolean | true | 设置是否需要验证token |
cacheMode | CacheMode | CacheMode.NO_CACHE | 设置请求的缓存模式 |
注解参数 | 类型 | 默认值 | 备注 |
---|---|---|---|
key | String | / | 请求参数的key |
2.使用XHttpSDK进行请求。
post(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread): 获取PostRequest请求(使用实体参数名作为请求Key)。
postToMain(XHttpRequest xHttpRequest): 获取PostRequest请求(主线程->主线程)。
postToIO(XHttpRequest xHttpRequest): 获取PostRequest请求(主线程->子线程)。
postInThread(XHttpRequest xHttpRequest): 获取PostRequest请求(子线程->子线程)。
execute(XHttpRequest xHttpRequest, boolean isSyncRequest, boolean toMainThread) : 执行PostRequest请求,返回observable对象(使用实体参数名作为请求Key)。
executeToMain(XHttpRequest xHttpRequest)
: 执行post请求,返回observable对象(主线程->主线程)
executeToMain(XHttpRequest xHttpRequest,BaseSubscriber<T> subscriber)
: 执行post请求并进行订阅,返回订阅信息(主线程->主线程)
3.请求使用演示。
XHttpRequest req = ApiProvider.getAddUserReq(getRandomUser()); XHttpSDK.executeToMain(req, new ProgressLoadingSubscriber<Boolean>(mIProgressLoader) { @Override public void onSuccess(Boolean aBoolean) { ToastUtils.toast("用户添加成功!"); mRefreshLayout.autoRefresh(); } });
在使用它之前,需要下载/定义对应的接口协议,如下:
/** * 订单 */ 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.注解说明
注解参数 | 类型 | 默认值 | 备注 |
---|---|---|---|
ParameterNames | String[] | {} | 参数名集合 |
BaseUrl | String | "" | 设置该请求的baseUrl |
Url | String | "" | 请求网络接口地址 |
Timeout | long | 10000 | 设置超时时间 |
AccessToken | boolean | true | 设置是否需要验证token |
CacheMode | CacheMode | CacheMode.NO_CACHE | 设置请求的缓存模式 |
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(); } });
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种打印模式
NONE: 不打印log
BASIC: 只打印"请求首行"和"响应首行"。
HEADERS: 打印请求和响应的所有 Header
PARAM: 只打印请求和响应参数
BODY: 打印所有数据(默认是这种)
(2)如果需要对网络请求的相关参数进行自定义记录的话,可以继承 HttpLoggingInterceptor
实现一个自己的网络请求日志拦截器,重写 logForRequest
和 logForResponse
两个方法即可。
(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
,实现 isResponseExpired
和 responseExpired
方法,如下:
/** * 判断是否是失效的响应 * * @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(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重新进行请求..."); 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()); //请求失效校验拦截器
如果你不想使用默认的ApiResult实体作为统一的服务端响应实体,比如说你想要下面的响应实体:
private int errorCode; //请求的错误码 private String errorInfo; //请求错误的原因描述 private T result; //请求的结果 private long timeStamp; //服务端返回的时间戳
(1)首先,继承 ApiResult
实体,重写其 getCode
、 getMsg
、 isSuccess
和 getData
方法。
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)进行请求的时候使用 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); } }){});
如果你觉得写一长串比较麻烦,你可以自定义请求继承你需要的请求方式,例如这里是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接口情有独钟,我也提供了相应的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 RequestBody jsonBody); }
2.使用 XHttp.custom()
构建的 CustomRequest
进行请求,你可以使用 apiCall
和 call
进行请求。
apiCall: 针对的是retrofit定义的接口,返回的是Observable<ApiResult>的情况。对于上面定义的第一个接口 registerUser
。
call: 针对的是retrofit定义的接口,返回的是Observable的情况。对于上面定义的第二个接口 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: 不使用缓存(默认方式)
DEFAULT: 完全按照HTTP协议的默认缓存规则,走OKhttp的Cache缓存
FIRST_REMOTE: 先请求网络,请求网络失败后再加载缓存
FIRST_CACHE: 先加载缓存,缓存没有再去请求网络
ONLY_REMOTE: 仅加载网络,但数据依然会被缓存
ONLY_CACHE: 只读取缓存
CACHE_REMOTE: 先使用缓存,不管是否存在,仍然请求网络,会回调两次
CACHE_REMOTE_DISTINCT: 先使用缓存,不管是否存在,仍然请求网络,会先把缓存回调给你,等网络请求回来发现数据是一样的就不会再返回,否则再返回(这样做的目的是防止数据是一样的你也需要刷新界面)
对于缓存的实现,提供了磁盘缓存 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