OkHttp3详解

本文介绍OkHttp库的使用方法,包括同步与异步请求、GET与POST操作、文件上传等高级功能。同时探讨了OkHttp如何通过连接池、GZIP压缩等特性提高HTTP请求效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

  HTTP是现代应用网络的方式。有效地进行HTTP使交互传输加载更快,并节省带宽。OkHttp是默认情况下是高效的HTTP客户端:HTTP/2支持允许对同一主机的所有请求共享套接字。连接池减少请求延迟(HTTP / 2不可用)。透明GZIP缩小下载大小。响应缓存可以避免重复请求的网络。
OkHttp启动与现代TLS功能(SNI,ALPN)的新连接,如果握手失败,则返回到TLS 1.0。使用OkHttp很容易它的请求/响应API设计有流畅的构建器和不变性。它支持同步阻塞调用和具有回调的异步调用。

OkHttp对于Java,最低要求是JDK1.7。

概述:

OKHttp好处多多,比HttpClient更高效、简洁的,容易上手的HTTP编程的代名词,本章就讲述比较常见的获取JSON数据的实例
,HttpClient调用接口及第三方接口最常用的方式,OkHttp继承了HttpClient的优点,同时比HttpClient更好用,易上手。

依赖:

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>${okhttp.version}</version>
</dependency>

<dependency>
  <groupId>com.squareup.okio</groupId>
  <artifactId>okio</artifactId>
  <version>${okio.version}</version>
</dependency>

查看版本依赖网址:

http://search.maven.org/
https://oss.sonatype.org/content/repositories/snapshots/
http://mvnrepository.com/


首先,简单介绍一下关键的几个常用的类:

OkHttpClient:类似于HttpClient的Http客户端,是一个比HttpClient更先进的专注于连接效率的 HTTP 客户端,并且语法也相对HttpClient更简单易上手。


RequestOkHttp访问的请求类,请求类可以包含:方法、请求头列表、请求提


Builder辅助类


Response:OkHttp的响应类


response.body()返回ResponseBody类,同时也可以string, 方法如下

public final String string() throws IOException
以流的形式获取响应result,方式如下:

获取方式,字节流的方法:

public final InputStream byteStream() {
    return this.source().inputStream();
}
InputStream inputStream = response.body().byteStream();
InputStream inputStream1 = response.body().source().inputStream();
以上是两种流的方式获取响应内容

Request类中最常用的方法如下:

public HttpUrl url() { //url
        return this.url;
    }

    public String method() {//方法
        return this.method;
    }

    public String header(String name) {
        return this.headers.get(name);
    }

    @Nullable
    public RequestBody body() {
        return this.body;
    }

Response类中最常用的方法如下:

    public int code() {
        return this.code;
    }

    public boolean isSuccessful() {
        return this.code >= 200 && this.code < 300;
    }


实例:

get请求

OkHttpClient client = new OkHttpClient();

    String run(String url) throws IOException {
        Request request =new Request.Builder()
                .url(url)
                .build();

        try (Response response = client.newCall(request).execute()){
            return response.body().string();
        }
    }

    public static void main(String[] args) throws IOException {
        test1  test1 = new test1();
        String run = test1.run("https://raw.github.com/square/okhttp/master/README.md");
        System.out.println(run);
    }

post请求提交json

 附上
public static final MediaType JSON
            = MediaType.parse("application/json; charset=utf-8");

    OkHttpClient client = new OkHttpClient();

    String post(String url, String json) throws IOException {
        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            return response.body().string();
        }
    }

    String bowlingJson(String player1, String player2) {
        return "{'test':'123',"
                + "'name':'Bowling',"
                + "'p':["
                + "{'name':'" + player1 + "','history':[7,8,10],'total':39},"
                + "{'name':'" + player2 + "','history':[6,9,10],'total':41}"
                + "]}";
    }

    public static void main(String[] args) throws IOException {
        test1 example = new test1();
        String json = example.bowlingJson("Jesse", "Jake");
        String response = example.post("http://www.roundsapp.com/post", json);
        System.out.println(response);

    }

异步GET:

OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();

    String run(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = client.newCall(request);
        final String[] result = {""};
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                System.out.println("异步线程,线程Id为:" + Thread.currentThread().getId());
                result[0] = response.body().string();
            }
        });
        for (int i = 0; i < 20; i++) {
            System.out.println("主线程,线程Id为:" + Thread.currentThread().getId() + "\t i;=" + i);
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("request url: " + request.url());
        return result[0];
    }

    public static void main(String[] args) throws IOException {
        test1  test1 = new test1();
        String run = test1.run("https://www.baidu.com/");
        System.out.println(run);
    }

异步获取:

下载一个工作线程的文件,当响应是可读的时候,获取回调(Callback)。当响应头已经准备好后,将产生回调(Callback)。读取响应体可能一直阻塞。目前OkHttp不提供异步API来接收响应体的部位。

OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();
    String run(String url) {
        Request request = new Request.Builder()
                .url(url)
                .build();
        LOGGER.info("request url:%s ",request.url());
        try {
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    try {
                        throw new Exception("Unexpected code" + e);
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    //打印头部信息
                    Headers responseHeaders = response.headers();
                    for (int i = 0; i < responseHeaders.size(); i++) {
                        System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
                    }
                    if(response.isSuccessful()){//code >= 200 && code < 300;
                        String result = response.body().toString();
                        System.out.println("response.body->result:" + result);
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

访问头:

HTTP Header就像一个Map的键值对 :每个字段都有一个值或无值。但是,一些头部(headers)允许多个值,比如Guava的Multimap。例如,它共同为一个HTTP响应提供多个Vary头。OkHttp的API,试图使这两种情况下都能使用。
当写请求头,用header(name, value)来为唯一出现的name设置value。如果存在现有值,在添加新的value之前,他们将被移除。使用addHeader(name, value)来添加头部不需要移除当前存在的headers。
当读取响应头,用header(name)返回最后设置name的value。如果没有value,header(name)将返回null。读取所有以列表字段的值,可以使用headers(name)。要访问所有的头部,用Headers类,它支持索引访问。

 OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();
    String run() {
        Request request = new Request.Builder()
                .url("https://api.github.com/repos/square/okhttp/issues")
                .header("User-Agent", "OkHttp Headers.java")
                .addHeader("Accept", "application/json; q=0.5")
                .addHeader("Accept", "application/vnd.github.v3+json")
                .build();
        LOGGER.info("request url:%s ",request.url());
        try (Response response = client.newCall(request).execute()){
            //打印头部信息
            System.out.println("Server: " + response.header("Server"));
            System.out.println("Date: " + response.header("Date"));
            System.out.println("Vary: " + response.headers("Vary"));
            if(response.isSuccessful()){//code >= 200 && code < 300;
                String result = response.body().string();
                System.out.println("response.body->result:" + result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Post:HTTP POST的请求体发送到服务。下面例子post了一个markdown文档到一个的Web服务(将markdown作为HTML)。由于整个请求体是同时在内存中,应避免使用此API发送较大(大于1 MIB)的文件。


private static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");

    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();
    String run() {
        String postBody = ""
                + "Releases\n"
                + "--------\n"
                + "\n"
                + " * _1.0_ May 6, 2013\n"
                + " * _1.1_ June 15, 2013\n"
                + " * _1.2_ August 11, 2013\n";

        Request request = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
                .build();
        try (Response response = client.newCall(request).execute()){
            //打印头部信息
            LOGGER.info("Server: " + response.header("Server"));
            LOGGER.info("Date: " + response.header("Date"));
            LOGGER.info("Vary: " + response.headers("Vary"));
            if(response.isSuccessful()){//code >= 200 && code < 300;
                String result = response.body().string();
                LOGGER.info("response.body->result:" + result);
            }
            LOGGER.info("result : " + response.body().string());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Post Streaming:使用HTTP POST的请求体发送到服务。下面例子post了一个markdown文档到一个的Web服务(将markdown作为HTML)。由于整个请求体是同时在内存中,应避免使用此API发送较大(大于1 MIB)的文件。

private static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");

    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();
    String run() {

        RequestBody requestBody = new RequestBody() {
            @Override public MediaType contentType() {
                return MEDIA_TYPE_MARKDOWN;
            }

            @Override public void writeTo(BufferedSink sink) throws IOException {
                sink.writeUtf8("Numbers\n");
                sink.writeUtf8("-------\n");
                for (int i = 1; i <= 100; i++) {
                    sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
                }
            }
            private String factor(int n) {
                for (int i = 2; i < n; i++) {
                    int x = n / i;
                    if (x * i == n) return factor(x) + " × " + i;
                }
                return Integer.toString(n);
            }
        };
        Request request = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(requestBody)
                .build();
        try (Response response = client.newCall(request).execute()){
            if(response.isSuccessful()){//code >= 200 && code < 300;
                String result = response.body().string();
                LOGGER.info("response.body->result:" + result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Posting a File :将文件作为请求体

 private static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");

    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();
    String run() {
        File file = new File("E:\\README.md");
        Request request = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(RequestBody.create(MEDIA_TYPE_MARKDOWN,file))
                .build();
        try (Response response = client.newCall(request).execute()){
            if(!response.isSuccessful()){//code >= 200 && code < 300;
              throw new Exception("fail");
            }
            String result = response.body().string();
            LOGGER.info("request success->result:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Post提交表单:表单参数使用FormBody.Builder建立一个请求体,它就像一个HTML的标记,Names和Values将使用HTML兼容的表单URL编码进行编码。

OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .build();
    String run() {
        RequestBody requestBody = new FormBody
                .Builder()
                .add("search","Wind")
                .build();
        Request request = new Request.Builder()
                .url("https://en.wikipedia.org/w/index.php")
                .post(requestBody)
                .build();
        try (Response response = client.newCall(request).execute()){
            if(!response.isSuccessful()){//code >= 200 && code < 300;
              throw new Exception("fail");
            }
            String result = response.body().string();
            LOGGER.info("request success->result:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

POST:发送multipart请求
MultipartBody.Builder可以构建与HTML文件上传表单兼容的复杂的请求主体。multipart请求体的每一部分本身就是请求体,并且可以定义自己的头部。如果存在,这些头应该描述的部分请求体,如它的Content-Disposition。如果Content-Length 和 Content-Type头部可以使用,则他们会自动添加。

private static final String IMGUR_CLIENT_ID = "...";
    private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

    private final OkHttpClient client = new OkHttpClient();

    public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("title", "Square Logo")
                .addFormDataPart("image", "bamboo8.png",
                        RequestBody.create(MEDIA_TYPE_PNG, new File("G:\\bamboo8.png")))
                .build();

        Request request = new Request.Builder()
                .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
                .url("https://api.imgur.com/3/image")
                .post(requestBody)
                .build();

        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
        System.out.println(response.body().string());
    }

GET请求:使用GSON解析JSON数据
 private final OkHttpClient client = new OkHttpClient();
    public void run() throws Exception {
        final Gson gson = new Gson();
        Request request = new Request.Builder()
                .url("https://api.github.com/gists/c2a7c39532239ff261be")
                .build();
        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
        Result result = gson.fromJson(response.body().string(), Result.class);
        System.out.println(result.getUrl());
    }


其他功能:

使用 GZIP 压缩减少传输的数据量;
缓存,减少重复请求;
SPDY;
连接池;
超时重试机制
失败重试(如果你的服务有多个 IP 地址,如果第一次连接失败,OkHttp 将使备用地址);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值