OkHttp

  • Post author:
  • Post category:其他




OkHttp



几个主流的网络框架



HttpURLConnection:

  • HttpURLConnection是java的标准类,什么都没封装,用起来太原始,不方便,比如重访问的自定义,以及一些高级功能等。



HttpClient:

  • 在Android中,androidSDK中集成了Apache的HttpClient模块,HttpClient就是一个增强版的HttpURLConnection,它只是关注于如何发送请求、接收响应,以及管理HTTP连接。如果做好封装或者使用android-async-http,Afinal,Xutils也能挺简单的完成http请求,但是Android6.0谷歌因为和Apache更新难以同步等原因吧已经放弃了HttpClient,HttpClient是不是系统自带的了,不过它在最近的更新中将HttpClient的所有代码copy了一份进来,所以还能使用。



AsyncHttpClient:

  • android-async-http内部实现是基于HttpClient, 我想可能也是因为目前 HttpClient 已经被废弃所以作者放弃维护了。



Volley:

  • 是谷歌官方13年I/O大会推出的,volley在设计的时候是将具体的请求客户端做了下封装:
  • HurlStack,也就是说可以支持HttpUrlConnection, HttpClient, OkHttp,我理解的就是说它是在应用层,做了封装,使用起来比较方便,直接用,还可以扩展HttpUrlConnection, HttpClient, OkHttp。相当于模版模式。这样解耦还是非常方便的,可以随意切换。
  • Volley 里面也封装了 ImageLoader ,所以如果图片处理需求简单我们可以不需要专门再去使用图片加载框架。
  • 但Volley 也有缺陷,比如不支持 post 大数据,所以不适合上传文件。不过 Volley 设计的初衷本身也就是为频繁的、数据量小的网络请求而生!



Retrofit:

  • Retrofit 是 Square 公司出品的默认基于 OkHttp 封装的一套 RESTful 网络请求框架,,RESTful 可以说是目前流行的一套 api 设计的风格,并不是标准。Retrofit 的封装可以说是很强大,里面涉及到一堆的设计模式,你可以通过注解直接配置请求,你可以使用不同的 http 客户端,虽然默认是用 OKhttp ,可以使用不同 Json Converter 来序列化数据,同时提供对 RxJava 的支持,使用 Retrofit + OkHttp + RxJava + Dagger2 可以说是目前比较潮的一套框架,但是需要有比较高的门槛。



简介

  • OkHttp是Square公司开源的针对Java和Android程序,封装的一个高性能http请求库
  • OkHttp是一款高效的HTTP客户端,支持连接同一地址的链接共享同一个socket,通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势,其核心主要有路由、连接协议、拦截器、代理、安全性认证、连接池以及网络适配,拦截器主要是指添加,移除或者转换请求或者回应的头部信息



OkHttp的使用

import com.alibaba.fastjson.JSON;
import lombok.Builder;
import lombok.ToString;
import okhttp3.*;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Created by zhang_j on 2019/9/23
 */
public class OkHttpUtil {

    public final static String GET = "GET";

    public final static String POST = "POST";

    public final static String PUT = "PUT";

    public final static String DELETE = "DELETE";

    public final static String PATCH = "PATCH";

    private final static String UTF8 = "UTF-8";

    private final static String GBK = "GBK";

    private final static String DEFAULT_CHARSET = UTF8;

    private final static String DEFAULT_METHOD = GET;

    private final static String DEFAULT_MEDIA_TYPE = "application/json";

    private final static boolean DEFAULT_LOG = false;

    private final static OkHttpClient client = new OkHttpClient.Builder().connectionPool(
            new ConnectionPool(20, 5, TimeUnit.MINUTES)).readTimeout(20, TimeUnit.SECONDS).connectTimeout(
            20, TimeUnit.SECONDS).build();

    /**
     * GET请求
     *
     * @param url
     *            URL地址
     * @return
     */
    public static String get(String url)
            throws Exception
    {
        return execute(OkHttp.builder().url(url).build());
    }

    /**
     * GET请求
     *
     * @param url
     *            URL地址
     * @return
     */
    public static String get(String url, String charset)
            throws Exception
    {
        return execute(OkHttp.builder().url(url).responseCharset(charset).build());
    }

    /**
     * 带查询参数的GET查询
     *
     * @param url
     *            URL地址
     * @param queryMap
     *            查询参数
     * @return
     */
    public static String get(String url, Map<String, String> queryMap)
            throws Exception
    {
        return execute(OkHttp.builder().url(url).queryMap(queryMap).build());
    }

    /**
     * 带查询参数的GET查询
     *
     * @param url
     *            URL地址
     * @param queryMap
     *            查询参数
     * @return
     */
    public static String get(String url, Map<String, String> queryMap, String charset)
            throws Exception
    {
        return execute(OkHttp.builder().url(url).queryMap(queryMap).responseCharset(charset).build());
    }

    /**
     * POST application/json
     *
     * @param url
     * @param obj
     * @return
     */
    public static String postJson(String url, Object obj)
            throws Exception
    {
        return execute(OkHttp.builder().url(url).method(POST).data(JSON.toJSONString(obj)).mediaType(
                "application/json").build());
    }

    /**
     * POST application/x-www-form-urlencoded
     *
     * @param url
     * @param formMap
     * @return
     */
    public static String postForm(String url, Map<String, String> formMap)
            throws Exception
    {
        String data = "";
        if (MapUtils.isNotEmpty(formMap))
        {
            data = formMap.entrySet().stream().map(
                    entry -> String.format("%s=%s", entry.getKey(), entry.getValue())).collect(
                    Collectors.joining("&"));
        }
        return execute(OkHttp.builder().url(url).method(POST).data(data).mediaType(
                "application/x-www-form-urlencoded").build());
    }

    private static String post(String url, String data, String mediaType, String charset)
            throws Exception
    {
        return execute(OkHttp.builder().url(url).method(POST).data(data).mediaType(mediaType).responseCharset(
                charset).build());
    }

    /**
     * 通用执行方法
     */
    private static String execute(OkHttp okHttp)
            throws Exception
    {
        if (StringUtils.isEmpty(okHttp.requestCharset))
        {
            okHttp.requestCharset = DEFAULT_CHARSET;
        }
        if (StringUtils.isEmpty(okHttp.responseCharset))
        {
            okHttp.responseCharset = DEFAULT_CHARSET;
        }
        if (StringUtils.isEmpty(okHttp.method))
        {
            okHttp.method = DEFAULT_METHOD;
        }
        if (StringUtils.isEmpty(okHttp.mediaType))
        {
            okHttp.mediaType = DEFAULT_MEDIA_TYPE;
        }
        if (okHttp.requestLog)
        {// 记录请求日志
            System.out.println(okHttp.toString());
        }

        // 获取请求URL
        String url = okHttp.url;
        // 创建请求
        Request.Builder builder = new Request.Builder();

        if (MapUtils.isNotEmpty(okHttp.queryMap))
        {
            String queryParams = okHttp.queryMap.entrySet().stream().map(
                    entry -> String.format("%s=%s", entry.getKey(), entry.getValue())).collect(
                    Collectors.joining("&"));
            url = String.format("%s%s%s", url, url.contains("?") ? "&" : "?", queryParams);
        }
        builder.url(url);

        // 设置请求头
        if (MapUtils.isNotEmpty(okHttp.headerMap))
        {
            okHttp.headerMap.forEach(builder::addHeader);
        }

        // 设置请求类型
        String method = okHttp.method.toUpperCase();
        String mediaType = String.format("%s;charset=%s", okHttp.mediaType, okHttp.requestCharset);

        if (method.equals(GET))
        {
            builder.get();
        }
        else if (ArrayUtils.contains(new String[] {POST, PUT, DELETE, PATCH}, method))
        {
            RequestBody requestBody = RequestBody.create(MediaType.parse(mediaType), okHttp.data);
            builder.method(method, requestBody);
        }
        else
        {
            throw new Exception("not set request method");
        }

        // 返回值
        String result = "";
        try
        {
            Response response = client.newCall(builder.build()).execute();
            byte[] bytes = response.body().bytes();
            result = new String(bytes, okHttp.responseCharset);
            if (okHttp.responseLog)
            {// 记录返回日志
                System.out.println(result);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 一个内部类
     *
     * @since
     */
    @Builder
    @ToString(exclude = {"requestCharset", "responseCharset", "requestLog", "responseLog"})
    static class OkHttp
    {
        private String url;

        private String method = DEFAULT_METHOD;

        private String data;

        private String mediaType = DEFAULT_MEDIA_TYPE;

        private Map<String, String> queryMap;

        private Map<String, String> headerMap;

        private String requestCharset = DEFAULT_CHARSET;

        private boolean requestLog = DEFAULT_LOG;

        private String responseCharset = DEFAULT_CHARSET;

        private boolean responseLog = DEFAULT_LOG;
    }

}



OkHttp框架的优势

  • 链接复用
  • Response 缓存和 Cookie
  • 默认 GZIP
  • 请求失败自动重连
  • DNS 扩展
  • Http2/SPDY/WebSocket 协议支持
  • 默认情况下,OKHttp会自动处理常见的网络问题:像二次连接、SSL的握手问题。
  • 从Android4.4开始HttpURLConnection的底层实现采用的是okHttp.
  • 需要注意的是:okHttp的回调方法,并不处于UI 线程中,对网络请求结果如果涉及UI 线程的操作,需要使用Handler。可以把它理解成是一个封装之后的类似 HttpUrlConnection 的一个东西,但是你在使用的时候仍然需要自己再做一层封装,这样才能像使用一个框架一样更加顺手。
  • 谷歌官方在6.0以后在android sdk已经移除了httpClient,加入okHttp.
  • okHttp支持SPDY(是谷歌开发的基于TCP的应用层协议,用于最小化网络延迟,提升网络速度,优化用户的网络使用体验.SPDY并不是一种替代http的协议,只是对http的一种增强.)允许连接在一主机的所有请求分享一个socket.如果SPDY不可用.会使用连接池来减少请求延迟.利用响应缓存来避免重复的网络请求.即便是网络出现问题时,okhttp依然起作用.它将从常见的链接问题当中回复.如果你的服务器有多个IP地址,当地一个失败时,okhttp会自动尝试连接其他的地址.这对于IPV4和IPV6以及寄宿在多个数据中心的服务而言,是非常有必要的,所以okhttp的稳定性可以说是非常棒的



OkHttp与其他框架的对比



OkHttp VS Volley

  • 毫无疑问Volley的优势在于封装的更好,而使用 OkHttp 你需要有足够的能力再进行一次封装。而 OkHttp 的优势在于性能更高,因为 OkHttp 基于NIO和Okio,所以性能上要比Volley更快。IO和NIO区别,NIO要比IO的性能要好,阻塞方面(IO阻塞),NIO面向缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。而 Okio是Square公司基于IO和NIO 基础上做的一个更简单、高效处理数据流的一个库。



Volley VS Retrofit

  • 这两个库都做了非常不错的封装,但是 Retrofit 解耦的更彻底,尤其 Retrofit 2.0 出来,Jake 对之前 1.0 设计不合理的地方做了大量重构,职责更细分,而且 Retrofit 默认使用 OkHttp ,性能上也要比 Volley 占优势,再有如果你的项目如果采用了 RxJava ,那更该使用 Retrofit 。



版权声明:本文为zj57356498318原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。