Android之Okhttp的封装

最近需要用到网络请求,用到了Okhttp这个强大的库,写个小博客来做个笔记 先看一下Okhttp3和之前版本的不同之处。

1.Okhttp3和OkHttp的对比

  • OkHttpClient创建方式不同

例如:

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,很好的使用了创建者设计模式 有以下参数:

Dispatcher dispatcher; // 分发
Proxy proxy; // 代理
List<Protocol> protocols;
List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors = new ArrayList<>(); // 拦截器 final List<Interceptor> networkInterceptors = new ArrayList<>(); // 网络拦截器 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调用OkHttpClient的setCookieHandler方法,CookieHandler是Android SDK提供的标注的cookie管理,CookieHandler 的之类CookieManager实现了cookie的具体管理方法,构建CookieManager需要实现CookieStore接口,系统提供了一个默认的实现CookieStoreImpl,只负责把cookie存储在内存中。

  • post消息体构建方式不同

okhttp使用MultipartBuilder,FormEncodingBuilder构建post消息体,最终构建出来的都是RequestBody,而okhttp3增加了RequestBody的子类,构造器放到了RequestBody的子类中,MultipartBody.Builder既可以添加表单数据,也可以添加文件等二进制数据

  • Call和Callback不同

okhttp3 之前用CookieHandler管理cookie

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

3.0 之后新增了两个类Cookiejar、Cookie两个类,开放接口,需要用户自己去实现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之前我们去掉call 可以直接使用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参数的构造

public class Params {
    public static final String METHOD_GET = "get";
    public static final String METHOD_POST = "post";
    //3个构造参数 一般参数 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());

        //统一添加请求头
        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> {
        //开始的时候调用,子类可选择实现
        public void onStarted(Request request);
        //当进度条改变的时候,子类可选择实现
        public void onProgressChanged(float progress, long total);

        /**
         * response是否正确
         *
         * @param response
         * @return
         */
        @Deprecated
        public boolean validateReponse(Response response);
        //解析数据,可选择实现,一般子类会自定义一个callback类,去解析
        public T parseNetworkResponse(Response response) throws ApiException;
        //子类回调,可选择实现
        public void onError(ApiException e);
        //子类回调
        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的时候,调用
    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;
    }
    //网络拦截器
    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处理,原数据返回
     *
     * @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方法执行相应的操作,例如解析数据等

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("联网失败,请检查网络");
        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的时候,调用
    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的实现,PersistentCookieStore的永久化代码,网上有,直接搜

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;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 这篇文章主要讲 Android 网络请求时所使用到的各个请求库的关系,以及 OkHttp3 的介绍。(如理解有误,...
    小庄bb阅读 1,084评论 0 4
  • 这个笔记会记录一些比较杂的东西,主要是Retrofit2使用中需要注意的要点。 以上是官方网站上的一段介绍,大意就...
    不羁的木木阅读 1,097评论 0 49
  • 这篇文章说下OkHttp的基本用法,是最新的3哦,如果你曾经在网上搜索OkHttp怎么使用发现有些类没有了可能是因...
    追逐黄昏的太阳阅读 886评论 1 1
  • Github地址---MyOkhttpUtils 初衷 对于okhttp3出来以后,使用的都是网上封装好的库,为了...
    iam薛定諤阅读 6,747评论 1 13
  • 是你了解的那个我做了你不能理解的事情,这并没有什么…… 好像一切都不一样了,做了那些早就要做的抉择,所以人生就是应...
    躲猫儿阅读 189评论 0 0