Rxjava+retrofit 新增公共請求引數及請求結果封裝
RxJava + Retrofit + okHttp 做為當前android主流的網路請求框架,寫一些自己在專案中的應用!本文主要內容:1.為所用請求新增公共的請求引數。2.封裝適合於自己專案的伺服器介面資料處理類。
開發環境
retrofitVersion = ‘2.2.0’
ok3Version = ‘3.6.0’
rxjavaVersion = ‘1.2.7’
rxAndroidVersion = ‘1.2.1’
rxlifecycleVersion = ‘0.3.0’
其中,rxlifecycleVersion是為了防止RxJava中subscription導致記憶體洩漏而誕生的。例如:當要引用的view(fragment,activity)被銷燬,但rxjava生命週期未結束時,再次呼叫已銷燬的view(fragment,activity)會產生異常!
- 形如一個Post介面:http://base/interface/xxx
要求每個介面都有一個必傳欄位:APPTOKEN(記錄使用者登陸狀態)
返回值可能為:
{"erron":"100000","msg":"成功"}//介面返回單純訊息
{"data":{},"erron":"100000","msg":"成功"}//介面返回帶有詳細欄位
{"nowPage":"1","totalPage":"4","data":[],"erron":"100000","msg":"成功"}//介面返回分頁資料
首先為所有請求都加一個預設的apptoken欄位。使用到一個Okhttp3的網路攔截器Interceptor。
核心程式碼:
builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(logging);
}
builder.connectTimeout(6 * 1000, TimeUnit.MILLISECONDS)//10s連線超時
.readTimeout(10 * 1000, TimeUnit.MILLISECONDS)
.writeTimeout(10 * 1000, TimeUnit.MILLISECONDS);
//新增token攔截器
builder.addInterceptor(new TokenInterceptord());
//新增網路異常攔截器
Interceptor netErrorInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
if (NetWorkUtil.isNetworkConnected())
return chain.proceed(chain.request());
else {
String res = "{\"erron\":\"-1200\",\"msg\":\"網路連線失敗\",\"nowPage\":\"0\",\"totalPage\":\"0\"}";
Response response = new Response.Builder()
.code(200)
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.addHeader("Cache-Control", "no-cache")
.addHeader("Content-Type", "text/html; charset=utf-8")
.body(RealResponseBody.create(MediaType.parse("text"), res.getBytes("utf-8")))
.build();
return response;
}
}
};
builder.addNetworkInterceptor(netErrorInterceptor);
上文中添加了兩個網路攔截器,其中新增apptoken的攔截器是應用攔截器(Application Intercetor),新增網路異常的攔截器是網路攔截器(netErrorInterceptor)。
新增網路異常的攔截器主要作用是為了後續結果集封裝的時候不需要額外處理網路異常的情況。
apptoken引數新增攔截器的詳細實現:
public class TokenInterceptord implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder requestBuilder = request.newBuilder();
if (canInjectIntoBody(request)) {
FormBody.Builder formBodyBuilder = new FormBody.Builder();
formBodyBuilder.add("apptoken", SharedPreUtil.getAppToken());//此處新增apptoken引數
RequestBody formBody = formBodyBuilder.build();
String postBodyString = bodyToString(request.body());
postBodyString += ((postBodyString.length() > 0) ? "&" : "") + bodyToString(formBody);
requestBuilder.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), postBodyString));
}
request=requestBuilder.build();
return chain.proceed(request);
}
private boolean canInjectIntoBody(Request request) {
if (request == null) {
return false;
}
if (!TextUtils.equals(request.method(), "POST")) {
return false;
}
RequestBody body = request.body();
if (body == null) {
return false;
}
MediaType mediaType = body.contentType();
if (mediaType == null) {
return false;
}
if (!TextUtils.equals(mediaType.subtype(), "x-www-form-urlencoded")) {
return false;
}
return true;
}
private String bodyToString(final RequestBody request) {
try {
final RequestBody copy = request;
final Buffer buffer = new Buffer();
if (copy != null)
copy.writeTo(buffer);
else
return "";
return buffer.readUtf8();
} catch (final IOException e) {
return "did not work";
}
}
}
其實就是攔截request請求並添加了一個apptoken的post欄位。
* 接下來分析3種返回型別,一般app介面應該差不多就這3種情況。
1. 針對第一種:
{“erron”:”100000”,”msg”:”成功”}//介面返回單純訊息資訊
自定義處理結果BaseBeanSubscribe
public abstract class BaseBeanSubscribe<T extends BaseBean> extends Subscriber<T> {
public enum ERROR_TYPE {
DATA_EMPTY,//資料為空
DATA_ERROR,//資料解析錯誤
NET_ERROR,//網路連線錯誤
TOKEEN_ERROR,//apptoken無效或異常。
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
LogUtils.i("Zuzu", "ResultSubscriber-->exception");
e.printStackTrace();
if (NetWorkUtil.isNetworkConnected()) {
onError(ERROR_TYPE.DATA_ERROR, "未知異常!");
} else {
onError(ERROR_TYPE.NET_ERROR, "網路連線超時,請檢測您的網路狀況!");
}
LogUtils.i("Zuzu", "request fail!");
}
@Override
public void onNext(T t) {
if (t == null) {
LogUtils.i("Zuzu", "data_empty!");
onError(ERROR_TYPE.DATA_EMPTY, "伺服器異常!");
} else if ("100002".equals(t.getErron()) || "100003".equals(t.getErron())) {
String msg;
if ("-1".equals(SharedPreUtil.getAppToken())) {
msg = "您還沒有登入,不能進行該操作!";
} else {
msg = "抱歉您賬戶已過期,請重新登入!";
}
onError(ERROR_TYPE.TOKEEN_ERROR, msg);
} else if ("100000".equals(t.getErron())) {
sucess();
} else if ("-1200".equals(t.getErron())) {
onError(ERROR_TYPE.NET_ERROR, t.getMsg());
} else {
onError(ERROR_TYPE.DATA_ERROR, t.getMsg());
}
}
//請求成功
protected abstract void sucess();
//發生錯誤!
protected abstract void onError(ERROR_TYPE type, String msg);
}
其中basebean
public class BaseBean<T> {
private String erron;
private String msg;
private T data;
...set get方法...
}
該程式碼段實現了根據結果過濾集處理錯誤的情況。由於上文已經處理過了網路異常情況,因此此處可以直接獲取網路異常情況。
2 . 第二種:
{“data”:{},”erron”:”100000”,”msg”:”成功”}//介面返回帶有詳細欄位
public abstract class ResultSubscriber<D, T extends BaseBean<D>> extends BaseBeanSubscribe<T> {
@Override
public void onNext(T t) {
if (t != null && "100000".equals(t.getErron())) {
onSuccess(t.getData());
return;
}
super.onNext(t);
}
@Override
protected void sucess() {
}
//請求成功
protected abstract void onSuccess(D data);
}
這樣寫的好處是onSuccess中返回的資料是”data”:{}的資料。
3. 最後一種型別,分頁:
{“nowPage”:”1”,”totalPage”:”4”,”data”:[],”erron”:”100000”,”msg”:”成功”}
public abstract class PageListResultSubscriber<D, T extends BasePageBean<D>> extends ResultSubscriber<List<D>, T> {
@Override
public void onNext(T t) {
if (t != null && "100000".equals(t.getErron())) {
onSuccess(t.getData(), Integer.parseInt(t.getNowPage()), Integer.parseInt(t.getTotalPage()));
return;
}
super.onNext(t);
}
protected abstract void onSuccess(List<D> data, int nowPage, int totalPage);
@Override
protected void onSuccess(List<D> data) {
//空實現
}
onSucess中返回List集合,以及totalpage,及nowpage
* 最後完成上述準備工作,程式碼中的使用就簡單多了
eg:有一個介面
@POST(“admin/index/surnames”)
@FormUrlEncoded
Observable getBjxList(@Field(“id”) String id);
mService.getBjxList("0")
.compose(getView().<BjxListBean>bindToLifecycle())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ResultSubscriber<List<BjxListBean.DataBean>,BjxListBean>() {
@Override
protected void onSuccess(List<BjxListBean.DataBean> data) {
if(getView()!=null)
{
getView().showSucessUi(data);
}
}
@Override
protected void onError(ERROR_TYPE type, String msg) {
if(getView()!=null)
{
getView().showError(type,msg);
}
}
});
其中BjxListBean
public class BjxListBean extends BaseBean<List<BjxListBean.DataBean>> {
public static class DataBean implements Serializable{
private String id;
private String xing;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getXing() {
return xing;
}
public void setXing(String xing) {
this.xing = xing;
}
}
}