打造屬於自己的網路請求框架(OKHttp+Retrofit+RxJava)
阿新 • • 發佈:2019-01-25
開發者們現在都在使用OkHttp了,在很多借鑑之後,現在也來封裝屬於自己的網路請求框架。
該框架使用Retrofit,OkHttp,RxJava,RxAndroid,Gson一起封裝。
客戶端請求一般分為如下幾步:
通過API向伺服器傳送請求------->伺服器收到請求然後響應(這裡有兩種情況,一是請求成功返回Json資料,二是去請求失敗返回失敗狀態)---------->客服端拿到伺服器返回狀態解析資料或者請求失敗提示使用者
根據以上思路來看程式碼:
註釋寫的很清楚了,但是在新增header的時候,根據自己需要新增。import android.os.Build; import java.io.IOException; import java.util.concurrent.TimeUnit; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; /** * Created by hedong on 2016/4/19. */ public class LocalService { public static final String API_BASE_URL = "http://www.tngou.net/api/info/";//主Api路徑 private static final LocalApi service = getRetrofit().create(LocalApi.class); private static Retrofit mRetrofit; private static OkHttpClient mOkHttpClient; public final static int CONNECT_TIMEOUT = 60; //設定連線超時時間 public final static int READ_TIMEOUT = 60; //設定讀取超時時間 public final static int WRITE_TIMEOUT = 60; //設定寫的超時時間 private static OkHttpClient genericClient() { HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); OkHttpClient httpClient = new OkHttpClient.Builder() .addNetworkInterceptor(interceptor) .retryOnConnectionFailure(true) .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)//設定讀取超時時間 .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)//設定寫的超時時間 .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)//設定連線超時時間 .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request() .newBuilder() .addHeader("source-terminal", "Android") //作業系統名稱(注:ios、android)//裝置型號 .addHeader("device-model", Build.MODEL) //裝置型號 .addHeader("os-version", Build.VERSION.RELEASE)//作業系統版本號 //.addHeader("app-name", name);//應用名稱 .build(); return chain.proceed(request); } }).build(); return httpClient; } public static LocalApi getApi() { return service; } protected static Retrofit getRetrofit() { if (null == mRetrofit) { if (null == mOkHttpClient) { mOkHttpClient = genericClient(); } mRetrofit = new Retrofit.Builder() .baseUrl(API_BASE_URL) .addConverterFactory(ResponseConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(mOkHttpClient) .build(); } return mRetrofit; } }
LocalApi是什麼呢,在這個類裡面我們定義請求方法,get,post等等,
public interface LocalApi {
//獲取類別
@GET("classify")
Observable<List<HealthClassifyBean>> getHealthClassify();
}
請求發出去了,看一下怎麼解析返回的json資料呢,自定義ResponseConverterFactory繼承自Converter.Factory
這只是跟Retrofit繫結,真正的拿到資料,解析的json的在下面:public class ResponseConverterFactory extends Converter.Factory { public static ResponseConverterFactory create() { return create(new Gson()); } public static ResponseConverterFactory create(Gson gson) { return new ResponseConverterFactory(gson); } private final Gson gson; private ResponseConverterFactory(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); this.gson = gson; } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return new GsonResponseBodyConverter<>(gson, type); } @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return new GsonResponseBodyConverter<>(gson, type); } }
/** * Created by hedong on 2016/4/19. */ public class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> { private final Gson gson; private final Type type; public GsonResponseBodyConverter(Gson gson, Type type) { this.gson = gson; this.type = type; } /** * { * "status": true, * "data": [ * { * "description": "", * "id": 6, * "keywords": "", * "name": "", * "seq": 1, * "title": "" * }, * { * "description": "", * "id": 5, * "keywords": "", * "name": "", * "seq": 2, * "title": "" * } * ] * } * * @param value * @return * @throws IOException */ @Override public T convert(ResponseBody value) throws IOException { String response = value.string(); Log.d("Network", "response>>" + response); try { JSONObject jsonObject = new JSONObject(response); if (jsonObject.getString("status").equals("true")) { //result==true表示成功返回,繼續用本來的Model類解析 String data = jsonObject.getString("data"); return gson.fromJson(data, type); } else { //ErrResponse 將msg解析為異常訊息文字 ErrResponse errResponse = gson.fromJson(response, ErrResponse.class); throw new ResultException(0, errResponse.getMsg()); } } catch (JSONException e) { e.printStackTrace(); Log.e("Network", e.getMessage()); return null; } } }
這裡解釋一下:
在開始寫後臺的時候最好定好規範,以免造成不必要的麻煩。以上格式只是參考,使用者可自行修改。
我們也可以跟伺服器約定錯誤型別,捕獲異常:
/**
* 用於捕獲伺服器約定的錯誤型別
*/
public class ResultException extends RuntimeException {
private int errCode = 0;
public ResultException(int errCode, String msg) {
super(msg);
this.errCode = errCode;
}
public int getErrCode() {
return errCode;
}
}
自定義回撥,獲取http請求對應的狀態碼:
public abstract class AbsAPICallback<T> extends Subscriber<T> {
//對應HTTP的狀態碼
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int REQUEST_TIMEOUT = 408;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504;
protected AbsAPICallback() {
}
@Override
public void onError(Throwable e) {
Throwable throwable = e;
//獲取最根源的異常
while (throwable.getCause() != null) {
e = throwable;
throwable = throwable.getCause();
}
if (e instanceof HttpException) {//HTTP錯誤
HttpException httpException = (HttpException) e;
switch (httpException.code()) {
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
//Toast.makeText(App.getInstance(), R.string.server_http_error, Toast.LENGTH_SHORT).show();
break;
}
} else if (e instanceof SocketTimeoutException) {
//Toast.makeText(App.getInstance(), R.string.network_error, Toast.LENGTH_SHORT).show();
} else if (e instanceof ResultException) {//伺服器返回的錯誤
ResultException resultException = (ResultException) e;
// Toast.makeText(App.getInstance(), resultException.getMessage(), Toast.LENGTH_SHORT).show();
} else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
// Toast.makeText(App.getInstance(), R.string.data_error, Toast.LENGTH_SHORT).show(); //均視為解析錯誤
} else if(e instanceof ConnectException){
// Toast.makeText(App.getInstance(), R.string.server_http_error, Toast.LENGTH_SHORT).show();
} else {//未知錯誤
}
onCompleted();
}
protected abstract void onDone(T t);
@Override
public void onCompleted() {
}
@Override
public void onNext(T t) {
onDone(t);
}
}
程式碼裡面都註釋很清楚。
最後在Activity怎麼呼叫呢,直接貼程式碼:
private void requestData() {
LocalService.getApi().getHealthClassify()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new AbsAPICallback<List<HealthClassifyBean>>() {
@Override
protected void onDone(List<HealthClassifyBean> list) {
//請求成功,做相應的頁面操作
}
@Override
public void onError(Throwable e) {
super.onError(e);
//e.getMessage() 可獲取伺服器返回錯誤資訊
}
});
}
ok,到此就結束了。