Android 網(wǎng)絡(luò)框架Volley+okHttp+Gson支持https

項(xiàng)目地址

https://github.com/fengxing1234/VolleyOkhttpGson/tree/master

Volley

Volley官方

Github Volley

官方教程

Android Volley完全解析(一)均践,初識Volley的基本用法

Android Volley完全解析(二)卡乾,使用Volley加載網(wǎng)絡(luò)圖片

Android Volley完全解析(三),定制自己的Request

Android Volley 之自定義Request

Volley是Google開發(fā)的HTTP庫根悼,它使Android應(yīng)用程序的網(wǎng)絡(luò)更容易,最重要的是康栈,更快辆苔。

Volley非常適合去進(jìn)行數(shù)據(jù)量不大,但通信頻繁的網(wǎng)絡(luò)操作忿族,而對于大數(shù)據(jù)量的網(wǎng)絡(luò)操作锣笨,比如說下載文件等,Volley的表現(xiàn)就會非常糟糕道批。

官方給出的Volley優(yōu)點(diǎn):
image.png

本人英文不好错英,用軟件翻譯一下。隆豹。

  1. 自動調(diào)度網(wǎng)絡(luò)請求椭岩。
  2. 多個并發(fā)網(wǎng)絡(luò)連接。
  3. 具有標(biāo)準(zhǔn)HTTP緩存一致性的透明磁盤和內(nèi)存響應(yīng)緩存噪伊。
  4. 支持請求優(yōu)先級簿煌。
  5. 取消請求API。
  6. 可以取消單個請求鉴吹,也可以設(shè)置要取消的請求塊或范圍姨伟。
  7. 易于定制,例如豆励,重試和退避夺荒。
  8. 強(qiáng)大的排序,可以使用從網(wǎng)絡(luò)異步獲取的數(shù)據(jù)輕松正確填充UI良蒸。
  9. 調(diào)試和跟蹤工具技扼。

OkHttp

Gson

使用

  1. 在Application中初始化HttpClient
public class VolleyApplication extends Application {

    private static Context context;
    private HttpClient httpClient;

    @Override
    public void onCreate() {
        super.onCreate();
        httpClient = new HttpClient(this);
        context = this;
    }

    public HttpClient getClient() {
        return httpClient;
    }

    public static Context getContext() {
        return context;
    }
}

  1. 配置 HttpClient
    根據(jù)接口文檔配置頭信息
public Map<String, String> generateHeader(RequestBody requestBody) {
        HashMap<String, String> map = new HashMap<>();
        map.put("", "8");
        map.put("", "123456");
        map.put("", DeviceUtil.getUUID(VolleyApplication.getContext()));
        map.put("", "1");


        String token = "";
        if (!"".equals(token)) {
            map.put("picc-m-sid", token);
        }
        return map;
    }
  1. 創(chuàng)建子類實(shí)現(xiàn)DataHandler
    根據(jù)接口文檔填充。
public class DataHandler {

    public JsonElement getData(NetworkResponse response, JsonObject json) {
        return json.get("data");
    }

    public int getStatusCode(NetworkResponse response, JsonObject json) {
        return json.get("state").getAsInt();
    }

    public String getMessage(NetworkResponse response, JsonObject json) {
        return json.get("msg").getAsString();
    }

    public boolean isSuccess(NetworkResponse response, JsonObject json) {
        return getStatusCode(response, json) == 200;
    }

    public String getStringFromobject(JsonElement element, String name) {
        return element.getAsJsonObject().get(name).getAsString();
    }

    public boolean getBooleanFromobject(JsonElement element, String name) {
        return element.getAsJsonObject().get(name).getAsBoolean();
    }

    public JsonElement parseGsonResponse(NetworkResponse response) throws UnsupportedEncodingException {
        //Log.w("fengxing", "parseGsonResponse: "+new String(response.data,HttpHeaderParser.parseCharset(response.headers)) );
        //{"flag":true,"data":{"url":"/mcph5Version/downLoad","version":"254"},"state":"200","msg":"成功返回"}
        // trim the string to prevent start with blank, and test if the string
        // is valid JSON, because the parser don't do this :(. If Json is not
        // valid this will return null;
        return new JsonParser().parse(new String(response.data, HttpHeaderParser.parseCharset(response.headers)).trim());
    }
}
  1. 定義接口
public Request<?> queryClaimPage(Context context, String page_id, String report_id, Response.Listener<JsonElement> listener, Response.ErrorListener error) {
        String url = API_CLAIM_QUERY_CLAIM_PAGE;
        JsonBuilder builder = new JsonBuilder();
        builder.add("page_id", page_id);
        builder.add("report_id", report_id);
        RequestBody requestBody = builder.build();
        return addGsonRequest(context, POST, url, requestBody, requestBody, listener, error);
    }
  1. 代碼中使用
    首先初始化HttpClient
client = HttpClient.getClient(this);
@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.test_1:
                request = client.getProvinceCity(this, new Response.Listener<JsonElement>() {
                    @Override
                    public void onResponse(JsonElement response) {
                        tv.setText(response.toString());
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        toast(error.getMessage());
                    }
                });
                break;
            case R.id.test_2:
                client.queryClaimPage(this, "4", "08cc85f8db9f4befbebf2e57f97eb016", new Response.Listener<JsonElement>() {
                    @Override
                    public void onResponse(JsonElement response) {
                        tv.setText(response.toString());
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        toast(error.getMessage());
                    }
                });
                break;
            case R.id.test_cancel:
                client.cancelAll(this);
                boolean cancel = client.isCancel(request);
                Log.d("isCancel", "onClick: " + cancel);
                break;
        }
    }

主要
根據(jù)接口文檔一般的項(xiàng)目傳遞的都是Json格式嫩痰,在定義接口時可以使用``JsonBuilder builder = new JsonBuilder();```來添加參數(shù)剿吻。

如果有特殊需求例如 使用表單就不能使用JsonBuilder類。應(yīng)該使用FormBuilder類串纺。
區(qū)別在于:
表單的type:application/x-www-form-urlencoded
Json的type:application/json; charset=utf-8

現(xiàn)在就可以正常使用了

取消請求

每一個接口都會傳遞一個context上下文丽旅,如果頁面關(guān)閉椰棘,請求隊(duì)列還是會在后臺請求工作,當(dāng)請求成功一般都設(shè)置設(shè)置數(shù)據(jù)榄笙,但是頁面關(guān)閉了邪狞,如果不處理很容易造成崩潰浸赫,解決辦法就是在頁面關(guān)閉后者失去焦點(diǎn)時窄潭,取消請求。

@Override
    protected void onDestroy() {
        super.onDestroy();
        client.cancelAll(this);
    }

request封裝

自定義OkRequest類繼承Request(Volley)類冠王。


  private RequestBody requestBody;

    public OkRequest(int method, String url, RequestBody body, @Nullable Response.ErrorListener listener) {
        super(method, url, listener);
        this.requestBody = body;
    }

    @Override
    public String getUrl() {
        if (isRequestBodyNoNeed())
            return super.getUrl() + "?" + getParamsString();
        else
            return super.getUrl();
    }

    protected String getParamsString() {
        Buffer buffer = new Buffer();
        try {
            requestBody.writeTo(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer.readUtf8();
    }

    protected boolean isRequestBodyNoNeed() {
        return (getMethod() == Method.GET || getMethod() == Method.DELETE) && requestBody != null;
    }

    public void setRequestBody(RequestBody body) {
        this.requestBody = body;
    }

    public RequestBody getRequestBody() {
        return requestBody;
    }

    @Override
    public String getBodyContentType() {
        if (!isRequestBodyNoNeed() && requestBody != null) {
            return requestBody.contentType().toString();
        }
        return super.getBodyContentType();
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        if (!isRequestBodyNoNeed() && requestBody != null) {
            Buffer buffer = new Buffer();
            try {
                requestBody.writeTo(buffer);
                return buffer.readByteArray();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            } finally {
                buffer.close();
            }
        } else {
            return super.getBody();
        }

在這里處理url的拼接米丘,請求體剑令,和數(shù)據(jù)類型(content-type)以及頭信息。
頭信息我放在了HttpClient類處理蠕蚜。

DataRequest MessageRequest GsonRequest ModelRequest

這四個類都繼承OkRequest尚洽。區(qū)別在與處理數(shù)據(jù)不通。

  • DataRequest:

  • MessageRequest:
    DataRequest和MessageRequest 差不多相同靶累,都時根據(jù)業(yè)務(wù)邏輯和接口文檔定義出來的。
    適用于這樣的返回
    {"flag":true,"data":{"url":"/mcph5Version/downLoad","version":"254"},"state":"200","msg":"成功返回"}
    DataRequest:取出來的數(shù)據(jù)data數(shù)據(jù)癣疟。
    MessageRequest:取出來的時msg數(shù)據(jù)挣柬。

  • GsonRequest:
    獲取到的JsonElement數(shù)據(jù),可以說時萬能的request睛挚。

private DataHandler mHandler;

    private Response.Listener<JsonElement> mListener;
    public GsonRequest(int method, String url, RequestBody requestBody, DataHandler handler, Response.Listener<JsonElement> listener, @Nullable Response.ErrorListener errorListener) {
        super(method, url, requestBody, errorListener);
        this.mListener = listener;
        if(handler == null){
            handler = new DataHandler();
        }
        this.mHandler = handler;
    }

    public GsonRequest(int method, String url, @NonNull Response.Listener<JsonElement> listener, Response.ErrorListener errorListener) {
        this(method, url, null, listener, errorListener);
    }

    public GsonRequest(int method, String url, RequestBody body, @NonNull Response.Listener<JsonElement> listener, Response.ErrorListener errorListener) {
        super(method, url, body, errorListener);
        this.mListener = listener;
    }

    @Override
    protected Response<JsonElement> parseNetworkResponse(NetworkResponse response) {
        try {
            dd(new String(response.data, HttpHeaderParser.parseCharset(response.headers)).trim());
            JsonElement element = new JsonParser().parse(new String(response.data, HttpHeaderParser.parseCharset(response.headers)).trim());
            return Response.success(element, HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(JsonElement response) {
        mListener.onResponse(response);
    }
}

在成功回調(diào)中使用Gson就可以解析出來邪蛔。

  • ModelRequest:
public class ModelRequest<T> extends OkRequest<T> {
    private Type type;
    private Response.Listener<T> mListener;
    private DataHandler mHandler;

    public ModelRequest(int method, String url, Type type, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
        this(method, url, null, type, null, listener, errorListener);
    }

    public ModelRequest(int method, String url, RequestBody requestBody, Type type, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
        this(method, url, requestBody, type, null, listener, errorListener);
    }

    public ModelRequest(int method, String url, Type type, DataHandler dataHandler, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
        this(method, url, null, type, dataHandler, listener, errorListener);
    }

    public ModelRequest(int method, String url, RequestBody requestBody, Type type, DataHandler dataHandler, @NonNull Response.Listener<T> listener, Response.ErrorListener errorListener) {
        super(method, url, requestBody, errorListener);
        this.type = type;
        this.mListener = listener;
        if (dataHandler == null) {
            dataHandler = new DataHandler();
        }
        mHandler = dataHandler;
    }

    public Type getType() {
        return type;
    }

    public DataHandler getDataHandler() {
        return mHandler;
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            JsonObject json = mHandler.parseGsonResponse(response).getAsJsonObject();
            if (mHandler.isSuccess(response, json)) {
                return Response.success((T) Model.commonCreate(mHandler.getData(response, json), type), HttpHeaderParser.parseCacheHeaders(response));
            } else {
                return Response.error(new VolleyRequestError(this, mHandler.getStatusCode(response, json), mHandler.getMessage(response, json)));
            }
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(T response) {
        mListener.onResponse(response);
    }
}

使用時需要傳入轉(zhuǎn)換的類型,然后由Gson把親故的數(shù)據(jù)轉(zhuǎn)換成實(shí)體類扎狱。

在定義接口時:

public Request<?> queryHospitalInfo(Context context, int now_page, int row_num, String hospital_name, Response.Listener<HospitalInfo> listener, Response.ErrorListener error) {
        String url = API_CLAIM_QUERY_HOSPITAL_INFO;
        McpJsonBuilder builder = new McpJsonBuilder();
        builder.add("now_page", 1);
        builder.add("row_num", 20);
        builder.add("hospital_name", hospital_name);
        RequestBody requestBody = builder.build();
        return addModelRequest(context, POST, url, requestBody, requestBody, HospitalInfo.class, listener, error);
    }

使用ModelRequest 在定義接口時侧到,需要傳入類型,HospitalInfo.class淤击。

如果是一個集合應(yīng)該這樣寫:

public Request<?> queryBankInfo(Context context, Response.Listener<List<BankData>> listener, Response.ErrorListener error) {
        String url = API_CLAIM_QUERY_BANK_INFO;
        McpJsonBuilder builder = new McpJsonBuilder();
        RequestBody requestBody = builder.build();
        return addModelRequest(context, POST, url, requestBody, requestBody, new TypeToken<List<BankData>>() {
        }.getType(), listener, error);
    }

其它不變匠抗。

設(shè)置策略

public <T> Request<?> addModelRequest(Context context, int method, String url,
                                          RequestBody requestBody, Type type, final RequestBody requestBodyForHeader,
                                          RetryPolicy retryPolicy,
                                          Response.Listener<T> listener, @NonNull Response.ErrorListener error) {
        ModelRequest<T> request =
                new ModelRequest<T>(method, getAbsoluteUrl(url), requestBody, type, mDataHandler, listener,
                        error) {
                    @Override
                    public Map<String, String> getHeaders() throws AuthFailureError {
                        return generateHeader(requestBodyForHeader);
                    }
                };
        request.setRetryPolicy(retryPolicy);
        return add(context, request);
    }

    public Request<?> addMessageRequest(Context context, int method, String url,
                                        RequestBody requestBody, final RequestBody requestBodyForHeader,
                                        RetryPolicy retryPolicy,
                                        Response.Listener<String> listener, @NonNull Response.ErrorListener error) {
        MessageRequest request =
                new MessageRequest(method, getAbsoluteUrl(url), requestBody, mDataHandler, listener,
                        error) {
                    @Override
                    public Map<String, String> getHeaders() throws AuthFailureError {
                        return generateHeader(requestBodyForHeader);
                    }
                };
        request.setRetryPolicy(retryPolicy);
        return add(context, request);
    }

    public Request<?> addDataRequest(Context context, int method, String url, RequestBody requestBody,
                                     final RequestBody requestBodyForHeader,
                                     RetryPolicy retryPolicy,
                                     Response.Listener<JsonElement> listener,
                                     @NonNull Response.ErrorListener error) {
        DataRequest request =
                new DataRequest(method, getAbsoluteUrl(url), requestBody, mDataHandler, listener, error) {
                    @Override
                    public Map<String, String> getHeaders() throws AuthFailureError {
                        return generateHeader(requestBodyForHeader);
                    }
                };
        request.setRetryPolicy(retryPolicy);
        return add(context, request);
    }

    public Request<?> addGsonRequest(final Context context, int method, String url,
                                     RequestBody requestBody, final RequestBody requestBodyForHeader,
                                     RetryPolicy retryPolicy,
                                     Response.Listener<JsonElement> listener, @NonNull Response.ErrorListener error) {
        GsonRequest request =
                new GsonRequest(method, getAbsoluteUrl(url), requestBody, mDataHandler, listener,
                        error) {
                    @Override
                    public Map<String, String> getHeaders() throws AuthFailureError {
                        return generateHeader(requestBodyForHeader);
                    }
                };
        request.setRetryPolicy(retryPolicy);
        return add(context, request);
    }

    public Request<?> addGsonRequest(Context context, int method, String url, RequestBody requestBody, RequestBody requestBodyForHeader, Response.Listener<JsonElement> listener, Response.ErrorListener error) {
        return addGsonRequest(context, method, url, requestBody, requestBodyForHeader,
                new DefaultRetryPolicy(DEFAULT_SOCKET_TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
                        DefaultRetryPolicy.DEFAULT_BACKOFF_MULT),
                listener, error);
    }

在定義接口時,這幾個方法對應(yīng)上面的說的request污抬。
這里都時做了相同的幾件事:

  • 定義頭信息generateHeader方法
  • 設(shè)置策略
  • 把請求添加到隊(duì)列
  • 獲取到完整的url

完整的url是由下面組成的

private static final String mDebugApiHost = "http://mtest.baidu.cn/abcd/";
private String getBaseHost() {
        return mDebugApiHost;
    }
public static final String API_CLAIM_QUERY_CLAIM_PAGE = "/api/claim/query-claim-page";

public Request<?> queryClaimPage(Context context, String page_id, String report_id, Response.Listener<JsonElement> listener, Response.ErrorListener error) {
        String url = API_CLAIM_QUERY_CLAIM_PAGE;
        JsonBuilder builder = new JsonBuilder();
        builder.add("page_id", page_id);
        builder.add("report_id", report_id);
        RequestBody requestBody = builder.build();
        return addGsonRequest(context, POST, url, requestBody, requestBody, listener, error);
    }

最后調(diào)用VolleyClient的方法汞贸,得到完整的url

public String getAbsoluteUrl(String relativeUrl) {
        return !relativeUrl.startsWith("http://") && !relativeUrl.startsWith("https://") ? (relativeUrl.startsWith("/") ? this.getHost() + relativeUrl.substring(1) : this.getHost() + relativeUrl) : relativeUrl;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市印机,隨后出現(xiàn)的幾起案子矢腻,更是在濱河造成了極大的恐慌,老刑警劉巖射赛,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件多柑,死亡現(xiàn)場離奇詭異,居然都是意外死亡楣责,警方通過查閱死者的電腦和手機(jī)竣灌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門聂沙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帐偎,你說我怎么就攤上這事逐纬。” “怎么了削樊?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵豁生,是天一觀的道長。 經(jīng)常有香客問我漫贞,道長甸箱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任迅脐,我火速辦了婚禮芍殖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谴蔑。我一直安慰自己豌骏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布隐锭。 她就那樣靜靜地躺著窃躲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钦睡。 梳的紋絲不亂的頭發(fā)上蒂窒,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音荞怒,去河邊找鬼洒琢。 笑死,一個胖子當(dāng)著我的面吹牛褐桌,可吹牛的內(nèi)容都是我干的衰抑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼撩嚼,長吁一口氣:“原來是場噩夢啊……” “哼停士!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起完丽,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤恋技,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后逻族,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜻底,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年聘鳞,在試婚紗的時候發(fā)現(xiàn)自己被綠了薄辅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片要拂。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖站楚,靈堂內(nèi)的尸體忽然破棺而出脱惰,到底是詐尸還是另有隱情,我是刑警寧澤窿春,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布拉一,位于F島的核電站,受9級特大地震影響旧乞,放射性物質(zhì)發(fā)生泄漏蔚润。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一尺栖、第九天 我趴在偏房一處隱蔽的房頂上張望嫡纠。 院中可真熱鬧,春花似錦延赌、人聲如沸除盏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痴颊。三九已至,卻和暖如春屡贺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锌杀。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工甩栈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糕再。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓量没,卻偏偏與公主長得像,于是被迫代替她去往敵國和親突想。 傳聞我的和親對象是個殘疾皇子殴蹄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345