1. 程式人生 > 其它 >Android的網路請求之OkHttp封裝

Android的網路請求之OkHttp封裝

OkHttp的封裝

市面上每個人都有自己IDE網路請求封裝,比如OKGo、OkHttpUtil、NoHttp都是很不錯的國內開發者封裝的框架,我封裝的更多的是自己隨心而為之。主要的一個點是將Request的封裝和解析更加自由。

使用的步驟如下

  1. 依賴相關的OkHttp的庫,我這裡依賴的是3.14.9,因為4.X以上開始支援kotlin啦,書寫方式有些許不一樣
  2. 依賴GSON庫,用於json物件的解析
  3. 你的資料基礎物件實現Result
  4. 封裝好你自己的網路Request,然後在呼叫OkHttpUtil.getInstance().syncRequest()/asyncRequest()/downAsync(),來呼叫同步、非同步、下載檔案的方法

具體的內容如下

  1. 配置類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;
}
  1. 請求返回封裝Result.java
public interface Result<T> {
    boolean extSuccess();//專案中返回成功的欄位,有的是T,有的是Y,有的是200,有的是0等,可以自定義此狀態
    T extBody();//返回結果
}
  1. 定義網路的請求回撥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();
}
  1. 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();
                }
            }
        }
    }
}
  1. [進階版]使用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();
}
  1. [高階版] 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;
    }
}