12

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

 3 years ago
source link: https://my.oschina.net/dllwh/blog/4868717
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、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-tests OkHttp库单元测试的模块 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 < 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:[email protected]">独泪了无痕</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());
                }
            }

        });
    }
}

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK