1. 程式人生 > >安卓網路請求框架第三彈——OkHttp的封裝

安卓網路請求框架第三彈——OkHttp的封裝

概述:此篇為Android網路請求框架第三彈,底層請求用的是okhttp 基本機構來自包建強老師的app研發錄,在研發錄中,包老師使用的是HttpClient,但是由於google在Android4.0之後刪除了HttpClient的APi,所以在包老師的基礎,加以修改,使用okHttp作為請求方式。建議沒讀過包老師APP研發錄的同學,先去研究一下此書,對我的幫助還是很大的。傳送門:包老師部落格地址 原始碼下載

一 本框架的核心內容就是:用執行緒池來管理我們的網路請求。不使用Asynctask的原因就是Asynctask不能靈活控制其內部的執行緒池。Asynctask沒有暴露出取消請求的方法。我們使用 ThreadPoolExecutor+Runnable+Handler的原生方式進行網路底層封裝。

1 URLData和UrlConfigManager

urlData 是我們自定義的請求實體

/**
     * 介面名
     */
    private String key;
    /**
     * 資料快取時間
     */
    private long expires;
    /**
     * 請求方式 get或者post
     */
    private String netType;
    /**
     * 請求地址
     */
    private String url;

urlConfigManager是將關於介面的xml配置檔案 轉換成urldata放入ArrayList中 xml檔案如下

<Node
        Expires="300"
        Key="getWeatherInfo"
        NetType="get"
        Url="http://www.weather.com.cn/data/sk/101010100.html" />

2 RemoteService和RequestCallback,RequestParameter。RequestManager,DefaultThreadPool

 RequestCallback callback = new RequestCallback() {
            @Override
public void onSuccess(String content) { Log.i("content", content); } @Override public void onFail(String errorMessage) { Log.i("error", errorMessage); } @Override public void onCookieExpired() { } }; ArrayList<RequestParameter> parameters = new ArrayList<>(); RequestParameter parameter1 = new RequestParameter("k1", "v1"); RequestParameter parameter2 = new RequestParameter("k1", "v1"); parameters.add(parameter1); parameters.add(parameter2); RemoteService.getInstance().invoke(this, "serverDemo", parameters, callback);

a RequestCallback是回撥,有onSuccess和onFail兩個方法。

b RequestParameter是用來傳遞呼叫介面所需要引數鍵值對的,當然你也可以使用HashMap

c RemoteService這個單例是用來發起請求的,它會建立一個request並將它新增到requestManager中 然後放到DefaultThreadPool的一個執行緒中去執行這個request。

d requestManager這個集合類是用於取消請求(cancelRequest)的,因為每發起一個請求,都會新增到RequestManager中,所以RequestManager儲存了全部Request ,所以在從ActivityA 跳轉到ActivityB,為了不產生阻塞,所以要取消ActivityA中所有未完成的Request 。

 * 取消網路請求
 */
public void cancelRequest() {
    if ((requestList != null) && (requestList.size() > 0)) {
        for (final OkHttpRequest request : requestList) {
            if (request.getCall() != null) {
                try {
                    request.getCall().cancel();
                    requestList.remove(request);
                } catch (final UnsupportedOperationException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}`

我們在BaseAcitivty中,會保持對RequestManager一個引用,這樣在onDestroy和onPause的時候執行cancelRequest方法取消所有未完成的請求。

e DefaultThreadPool只是對ThreadPoolExecutor和ArrayBlockingQueue的簡單封裝。我們可以認為它就是一個執行緒池,每發起一個請求,就由執行緒池分配一個新的執行緒來執行該請求。

3 OkHttpRequest 這個類 就是我自己寫的代替了以前的HttpClient 。在這個類中,整體的架構設計沒有改變。它實現了Runable介面,提供一個run方法來執行請求。

a 第一步 還是根據urlConfigManager得到的urlData來區分get還是post來建立一個request請求。

b 第二步 設定請求超時時間,讀寫時間,新增cookie到請求頭中,新增必要頭部資訊到請求頭中。


            okBuilder.connectTimeout(4000, TimeUnit.MILLISECONDS)
                    .readTimeout(4000, TimeUnit.MILLISECONDS)
                    .writeTimeout(4000, TimeUnit.MILLISECONDS);

            addCookie();// 新增Cookie到請求頭中

            setHttpHeaders(okBuilder); //新增必要的頭部資訊

            mOkHttpClient = okBuilder.build();

c 發起請求,使用requestCallback 處理返回結果。

 //傳送請求
            call = mOkHttpClient.newCall(request);
            //結果回撥
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, final IOException e) {
                    if (requestCallback != null) {

                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                requestCallback.onFail(e.getMessage());
                            }
                        });
                    } else {
                        // TODO: 2016/11/24  處理介面為空的情況
                    }

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (requestCallback != null) {
                        int code = response.code();
                        if (code >= 200 && code < 300) {  //代表成功

//                            updateDeltaBetweenServerAndClientTime(response);  // 更新伺服器時間和本地時間的差值

                            //okHttp3 如果在頭部中 添加了 gzip欄位 會自動進行 gzip 解壓 這裡不在處理
                            String string = response.body().string();
                            Log.i("DataResponse", string);

                            final DataResponse dataResponse = JSON.parseObject(string, DataResponse.class);

                            if (dataResponse.hasError()) {  // 包含錯誤

                                if (dataResponse.getErrorType() == 1) {
                                    handler.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            requestCallback.onCookieExpired();
                                        }
                                    });
                                } else {
                                    handleNetworkError(dataResponse.getErrorMessage());
                                }

                            } else {
                                //當是get請求 並且快取時間>0時 儲存到快取
                                if (urlData.getNetType().equals(REQUEST_GET) && urlData.getExpires() > 0) {
                                    CacheManager.getInstance().putFileCache(newUrl, dataResponse.getResult(), urlData.getExpires());
                                }

                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        requestCallback.onSuccess(dataResponse.getResult() + "  success");
                                    }
                                });
                            }
                        } else {
                            handleNetworkError("網路異常2");
                        }

                    } else {
                        // TODO: 2016/11/24  處理介面為空的情況

                    }
                }
            });

d 在本類中,還介紹了關於 cookie的持久化處理,這裡不做贅述。

/**
     * 從本地獲取cookie列表
     *
     * @return
     */
    public void addCookie() {
        okBuilder.cookieJar(new CookiesManager());
    }

/**
     * 自動管理Cookies
     */
    private class CookiesManager implements CookieJar {
        private final PersistentCookieStore cookieStore = new PersistentCookieStore(activity);

        @Override
        public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
            if (cookies != null && cookies.size() > 0) {
                for (Cookie item : cookies) {
                    cookieStore.add(url, item);
                }
            }
        }

        @Override
        public List<Cookie> loadForRequest(HttpUrl url) {
            List<Cookie> cookies = cookieStore.get(url);
            return cookies;
        }
    }

二 總結:其實,本次封裝就是使用okhttp代替了HttpClient,整體框架還是包老師的架子。具體的okHttp的使用方法和其他思路的封裝,請參考鴻楊大神的部落格 瞭解okHttp okHttp封裝 本部落格原始碼下載地址 原始碼下載地址