Android架構系列-封裝自己的okhttp

Tsy遠發表於2019-03-01

本文簡述了為什麼選擇okhttp作為自己的專案網路庫,和如何二次封裝為module便於使用

注:進階版MyOkHttp見文章:

juejin.im/post/59c4be…

0 Android架構系列文章

該系列文章會不斷更新Android專案開發中一些好的架構和小技巧

系列一 Android架構系列-基於MVP建立適合自己的架構
系列二 Android架構系列-如何優美的寫Intent
系列三 Android架構系列-開發規範
系列四 Android架構系列-封裝自己的okhttp
系列五 Android架構系列-MVP架構的實際應用

1 網路庫的選擇和為什麼放棄retrofit

本來專案的網路庫選擇的搭配是最流行的retrofit+okhttp+gson.如下圖版本庫裡還有記錄。

版本記錄
版本記錄

But!後來遇到了這樣的問題:服務端提供的API無法遵循Restful的API格式,而且不同模組由於分服務開發的,也不能保證格式一致。導致API介面層的實現十分牽強。最終api層的編寫不能專注於業務,適得其反。(最起碼現在的服務端還不適合與retrofit的使用)

最後決定放棄使用retrofit,使用自己二次封裝的okhttp。

2 封裝使用okhttp

封裝過程中參考了:

hongyang的Android OkHttp完全解析 是時候來了解OkHttp了

趙凱強的開源專案OkHttpPlus

2.1 怎麼使用

先說封裝好的okhttp+gson如何使用。(封裝了POST請求,GET請求,上傳檔案,下載檔案,取消請求和Gson轉換等功能)

2.1.1 POST請求

Map<String, String> params = new HashMap<String, String>();
params.put("name", "tsy");

MyOkHttp.get().post(this, "http://192.168.3.1/test_okhttp.php", params, new JsonResponseHandler() {
    @Override
    public void onSuccess(int statusCode, JSONObject response) {
        LogUtils.v(TAG, statusCode + " " + response);
    }

    @Override
    public void onFailure(int statusCode, String error_msg) {
        LogUtils.v(TAG, statusCode + " " + error_msg);
    }
});複製程式碼

2.1.2 GET請求

Map<String, String> params = new HashMap<String, String>();
params.put("name", "tsy");

MyOkHttp.get().get(this, "http://192.168.3.1/test_okhttp.php", params, new RawResponseHandler() {
    @Override
    public void onSuccess(int statusCode, String response) {
        LogUtils.v(TAG, statusCode + " " + response);
    }

    @Override
    public void onFailure(int statusCode, String error_msg) {
        LogUtils.v(TAG, statusCode + " " + error_msg);
    }
});複製程式碼

2.1.3 上傳檔案

Map<String, String> params = new HashMap<String, String>();
params.put("name", "tsy");

Map<String, File> files = new HashMap<String, File>();
File file = new File(Environment.getExternalStorageDirectory() + "/com.ci123.service.splashandroid/splash/1.png");
files.put("avatar", file);

MyOkHttp.get().upload(this, "http://192.168.3.1/test_post.php", params, files, new GsonResponseHandler<BB>() {
    @Override
    public void onFailure(int statusCode, String error_msg) {
        LogUtils.v(TAG, statusCode + " " + error_msg);
    }

    @Override
    public void onSuccess(int statusCode, BB response) {
        LogUtils.v(TAG, statusCode + " " + response.ret);
    }

    @Override
    public void onProgress(long currentBytes, long totalBytes) {
        LogUtils.v(TAG, currentBytes + "/" + totalBytes);
    }
});複製程式碼

2.1.4 下載檔案

MyOkHttp.get().download(this, "http://192.168.3.1/output_tmp.jpg",
        Environment.getExternalStorageDirectory() + "/com.tsy.splashandroid/", "1.jpg",
        new DownloadResponseHandler() {
    @Override
    public void onFinish(File download_file) {
        LogUtils.v(TAG, "onFinish:" + download_file.getPath());
    }

    @Override
    public void onProgress(long currentBytes, long totalBytes) {
        LogUtils.v(TAG, currentBytes + "/" + totalBytes);
    }

    @Override
    public void onFailure(String error_msg) {
        LogUtils.v(TAG, error_msg);
    }
});複製程式碼

2.1.5 取消請求(建議放在BaseActivity,BaseFragment的onDestroy中)

MyOkHttp.get().cancel(this);複製程式碼

2.1.6 返回格式

post,get,upload3個介面可以選擇返回格式為普通Json還是Gson

  1. 普通json
    回撥繼承JsonResponseHandler,例如POST請求的例子

  2. gson
    回撥繼承GsonResponseHandler,並設定泛型T,例如Upload請求的例子

  3. raw原始資料
    回撥繼承RawResponseHandler,例如GET請求例子

2.2 原始碼解析

原始碼整合在了BaseAndroidProject中作為網路底層模組,以module方式封裝。其他專案可以直接module拿過來引入專案即可使用。

BaseAndroidProject的Github地址:
github.com/tsy12321/Ba…

原始碼API入口在MyOkhttp檔案中。POST請求和GET請求的實現很簡單。在這我主要說明如何封裝gson response和上傳下載的進度監聽。

2.2.1 gson返回封裝

gson最後封裝成了如下的使用形式:

MyOkHttp.get().post(this, "http://192.168.3.1/test_okhttp.php", params, new GsonResponseHandler<BB>() {

    @Override
    public void onFailure(int statusCode, String error_msg) {
        LogUtils.v(TAG, statusCode + " " + error_msg);
    }

    @Override
    public void onSuccess(int statusCode, BB response) {
        LogUtils.v(TAG, statusCode + " " + response.ret);
    }
});複製程式碼

gson response與普通json返回不同的是,在GsonResponseHandler的建構函式中使用反射機制動態獲取到了本身的泛型型別,然後將該泛型型別轉化為了Gson可以使用的Type儲存起來。這樣在結果回撥時就可以使用該Type轉為Gson。

public abstract class GsonResponseHandler<T> implements IResponseHandler {

    Type mType;

    public GsonResponseHandler() {
        Type myclass = getClass().getGenericSuperclass();    //反射獲取帶泛型的class
        if (myclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        ParameterizedType parameter = (ParameterizedType) myclass;      //獲取所有泛型
        mType = $Gson$Types.canonicalize(parameter.getActualTypeArguments()[0]);  //將泛型轉為type
    }

    public final Type getType() {
        return mType;
    }

    public abstract void onSuccess(int statusCode, T response);

    @Override
    public void onProgress(long currentBytes, long totalBytes) {

    }
}複製程式碼

然後在okhttp獲得到response後,判斷到responseHandler是gson,就將結果轉為gson格式。

if(mResponseHandler instanceof JsonResponseHandler) {
    ...
} else if(mResponseHandler instanceof GsonResponseHandler) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            try {
                Gson gson = new Gson();
                ((GsonResponseHandler)mResponseHandler).onSuccess(response.code(),
                        gson.fromJson(response_body, ((GsonResponseHandler)mResponseHandler).getType()));
            } catch (Exception e) {
                LogUtils.e("onResponse fail parse gson, body=" + response_body, e);
                mResponseHandler.onFailure(response.code(), "fail parse gson, body=" + response_body);
            }

        }
    });
}複製程式碼

2.2.2 上傳和下載的監聽進度

該部分參考了趙凱強的-開源專案OkHttpPlus 裡面說明的比較清楚。大概的原理就是使用okio分別重寫requestbody和responsebody,在body中設定進度監聽返回。just so so。詳細原理可以直接跳轉部落格進行學習啊,在這就不重新造輪子了。

3 結尾

最終,okhttp被我封裝為了一個module,在這個module中就會引入gson和okhttp了,所以不把它匯出為jar包。以後的網路底層庫就用這個module啦!

目錄結構
目錄結構

更多文章關注我的公眾號

我的公眾號
我的公眾號

相關文章