Android之Okhttp的封裝

最近需要用到網(wǎng)絡請求抄谐,用到了Okhttp這個強大的庫窃爷,寫個小博客來做個筆記 先看一下Okhttp3和之前版本的不同之處皂冰。

1.Okhttp3和OkHttp的對比

  • OkHttpClient創(chuàng)建方式不同

例如:

OkHttpClient.Builder okHttpClientBuilder = client.newBuilder(); 
        /* 超時設置 */  
        okHttpClientBuilder.connectTimeout(10, TimeUnit.SECONDS) 
                            .writeTimeout(30, TimeUnit.SECONDS) 
                            .readTimeout(30, TimeUnit.SECONDS);
OkHttpClient mOkHttpClient = okHttpClientBuilder.build();

okhttp直接newOkHttpClient贿讹,而okhttp3中提供了Builder,很好的使用了創(chuàng)建者設計模式 有以下參數(shù):

Dispatcher dispatcher; // 分發(fā)
Proxy proxy; // 代理
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>(); // 攔截器 final List<Interceptor> networkInterceptors = new ArrayList<>(); // 網(wǎng)絡攔截器 ProxySelector proxySelector;
CookieJar cookieJar;
Cache cache; // 緩存
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator; // 代理證書
Authenticator authenticator; // 證書
ConnectionPool connectionPool;
Dns dns; // DNS
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int connectTimeout;
int readTimeout;
int writeTimeout;
  • cookie的設置方式不同:

okhttp調(diào)用OkHttpClient的setCookieHandler方法,CookieHandler是Android SDK提供的標注的cookie管理烂完,CookieHandler 的之類CookieManager實現(xiàn)了cookie的具體管理方法试疙,構(gòu)建CookieManager需要實現(xiàn)CookieStore接口,系統(tǒng)提供了一個默認的實現(xiàn)CookieStoreImpl抠蚣,只負責把cookie存儲在內(nèi)存中祝旷。

  • post消息體構(gòu)建方式不同

okhttp使用MultipartBuilder,F(xiàn)ormEncodingBuilder構(gòu)建post消息體嘶窄,最終構(gòu)建出來的都是RequestBody怀跛,而okhttp3增加了RequestBody的子類,構(gòu)造器放到了RequestBody的子類中护侮,MultipartBody.Builder既可以添加表單數(shù)據(jù),也可以添加文件等二進制數(shù)據(jù)

  • Call和Callback不同

okhttp3 之前用CookieHandler管理cookie

/* cookie管理 */ 
 mCookieHandler = new CookieManager(null, CookiePolicy.ACCEPT_ALL); 
 mOkHttpClient.setCookieHandler(mCookieHandler);

3.0 之后新增了兩個類Cookiejar储耐、Cookie兩個類羊初,開放接口,需要用戶自己去實現(xiàn)cookie的配管理什湘。用戶可以直接在CookieJar中簡單設置Cookie的管理长赞,如下:

okHttpClientBuilder.cookieJar(new CookieJar() {
       private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
             @Override
               public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                 cookieStore.put(url, cookies);
                }

            @Override
              public List<Cookie> loadForRequest(HttpUrl url) {
                   List<Cookie> cookies = cookieStore.get(url);
                   return cookies != null ? cookies : new ArrayList<Cookie>();
            }
    });
  • OkHttpClient的Cancel方法去掉

3.0之前我們?nèi)サ鬰all 可以直接使用mOkHttpClient.cancel(tag);3.0之后直接閹割掉此方法我們可以采用如下方法:

 public void cancelCallsWithTag(Object tag) {
        checkInit();

        if (tag == null) {
            return;
        }

        synchronized (mOkHttpClient.dispatcher().getClass()) {
            for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
                if (tag.equals(call.request().tag())) call.cancel();
            }

            for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
                if (tag.equals(call.request().tag())) call.cancel();
            }
        }
    }

2.Okhttp參數(shù)的構(gòu)造

public class Params {
    public static final String METHOD_GET = "get";
    public static final String METHOD_POST = "post";
    //3個構(gòu)造參數(shù) 一般參數(shù) file head
    private HashMap<String, Object> urlParams = new HashMap<>();
    private HashMap<String, File> fileParams = new HashMap<>();
    private HashMap<String, String> headers = new HashMap<>();

    private String api = "";
    private String baseUrl = "";
    private String method;

    public Params() {
        urlParams = new HashMap<String, Object>();
        fileParams = new HashMap<String, File>();
        headers = new HashMap<String, String>();
        setBaseUrl(Constants.me().getHost());

        //統(tǒng)一添加請求頭
        headers.put("Accept", "application/json");
        headers.put("Version", Constants.me().getVersionName());
        headers.put("Gis-Lat", String.valueOf(Constants.me().getLatitude()));
        headers.put("Gis-Lng", String.valueOf(Constants.me().getLongitude()));

        String token = Constants.me().getToken();
        if (!TextUtils.isEmpty(token)) {
            headers.put("Authorization", "Bearer " + token);
        }
        setMethod(METHOD_POST);
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getApi() {
        return api;
    }

    public void setApi(String api) {
        this.api = api;
    }

    public String getUrl() {
        return baseUrl + api;
    }

    public String getBaseUrl() {
        return baseUrl;
    }

    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }



    public void put(String key, Object value) {
        if (key != null) {
            urlParams.put(key, value);
        }
    }
    public void put(String key, String value) {
        if (key != null) {
            urlParams.put(key, value);
        }
    }

    public void put(String key, int value) {
        if (key != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }

    public void put(String key, double value) {
        if (key != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }
    public void put(String key, float value) {
        if (key != null) {
            urlParams.put(key, String.valueOf(value));
        }
    }

    public void putHeaders(final String key, final int value) {
        this.putHeaders(key, String.valueOf(value));
    }

    public void putHeaders(final String key, final String value) {
        headers.put(key, value);
    }

    /**
     * @param key
     * @param file
     */
    public void put(String key, File file) {
        if (file != null && file.exists()) {
            fileParams.put(key, file);
        }
    }
    //是否為文件
    public boolean isMultipart() {
        return !fileParams.isEmpty();
    }


    public HashMap<String, Object> getUrlParams() {
        return urlParams;
    }

    public HashMap<String, File> getFileParams() {
        return fileParams;
    }

    public HashMap<String, String> getHeaders() {
        return headers;
    }

    @Override
    public String toString() {
        return urlParams.toString();
    }
}

3.Okhttp的封裝

final public class HttpKit {
    private static final int DEFAULT_MILLISECONDS = 60000; //默認的超時時間

    private OkHttpClient.Builder okHttpClientBuilder;
    private OkHttpClient okHttpClient;
    private Handler mDelivery;
    private Context mContext;
    private final List<ResponseHandler> handlers = new ArrayList<>();//請求返回值處理
    private PersistentCookieStore cookieStore;//永久化保存cookie

    public interface HttpCallback<T> {
        //開始的時候調(diào)用,子類可選擇實現(xiàn)
        public void onStarted(Request request);
        //當進度條改變的時候闽撤,子類可選擇實現(xiàn)
        public void onProgressChanged(float progress, long total);

        /**
         * response是否正確
         *
         * @param response
         * @return
         */
        @Deprecated
        public boolean validateReponse(Response response);
        //解析數(shù)據(jù)得哆,可選擇實現(xiàn),一般子類會自定義一個callback類哟旗,去解析
        public T parseNetworkResponse(Response response) throws ApiException;
        //子類回調(diào)贩据,可選擇實現(xiàn)
        public void onError(ApiException e);
        //子類回調(diào)
        public abstract void onResponse(T response);
    }

    private static final HttpKit me = new HttpKit();


    private HttpKit() {
        okHttpClientBuilder = new OkHttpClient.Builder();
        okHttpClientBuilder.connectTimeout(DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);
        okHttpClientBuilder.readTimeout(DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);
        okHttpClientBuilder.writeTimeout(DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);

        mDelivery = new Handler(Looper.getMainLooper());
    }

    public static HttpKit me() {
        return me;
    }

    //當需要使用cookie的時候,調(diào)用
    public static HttpKit init(Context context) {
        me().setContext(context);
        return me();
    }

    private void setContext(Context context) {
        this.mContext = context;
        cookieStore = new PersistentCookieStore(mContext);//永久化保存cookie
        CookieJarImpl cookieJar = new CookieJarImpl(cookieStore);
        okHttpClientBuilder.cookieJar(cookieJar);
    }
    //設置cookie
    public void setCookie(Uri uri, Cookie cookie) {
        HttpUrl httpUrl = new HttpUrl.Builder().scheme(uri.getScheme()).host(uri.getHost()).build();
        cookieStore.add(httpUrl, cookie);
    }

    public void removeAllCookie() {
        cookieStore.removeAll();
    }

    //本地攔截器
    public HttpKit addInterceptor(Interceptor interceptor) {
        okHttpClientBuilder.addInterceptor(interceptor);
        return this;
    }
    //網(wǎng)絡攔截器
    public HttpKit addNetworkInterceptor(Interceptor interceptor) {
        okHttpClientBuilder.addNetworkInterceptor(interceptor);
        return this;
    }


    public List<ResponseHandler> handlers() {
        return handlers;
    }

    public HttpKit addHandler(ResponseHandler handler) {
        handlers.add(handler);
        return this;
    }

    public Handler getDelivery() {
        return mDelivery;
    }

    public void build() {
        okHttpClient = okHttpClientBuilder.build();
    }

    private Request buildRequest(Params params) {
        Request.Builder builder = new Request.Builder();
        builder.url(params.getUrl());
        //頭
        if (!params.getHeaders().isEmpty()) {
            for (Map.Entry<String, String> header : params.getHeaders().entrySet()) {
                if (header.getKey() != null) {
                    builder.addHeader(header.getKey(), header.getValue());
                }
            }
        }
        //文件表單上傳
        if (params.isMultipart()) {
            MultipartBody.Builder body = new MultipartBody.Builder();
            body.setType(MultipartBody.FORM);
            //有url
            if (!params.getUrlParams().isEmpty()) {
                for (Map.Entry<String, Object> param : params.getUrlParams().entrySet()) {
                    if (param.getKey() != null) {
                        body.addFormDataPart(param.getKey(), (String) param.getValue());
                    }
                }
            }
            //有file對象
            for (Map.Entry<String, File> param : params.getFileParams().entrySet()) {
                File file = param.getValue();
                if (file != null && file.exists()) {
                    RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
                    body.addFormDataPart(param.getKey(), file.getName(), fileBody);
                }
            }
            builder.post(body.build());
            //不是表單闸餐,是一般的Post請求的話
        } else if (Params.METHOD_POST.equals(params.getMethod())) {
            FormBody.Builder body = new FormBody.Builder();
            body.add("i", CryptKit.encrypt(params.getUrlParams(), params.getUrl()));
            builder.post(body.build());
        }
        return builder.build();
    }

    //同步請求
    public Response execute(Params params) throws IOException {
        Request request = buildRequest(params);

        Call call = okHttpClient.newCall(request);
        return call.execute();
    }

    /**
     * get請求不做parseNetworkResponse處理,原數(shù)據(jù)返回
     *
     * @param params
     * @param callback
     */
    public void get(Params params, final HttpCallback callback) {
        Request request = buildRequest(params);

        if (callback != null) callback.onStarted(request);
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendFail(new ApiException(e), callback);
            }

            @Override
            public void onResponse(Call call, Response response) {
                try {
                    if (call.isCanceled()) {
                        sendFail(new ApiException("取消", -1), callback);
                        return;
                    }

                    Object o = callback.parseNetworkResponse(response);
                    sendSuccess(o, callback);
                } catch (ApiException e) {
                    sendFail(e, callback);
                } finally {
                    if (response.body() != null)
                        response.body().close();
                }
            }
        });
    }

    public void post(final Params params, final HttpCallback callback) {
        if (params == null || callback == null) {
            Log4a.e("post params null");
            return;
        }
        Request request = buildRequest(params);

        if (callback != null) callback.onStarted(request);
        Call call = okHttpClient.newCall(request);
        Log4a.e("======>post "+params.getUrl());
        Log4a.e("======>   params:"+params.toString());
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sendFail(new ApiException(e.getCause()), callback);
            }

            @Override
            public void onResponse(Call call, Response response) {
                try {
                    if (call.isCanceled()) {
                        sendFail(new ApiException("取消", -1), callback);
                        return;
                    }

                    Object o = callback.parseNetworkResponse(response);
                    sendSuccess(o, callback);
                } catch (ApiException e) {
                    sendFail(e, callback);
                } finally {
                    if (response.body() != null)
                        response.body().close();
                }
            }
        });
    }

    private void sendFail(final ApiException e, final HttpCallback callback) {
        if (callback == null) return;

        mDelivery.post(new Runnable() {
            @Override
            public void run() {
                try {
                    if (callback != null) {
                        callback.onError(e);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void sendSuccess(final Object object, final HttpCallback callback) {
        if (callback == null) return;

        mDelivery.post(new Runnable() {
            @Override
            public void run() {
                try {
                    if (callback != null && object != null) {
                        callback.onResponse(object);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

4.子類自定義CallBack方法執(zhí)行相應的操作饱亮,例如解析數(shù)據(jù)等

public abstract class JSONCallback<T> extends TypeToken<T> implements HttpKit.HttpCallback<T> {

    private Type mClass;//獲取類似List<Student>或者 Student的type

    public JSONCallback() {
        mClass = getType();
    }

    public void onStarted(Request request) {
    }

    public void onProgressChanged(float progress, long total) {

    }

    /**
     * response是否正確
     *
     * @param response
     * @return
     */
    @Deprecated
    public boolean validateReponse(Response response) {
        return true;
    }

    public T parseNetworkResponse(Response response) throws ApiException {
        String resp = "";
        String data = "";
        int status;
        String message;

        try {
            resp = response.body().string();
            Log4a.e("======>> response:"+response.request().url().url());
            Log4a.json(resp);
        } catch (IOException e) {
            throw new ApiException(e);
        }

        try {
            JSONObject jsonObject = new JSONObject(resp);
            status = jsonObject.getInt("code");
            message = jsonObject.getString("message");
            if (status == 1000 && jsonObject.has("data")) {
                data = jsonObject.getString("data");
            }
        } catch (JSONException e) {
            throw new ApiException(e);
        }

        List<ResponseHandler> handlers = HttpKit.me().handlers();
        if (handlers != null && handlers.size() > 0) {
            for (ResponseHandler handler : handlers)
                handler.onHandler(status, message, response.request().url().url().getPath());
        }

        if (status != 1000) {
            throw new ApiException(message, status);
        }

        try {
            if (GsonKit.isSubObjectOf(mClass, List.class)) {
                if ("".equals(data) || data == null||"{}".equals(data)) {
                    data = "[]";
                }
            } else {
                if ("".equals(data) || data == null||"[]".equals(data)) {
                    data = "{}";
                }
            }
            return GsonKit.fromJson(data, mClass);
        } catch (Exception e) {
            throw new ApiException(message, status, e);
        }
    }

    public void onError(ApiException e) {
        if (e.getStatus() == 0)
            ViewKit.shortToast("聯(lián)網(wǎng)失敗,請檢查網(wǎng)絡");
        else if (e.getStatus() == -1) {// TODO: 2016/11/26

        } else if (!StringKit.isEmpty(e.getMessage()))
            ViewKit.shortToast(e.getMessage());
    }

    public abstract void onResponse(T response);
}

5.Cookie的一些設置

//當需要使用cookie的時候舍沙,調(diào)用
    public static HttpKit init(Context context) {
        me().setContext(context);
        return me();
    }

    private void setContext(Context context) {
        this.mContext = context;
        cookieStore = new PersistentCookieStore(mContext);//永久化保存cookie
        CookieJarImpl cookieJar = new CookieJarImpl(cookieStore);
        okHttpClientBuilder.cookieJar(cookieJar);
    }
    //設置cookie
    public void setCookie(Uri uri, Cookie cookie) {
        HttpUrl httpUrl = new HttpUrl.Builder().scheme(uri.getScheme()).host(uri.getHost()).build();
        cookieStore.add(httpUrl, cookie);
    }

    public void removeAllCookie() {
        cookieStore.removeAll();
    }

CookieJarImpl的實現(xiàn)近上,PersistentCookieStore的永久化代碼,網(wǎng)上有拂铡,直接搜

public class CookieJarImpl implements CookieJar {
    private CookieStore cookieStore;

    public CookieJarImpl(CookieStore cookieStore) {
        if (cookieStore == null)
            throw new IllegalArgumentException(String.format("cookieStore can not be null."));
        this.cookieStore = cookieStore;
    }

    @Override
    public synchronized void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        cookieStore.add(url, cookies);
    }

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

    public CookieStore getCookieStore() {
        return cookieStore;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壹无,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子感帅,更是在濱河造成了極大的恐慌斗锭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件失球,死亡現(xiàn)場離奇詭異拒迅,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門璧微,熙熙樓的掌柜王于貴愁眉苦臉地迎上來作箍,“玉大人,你說我怎么就攤上這事前硫“茫” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵屹电,是天一觀的道長阶剑。 經(jīng)常有香客問我,道長危号,這世上最難降的妖魔是什么牧愁? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮外莲,結(jié)果婚禮上猪半,老公的妹妹穿的比我還像新娘。我一直安慰自己偷线,他們只是感情好磨确,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著声邦,像睡著了一般犀斋。 火紅的嫁衣襯著肌膚如雪静稻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音但校,去河邊找鬼拔恰。 笑死抑片,一個胖子當著我的面吹牛牙捉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播材失,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼痕鳍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了龙巨?” 一聲冷哼從身側(cè)響起笼呆,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旨别,沒想到半個月后诗赌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡秸弛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年铭若,在試婚紗的時候發(fā)現(xiàn)自己被綠了洪碳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡叼屠,死狀恐怖瞳腌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情镜雨,我是刑警寧澤嫂侍,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站荚坞,受9級特大地震影響挑宠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜颓影,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一各淀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诡挂,春花似錦碎浇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吴裤。三九已至旧找,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間麦牺,已是汗流浹背钮蛛。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剖膳,地道東北人魏颓。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像吱晒,于是被迫代替她去往敵國和親甸饱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 這篇文章主要講 Android 網(wǎng)絡請求時所使用到的各個請求庫的關系仑濒,以及 OkHttp3 的介紹叹话。(如理解有誤,...
    小莊bb閱讀 1,147評論 0 4
  • 這個筆記會記錄一些比較雜的東西墩瞳,主要是Retrofit2使用中需要注意的要點驼壶。 以上是官方網(wǎng)站上的一段介紹,大意就...
    不羈的木木閱讀 1,128評論 0 49
  • 這篇文章說下OkHttp的基本用法喉酌,是最新的3哦热凹,如果你曾經(jīng)在網(wǎng)上搜索OkHttp怎么使用發(fā)現(xiàn)有些類沒有了可能是因...
    追逐黃昏的太陽閱讀 936評論 1 1
  • Github地址---MyOkhttpUtils 初衷 對于okhttp3出來以后泵喘,使用的都是網(wǎng)上封裝好的庫,為了...
    iam薛定諤閱讀 6,821評論 1 13
  • 是你了解的那個我做了你不能理解的事情,這并沒有什么…… 好像一切都不一樣了股冗,做了那些早就要做的抉擇霹陡,所以人生就是應...
    躲貓兒閱讀 199評論 0 0