Android的網路請求之OkHttp封裝
阿新 • • 發佈:2021-12-20
OkHttp的封裝
市面上每個人都有自己IDE網路請求封裝,比如OKGo、OkHttpUtil、NoHttp都是很不錯的國內開發者封裝的框架,我封裝的更多的是自己隨心而為之。主要的一個點是將Request的封裝和解析更加自由。
使用的步驟如下
- 依賴相關的OkHttp的庫,我這裡依賴的是3.14.9,因為4.X以上開始支援kotlin啦,書寫方式有些許不一樣
- 依賴GSON庫,用於json物件的解析
- 你的資料基礎物件實現Result
- 封裝好你自己的網路Request,然後在呼叫OkHttpUtil.getInstance().syncRequest()/asyncRequest()/downAsync(),來呼叫同步、非同步、下載檔案的方法
具體的內容如下
- 配置類HttpConfig.java
public class HttpConfig { //網路連線時間 private Long connectTimeout; //網路寫時間 private Long writeTimeout; //網路讀取時間 private Long readTimeout; //快取的資料夾 private File cacheDir; //自定義攔截器 private List<Interceptor> interceptors; //是否列印日誌 也可以不用,直接動態改變interceptors的攔截器也行 private boolean printLog; }
- 請求返回封裝Result.java
public interface Result<T> {
boolean extSuccess();//專案中返回成功的欄位,有的是T,有的是Y,有的是200,有的是0等,可以自定義此狀態
T extBody();//返回結果
}
- 定義網路的請求回撥ResultCallback.java
public abstract class ResultCallback<T> { public Type mType; public Context context; public ResultCallback(Context context) { this.context = context; mType = getSuperclassTypeParameter(getClass()); } static Type getSuperclassTypeParameter(Class<?> subclass) { Type superclass = subclass.getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } ParameterizedType parameterized = (ParameterizedType) superclass; return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); } /** * 網路請求開始的時候 可以用於彈窗的處理等 */ public abstract void onStart(); /** * 失敗的時候 * @param call 當前的call物件 * @param e 異常 */ public abstract void onFailure(Call call, Exception e); /** * 下載的進度監聽 * @param byteRead 已讀長度 * @param total 總長度 */ public void onProgress(long byteRead, long total) {} //成功返回的資料 public abstract void onSuccess(T response); //網路請求結束 不管是成功和失敗都會走這裡,一般用於同一處理UI操作 public abstract void onFinish(); }
- OkHttp的單列OkHttpUtil.java
//SingletonHelper 是一個單列的幫助類,也可以不繼承這個類 自己寫
public class OkHttpUtil extends SingletonHelper<OkHttpUtil> {
private static final int CACHE_SIZE = 10 * 1024 * 1024;
private OkHttpClient httpClient;
private Handler handler = new Handler(Looper.getMainLooper());
@Override
public OkHttpUtil newInstance() {
return new OkHttpUtil(null);
}
public OkHttpUtil(HttpConfig httpConfig) {
if (httpConfig == null) {
httpConfig = new HttpConfig.Builder().build();
}
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (httpConfig != null) {
builder.connectTimeout(httpConfig.getConnectTimeout(), TimeUnit.SECONDS)
.readTimeout(httpConfig.getReadTimeout(), TimeUnit.SECONDS)
.writeTimeout(httpConfig.getWriteTimeout(), TimeUnit.SECONDS);
if (httpConfig.getCacheDir() != null) {
builder.cache(new Cache(httpConfig.getCacheDir(), CACHE_SIZE));
}
// 新增一個請求攔截器
List<Interceptor> interceptors = httpConfig.getInterceptors();
//這裡對於printLog 的使用可以自定義
if (interceptors != null && interceptors.size() > 0) {
for (Interceptor interceptor : interceptors) {
builder.addInterceptor(interceptor);
}
}
}
httpClient = builder.build();
}
public OkHttpClient getHttpClient() {
return httpClient;
}
//同步請求
public Response syncRequest(Request request) throws IOException {
if (httpClient == null) {
return null;
}
return httpClient.newCall(request).execute();
}
//非同步請求
public <T> void asyncRequest(Request request, ResultCallback<T> resultCallback) {
if (httpClient == null) {
return;
}
sendStartCallback(resultCallback);
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailureCallback(call, e, resultCallback);
}
@Override
public void onResponse(Call call, Response response) {
try {
String string = response.body().string();
if (resultCallback.mType == String.class) {
sendSuccessCallback(string, resultCallback);
} else if (resultCallback.mType == Response.class) {
sendSuccessCallback(response, resultCallback);
} else {
Object result = GSONUtil.jsonToBean(string, resultCallback.mType);
if (null != result) {
sendSuccessCallback(result, resultCallback);
} else {
sendFailureCallback(call, new NullPointerException("資料解析異常"), resultCallback);
}
}
} catch (Exception e) {
sendFailureCallback(call, e, resultCallback);
}
}
});
}
/**
* 下載檔案並提供出進度監聽
*
* @param url 地址
* @param destFile 下載存放的檔案
* @param resultCallback 回撥
*/
public void downAsync(String url, String tag, final File destFile, final ResultCallback<File> resultCallback) {
if (httpClient == null || destFile == null) {
return;
}
File parentFile = destFile.getParentFile();
if (parentFile == null) {
return;
}
parentFile.mkdir();
sendStartCallback(resultCallback);
Request request = new Request.Builder()
.tag(tag)
.url(url)
.build();
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendFailureCallback(call, e, resultCallback);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len;
int sum = 0;
FileOutputStream fos = null;
try {
destFile.deleteOnExit();
destFile.createNewFile();
long contentLength = response.body().contentLength();
is = response.body().byteStream();
fos = new FileOutputStream(destFile);
while ((len = is.read(buf)) != -1) {
sum += len;
fos.write(buf, 0, len);
sendProgressCallback(sum, contentLength, resultCallback);
}
sendSuccessCallback(destFile, resultCallback);
fos.flush();
} catch (IOException e) {
sendFailureCallback(call, e, resultCallback);
} finally {
IOUtil.close(is, fos);
}
}
});
}
//開始網路請求
protected void sendStartCallback(final ResultCallback callback) {
if (callback != null) {
return;
}
handler.post(new Runnable() {
@Override
public void run() {
callback.onStart();
}
});
}
//網路請求失敗
protected void sendFailureCallback(final Call call, final Exception e, final ResultCallback callback) {
if (callback != null) {
return;
}
handler.post(new Runnable() {
@Override
public void run() {
callback.onFailure(call, e);
callback.onFinish();
}
});
}
//成功回撥
protected void sendSuccessCallback(final Object obj, final ResultCallback callback) {
if (callback != null) {
return;
}
handler.post(new Runnable() {
@Override
public void run() {
callback.onSuccess(obj);
callback.onFinish();
}
});
}
//下載進度回撥
protected void sendProgressCallback(final long byteRead, final long total, final ResultCallback callback) {
if (callback != null) {
return;
}
handler.post(new Runnable() {
@Override
public void run() {
callback.onProgress(byteRead, total);
}
});
}
//全部佇列中請求全部取消
public void cancelAll() {
if (null != httpClient) {
httpClient.dispatcher().cancelAll();
}
}
//根據標記取消請求的佇列和排隊中的佇列
public void cancel(String tag) {
if (null != httpClient) {
Dispatcher dispatcher = httpClient.dispatcher();
cancelCall(dispatcher.runningCalls(), tag);// 取消正在進行的請求佇列中具有tag標記的
cancelCall(dispatcher.queuedCalls(), tag);//取消待請求佇列中具有tag標記的
}
}
//
private void cancelCall(List<Call> callList, String tag) {
if (callList != null && callList.size() > 0) {
for (Call call : callList) {
if (call != null && tag.equals(call.request().tag())) {
call.cancel();
}
}
}
}
}
- [進階版]使用Retrofit+OkHttp
public abstract class ServiceFactory {
//存放根據Retrofit構建產生的Service的class
private static final ConcurrentHashMap serviceMap = new ConcurrentHashMap();
/**
* 需要使用者自己構建地址
*
* @param clazz 類
* @param <T> 返回類
* @return
*/
public <T> T createService(Class<T> clazz) {
return createService(clazz, serviceUrl(), OkHttpUtil.getInstance(httpConfig()).getHttpClient());
}
/**
* 根據類和網址建立服務
*
* @param clazz 類
* @param baseUrl 請求地址
* @param <T> 返回類
* @return
*/
public <T> T createService(Class<T> clazz, String baseUrl) {
return createService(clazz, baseUrl, OkHttpUtil.getInstance(httpConfig()).getHttpClient());
}
/**
* 根據類和網址建立服務
*
* @param clazz 類
* @param baseUrl 請求地址
* @param httpClient 客戶端 自定義
* @param <T> 返回的泛型
* @return
*/
public <T> T createService(Class<T> clazz, String baseUrl, OkHttpClient httpClient) {
String classKey = clazz.getSimpleName() + baseUrl;
Object service = serviceMap.get(classKey);
if (service == null) {
Retrofit.Builder builder = buildRetrofit(baseUrl, httpClient);
service = builder.build().create(clazz);
serviceMap.put(classKey, service);
}
return (T) service;
}
/**
* 資料解析 GsonConverterFactory.create()
* 網路切換適配 RxJava2CallAdapterFactory.create()
*
* @param url 請的地址
* @param httpClient 自定義的OkHttp客戶端
* @return Retrofit 物件
*/
public abstract Retrofit.Builder buildRetrofit(String url, OkHttpClient httpClient);
//網路構建的配置引數
public abstract HttpConfig httpConfig();
public abstract String serviceUrl();
}
- [高階版] Retrofit+OkHttp+RxJava
public abstract class SimpleObserver<T> implements SingleObserver<Result<T>>, LifecycleObserver {
private Lifecycle mLifecycle;
private Disposable mDisposable;
public SimpleObserver(Lifecycle lifecycle) {
mLifecycle = lifecycle;
}
@Override
public void onSubscribe(Disposable disposable) {
mDisposable = disposable;
if (mLifecycle != null) {
mLifecycle.addObserver(this);
}
onStart();
}
@Override
public void onSuccess(Result<T> tResult) {
if (null == tResult || !tResult.extSuccess()) {
onFailure(404, "伺服器異常,請稍後嘗試");
} else {
if (tResult.extSuccess()) {
onResult(tResult.extBody());
}
}
onHttpEnd();
}
@Override
public void onError(Throwable throwable) {
if (isNetError(throwable)) {
if (throwable instanceof SocketTimeoutException) {
onFailure(500, "請求超時,請檢查網路");
} else {
onFailure(500, "您的網路好像有問題,請檢查網路");
}
} else {
onFailure(404, "伺服器異常,請稍後嘗試");
}
onHttpEnd();
}
/**
* 網路請求開始
*/
public abstract void onStart();
/**
* 網路請求返回的物件
*
* @param result 結果物件
*/
public abstract void onResult(T result);
/**
* 請求失敗
*
* @param errorCode 錯誤碼
* @param errorMessage 錯誤資訊
*/
public abstract void onFailure(int errorCode, String errorMessage);
public void onHttpEnd() {
//解除與lifecycle的繫結。
if (mLifecycle != null) {
mLifecycle.removeObserver(this);
}
//動釋放
if (mDisposable != null && !mDisposable.isDisposed()) {
mDisposable.dispose();
}
}
private static boolean isNetError(Throwable throwable) {
return throwable instanceof HttpException || throwable instanceof SocketTimeoutException ||
throwable instanceof ConnectException || throwable instanceof UnknownHostException;
}
}