阅读 281

OkHttp是一个优秀的网络请求框架

image

一、OkHttp介绍

  HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttp是一个优秀的网络请求框架,它有以下默认特性:

  • 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
  • 连接池减少请求延时
  • 透明的GZIP压缩减少响应数据的大小
  • 缓存响应内容,避免一些完全重复的请求
     :@@@.
   .@@@@@@@:   +@@       `@@      @@`   @@     @@
  .@@@@'@@@@:  +@@       `@@      @@`   @@     @@
  @@@     @@@  +@@       `@@      @@`   @@     @@
 .@@       @@: +@@   @@@ `@@      @@` @@@@@@ @@@@@@  @@;@@@@@
 @@@       @@@ +@@  @@@  `@@      @@` @@@@@@ @@@@@@  @@@@@@@@@
 @@@       @@@ +@@ @@@   `@@@@@@@@@@`   @@     @@    @@@   :@@
 @@@       @@@ +@@@@@    `@@@@@@@@@@`   @@     @@    @@#    @@+
 @@@       @@@ +@@@@@+   `@@      @@`   @@     @@    @@:    @@#
  @@:     .@@` +@@@+@@   `@@      @@`   @@     @@    @@#    @@+
  @@@.   .@@@  +@@  @@@  `@@      @@`   @@     @@    @@@   ,@@
   @@@@@@@@@   +@@   @@@ `@@      @@`   @@@@   @@@@  @@@@#@@@@
    @@@@@@@    +@@   #@@ `@@      @@`   @@@@:  @@@@: @@'@@@@@
                                                     @@:
                                                     @@:
                                                     @@:
复制代码

引入maven依赖:

<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
	<groupid>com.squareup.okhttp3</groupid>
	<artifactid>okhttp</artifactid>
	<version>${laster.version}</version>
</dependency>
复制代码

OkHttp 项目有如下模块:

模块内容
okhttp实现OkHttp库的模块
okhttp-testsOkHttp库单元测试的模块
okhttp-android-support支持android平台使用OkHttp库的模块
okhttp-apache实现ApacheHttpClient接口(下一版本会弃用)
okhttp-testing-support支持单元测试的模块
okhttp-urlconnection实现HttpURLConnection接口(下一版本会弃用)
okhttp-ws支持WebSocket
okhttp-logging-interceptor实现Logging拦截器
okcurl实现OkCurl
mockwebserver脚本化WebServer,用于测试HTTP客户端

OkHttp 项目父模块中主要包含以下插件:

插件用途
maven-compiler-plugin编译项目
maven-surefire-plugin执行测试
maven-javadoc-plugin生成文档
maven-release-plugin自动化项目版本发布
maven-checkstyle-plugin检测编码风格
animal-sniffer-maven-plugin检测代码API

官方介绍

github源码

二、OkHttp 基本使用

2.1 进行Get请求

使用OkHttp进行Get请求只需要四步即可完成:

  1. 创建okHttpClient对象

    OkHttpClient client = new OkHttpClient();
    复制代码
  2. 构造Request对象

    // 首先构造一个Request对象,参数最起码有个url,当然可以通过Request.Builder设置更多的参数比如:header、method等
    Request request = new Request.Builder().get().url("https://api.github.com/users/dllwh").build();
    复制代码
  3. 将Request封装为Call

    // 通过request的对象去构造得到一个Call对象,类似于将请求封装成了任务
    Call call = client.newCall(request);
    复制代码
  4. 根据需要调用同步或者异步请求方法

    // 同步调用,返回Response,需要这里会抛出IO异常
    Response response = call.execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
    复制代码

    > 同步调用会阻塞主线程,直到 HTTP 响应返回,对应 OKHTTP 中的 execute 方法,一般不适用。

    // 以异步的方式去执行请求
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
    
        }
    
        @Override
        public void onResponse(Call call, final Response response) throws IOException {
            // http响应行的code,如果访问成功则返回200。这个不是服务器设置的,而是http协议中自带的。
    				final int httpCode = response.code();
            ResponseBody responseBody = response.body();
            // 返回的字符串,只能调用一次,在第一次时有返回值,第二次再调用时将会返回null。
            final String res = responseBody.string();
            // 返回的二进制字节数组
            final byte[] bytes = responseBody.bytes();
            // 返回的InputStream
            final InputStream inputStream = responseBody.byteStream();
        }
    });
    复制代码

    > 以异步调用的方式去执行非阻塞式请求,它的执行结果一般都是通过接口回调的方式告知调用者,它对应 OKHTTP 中的 enqueue 方法。

2.2 进行Post请求

2.2.1 Post请求键值对

  很多时候我们会需要通过POST方式把键值对数据传送到服务器,OkHttp提供了很方便的方式来做这件事情。使用OkHttp进行Post请求和进行Get请求很类似,只需要五步即可完成:

  1. 创建okHttpClient对象

    OkHttpClient client = new OkHttpClient();
    复制代码
  2. 构建FormBody,传入参数

    okhttp3.FormBody.Builder builder = new FormBody.Builder();
    builder.add("username", "admin");
    builder.add("password", "admin");
    FormBody formBody = builder.build();
    复制代码

    > post的参数是通过构造一个FormBody通过键值对的方式来添加进去的,其实post方法需要传入的是一个RequestBody对象,用它来携带我们要提交的数据,FormBodyRequestBody的子类。

  3. 构建Request,将FormBody作为Post方法的参数传入

    Request request = new Request.Builder().url(url).post(formBody).build();
    复制代码
  4. 将Request封装为Call

    Call call = client.newCall(request);
    复制代码
  5. 根据需要调用同步或者异步请求方法。

    // 同步调用,返回Response,它对应 OKHTTP 中的 execute 方法,会抛出IO异常。
    Response response = call.execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
    复制代码
    // 以异步的方式去执行非阻塞式请求,它的执行结果一般都是通过接口回调的方式告知调用者,它对应 OKHTTP 中的 enqueue 方法。
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
    
        }
    
        @Override
        public void onResponse(Call call, final Response response) throws IOException {
        	if (response.isSuccessful()) {
    
            }
        }
    });
    复制代码

2.2.2 Post请求提交Json

  1. 创建okHttpClient对象

    OkHttpClient client = new OkHttpClient();
    复制代码
  2. 构建RequestBody,传入参数

    MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
    RequestBody requestBody = RequestBody.create(mediaType, "{username:admin;password:admin}");
    复制代码

    > 这种方式与前面的区别就是在构造Request对象时,需要多构造一个RequestBody对象,用它来携带我们要提交的数据。在构造 RequestBody 需要指定MediaType,用于描述请求/响应 body 的内容类型。

  3. 构建Request,将FormBody作为Post方法的参数传入

    Request request = new Request.Builder().url("http://www.jianshu.com/").post(requestBody).build();
    复制代码
  4. 将Request封装为Call

    Call call = client.newCall(request);
    复制代码
  5. 根据需要调用同步或者异步请求方法。

    // 同步调用,返回Response,会抛出IO异常
    Response response = call.execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
    复制代码
    /// 以异步的方式去执行非阻塞式请求,它的执行结果一般都是通过接口回调的方式告知调用者,它对应 OKHTTP 中的 enqueue 方法。
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
    
        }
    
        @Override
        public void onResponse(Call call, final Response response) throws IOException {
            if (response.isSuccessful()) {
    
            }
        }
    });
    复制代码

2.2.3 Post请求提交表单

  我们在网页上经常会遇到用户注册的情况,需要你输入用户名、密码(当然还有其他参数,这里仅仅举例说明),这其实就是一个表单,那么接下来我们看看如何利用OkHttp来进行表单提交。经过上面的学习,大家肯定也明白,主要的区别就在于构造不同的RequestBody传递给post方法即可。这里我们会用到一个MuiltipartBody,这是RequestBody的一个子类,我们提交表单就是利用这个类来构建一个RequestBody,下面的代码我们会发送一个包含用户民、密码到服务端。

@Test
public void doPostForm() throws IOException {
	OkHttpClient okHttpClient = new OkHttpClient();
	okhttp3.MultipartBody.Builder builder = new MultipartBody.Builder();
	// 如果提交的是表单,一定要设置这句
	builder.setType(MultipartBody.FORM);
	builder.addFormDataPart("username", "admin");
	builder.addFormDataPart("password", "admin");
	RequestBody requestBody = builder.build();

	Request request = new Request.Builder().url("https://en.wikipedia.org/w/index.php").post(requestBody).build();
	Response response = okHttpClient.newCall(request).execute();
	if (!response.isSuccessful()) {
		throw new IOException("Unexpected code " + response);
	}
	System.out.println(response.body().string());
}
复制代码

2.3 进行文件上传、下载

2.3.1 Post请求上传文件

  接下来我们在介绍一个可以构造RequestBody的Builder,叫做MultipartBuilder。当我们需要做类似于表单上传的时候,就可以使用它来构造我们的requestBody。

2.3.2 get请求下载文件

@Test
public void doGetFilePro() {
    OkHttpClient okHttpClient = new OkHttpClient();
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
    Response response = okHttpClient.newCall(request).execute();
    if (!response.isSuccessful()) {
        throw new IOException("Unexpected code " + response);
    }
    Headers headers = response.headers();
    for (int i = 0; i &lt; headers.size(); i++) {
        System.out.println(headers.name(i) + ": " + headers.value(i));
    }
    System.out.println(response.body().string());
}
复制代码

2.4 HTTP头属性配置

  典型的HTTP头就是像是一个 Map<string, string> ,每个字段都有一个或没有值。
当写请求头的时候,使用header(name, value)可以设置唯一的name、value。如果已经有值,旧的将被移除,然后添加新的。使用addHeader(name, value)可以添加多值(添加,不移除已有的)。
当读取响应头时,使用header(name)返回最后出现的name、value。通常情况这也是唯一的name、value。如果没有值,那么header(name)将返回null。如果想读取字段对应的所有值,使用headers(name)会返回一个list。

@Test
public void doHeader() throws IOException {
    OkHttpClient okHttpClient = new OkHttpClient();
    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();
    Response response = okHttpClient.newCall(request).execute();
    if (!response.isSuccessful()) {
        throw new IOException("Unexpected code " + response);
    }
    System.out.println("Server: " + response.header("Server"));
    System.out.println("Date: " + response.header("Date"));
    System.out.println("Vary: " + response.headers("Vary"));
}
复制代码

三、总结

  通过上面的例子我们可以发现,OkHttp在很多时候使用都是很方便的,而且很多代码也有重复,而且 OkHttp 官方文档并不建议我们创建多个OkHttpClient,因此全局可以使用一个。

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import java.io.IOException;
import java.util.Map;

/**
 * 把今天最好的表现当作明天最新的起点..~
 * <p>
 * Today the best performance as tomorrow newest starter!
 *
 * @类描述: OkHttp 基本使用
 * @author: <a href="mailto:duleilewuhen@sina.com">独泪了无痕</a>
 * @创建时间: 2020-12-22 11:54
 * @版本: V 1.0.1
 * @since: JDK 1.8
 * @see <a href="https://square.github.io/okhttp/">官方介绍</a>
 */
@Slf4j
public final class OkHttpHelper {
    static ObjectMapper mapper = new ObjectMapper();
    /**
     * 获取操作类
     */
    private static final OkHttpClient okHttpClient = new OkHttpClient();
    private static final String CHARSET_NAME = "UTF-8";
    private static final MediaType JSONMediaType = MediaType.parse("application/json;charset=UTF-8");

    /**
     * 同步get方式请求
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static String doGet(String url) throws IOException {
        Request request = new Request.Builder().get().url(url).build();
        Call call = okHttpClient.newCall(request);
        return execute(request);
    }

    /**
     * 异步get方式请求
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static void doSyncGet(String url) throws IOException {
        Request request = new Request.Builder().get().url(url).build();
        Call call = okHttpClient.newCall(request);
        enqueue(request);
    }

    /**
     * 同步post方式请求
     */
    public static String doPost(String url, Map<string, object> params) throws IOException {
        RequestBody requestBody = RequestBody.create(JSONMediaType, mapper.writeValueAsString(params));
        Request.Builder builder = new Request.Builder();

        Request request = builder.url(url).post(requestBody).build();
        log.info("do post request and url[{}]", mapper.writeValueAsString(request));
        return execute(request);
    }

    /**
     * 同步post方式请求
     */
    public static String doPost(String url, String params) throws IOException {
        RequestBody requestBody = RequestBody.create(JSONMediaType, params);
        Request.Builder builder = new Request.Builder();

        Request request = builder.url(url).post(requestBody).build();
        log.info("do post request and url[{}]", mapper.writeValueAsString(request));
        return execute(request);
    }

    /**
     * 异步post方式请求
     */
    public static void doSyncPost(String url, String params) {
        RequestBody body = RequestBody.create(JSONMediaType, params);
        Request request = new Request.Builder().url(url).post(body).build();
        enqueue(request);
    }

    public static String doPostJSON(String url, Map<string, object> params, Headers headers) throws IOException {

        RequestBody requestBody = RequestBody.create(JSONMediaType, mapper.writeValueAsString(params));
        Request.Builder builder = new Request.Builder();

        Request request = builder.url(url).post(requestBody).headers(headers).build();
        log.info("do post request and url[{}]", mapper.writeValueAsString(request));
        return execute(request);
    }

    /**
     * 同步请求,不会开始异步线程
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static String execute(Request request) throws IOException {
        log.info("请求开始:请求地址为:{}", request.url());
        Response response = okHttpClient.newCall(request).execute();
        if (response.isSuccessful()) {
            String res = response.body().string();
            log.info("请求返回:{}", res);
            return res;
        } else {
            throw new IOException("Unexpected code " + response);
        }
    }

    /**
     * 开启异步线程访问
     *
     * @param request
     * @param responseCallback
     */
    public static void enqueue(Request request, Callback responseCallback) {
        okHttpClient.newCall(request).enqueue(responseCallback);
    }

    /**
     * 开启异步线程访问网络, 且不在意返回结果(实现空callback)
     *
     * @param request
     */
    private static void enqueue(Request request) {
        okHttpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                log.error("",e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {ßß
                if (response.isSuccessful()) {
                    log.info("Successful data acquisition . . . ");
                    log.info("response.code()==" + response.code());
                    log.info("response.body().string()==" + response.body().string());
                }
            }

        });
    }
}
复制代码
文章分类
后端
文章标签