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加我合砂。