[Java]-HTTP客户端工具OkHttp简介

文章目录


OkHttp是一款优秀的HTTP客户端框架,OkHttp3是OkHttp发展到版本3.0之后的名字。

简介

OkHttp是一个高效的HTTP客户端,他有如下特性:

  • 对 HTTP/2 和 SPDY 的支持:使得对同一个主机发出的所有请求都可以共享同一个套接字连接;若HTTP/2 和 SPDY 不可用,OkHttp会使用连接池来复用连接以提高效率;
  • 透明的GZIP压缩,以降低传输内容的大小;
  • HTTP 响应的缓存机制,可以避免不必要的网络请求;

当网络出现问题时,OkHttp会自动恢复一般的连接问题;若服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP。

OkHttp采用流式构造接口,方便调用;同时支持同步与异步方式调用。

依赖包

要使用OkHttp,需要先在pom.xml中引入依赖包;okhttp3是使用kotlin实现的,所以对应包也需要引入:

<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib -->
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
    <version>1.5.0</version>
</dependency>

创建OkHttpClient

通过共享的响应缓存/线程池/复用的连接等,绝大多数应用只需一个OkHttpClient实例,便可以满足整个应用的所有Http请求。

OkHttpClient client = new OkHttpClient();

OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS) // default 10s
            .writeTimeout(30, TimeUnit.SECONDS)   // default 10s
            .readTimeout(30, TimeUnit.SECONDS)    // default 10s
            .build();
			
OkHttpClient anotherClient  = client.newBuilder().build();

构造Url

通过HttpUrl.Builder可方便地构造Url:

  • addPathSegment:添加路径;
  • addQueryParameter:添加请求参数,允许重复;
  • setQueryParameter:设定请求参数,覆盖重复;
  • username、password:添加用户名、密码;
public static HttpUrl buildUrl(String url, Map<String, String> queryParam) {
    HttpUrl.Builder builder = HttpUrl.parse(url).newBuilder();
    queryParam.forEach((k, v) -> {
        builder.addQueryParameter(k, v);
    });

    return builder.build();
}

构造完整Url的流程:

HttpUrl.Builder builder = new HttpUrl.Builder()
        .host("127.0.0.1")
        .port(8001)
        .addPathSegment("seg1")
        .addPathSegment("path2")
        .username("user")
        .password("pass")
        .scheme("https")
        .addQueryParameter("k1", "v1")
        .addQueryParameter("k1", "v2")
        .setQueryParameter("uk", "v1")
        .setQueryParameter("uk", "v2");

HttpUrl http = builder.build();
System.out.println(http.toString());
// https://user:pass@127.0.0.1:8001/seg1/path2?k1=v1&k1=v2&uk=v2

Header头设定

HTTP头(可参见请求头大全)是 Map<String, List<String>>类型。也就是说,对于每个 HTTP 头,可能有多个值;但是大部分 HTTP 头都只有一个值。

OkHttp中通过Request构造时添加:

  • header(name,value):设置HTTP头的唯一值,若请求已经存在则替换掉。
  • addHeader(name,value):添加新值,若请求头中已经存在此name还会继续添加(此时,请求头中便会存在多个name相同而value不同的“键值对”)。
  • header(name):读取唯一值或多个值的最后一个值
  • headers(name):获取所有值

构造Request时,必须设定Url,默认是GET模式:

Request request = new Request.Builder()
        .url("http://127.0.0.1")
        .addHeader("h1", "v1")
        .addHeader("h1", "v2")
        .header("uh", "v1")
        .header("uh", "v2")
        .build();
System.out.println(request.toString());
// Request{method=GET, url=http://127.0.0.1/, headers=[h1:v1, h1:v2, uh:v2]}

请求示例

使用OkHttp,需要:

  • 构造OkHttpClient对象;
  • 构造Request对象;
  • 构造Call对象;
  • 发起请求:execute同步;enqueue异步;
  • 返回Response:不可修改的Http相应对象。

Get请求

Request默认就是Get请求,所以构造时可以省略Get;Get请求参数通过queryParameter添加。

同步Get

以请求百度为例,通过newCall会同步调用,其返回内容可通过body来获取;

private static void getSync() throws IOException {
    String url = "http://wwww.baidu.com";
    Request request = new Request.Builder()
            .url(url) // 可以字符串,也可以是HttpUrl
            .build();
    Call call = httpClient.newCall(request);
    Response resp = call.execute();
    if (resp.code() == 200) {
        System.out.println("Response: " + resp.body().string());
    } else {
        // Error handle
        System.out.println("Code:" + resp.code() + ", Msg:" + resp.message());
    }
}

异步Get

通过enqueue,可提交异步请求;请求的应答通过回调Callback返回。

private static void getAsync() {
    HttpUrl url = buildUrl("http://127.0.0.1:7087/study/test/testEvent",
            new HashMap<String, String>(){{
                put("msg", "Test Msg event");
            }});
    // http://127.0.0.1:7087/study/test/testEvent?msg=Test%20Msg%20event
    Request request = new Request.Builder()
            .url(url)
            .get()  //默认就是GET请求,可以不写
            .build();
    Call call = httpClient.newCall(request);
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            System.out.println("Fail: " + e.getMessage());
        }

        @Override
        public void onResponse(Call call, Response resp) throws IOException {
            System.out.println("onResponse: " + resp.body().string());
        }
    });
}

Post请求

Request中通过Post来标识Post请求,并设定Post的Body内容。

Json内容

Json是常用的序列化方式,只需把要传递的对象序列化为Json字符串,然后以字符串Body的方式传递到服务端。

private static void postJsonBody() throws IOException {
    MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
    RequestBody reqBody = RequestBody.create("msg for test", mediaType);
    Request request = new Request.Builder()
            .url("http://127.0.0.1:7087/study/test/postMsg")
            .post(reqBody)
            .build();
    Response resp = httpClient.newCall(request).execute();
    System.out.println("Response: " + resp.body().string());
}

Form表单

Form表单都是以键值对的方式传递内容到服务端的,通过FormBody可方便地构造。

private static void postFormBody() throws IOException {
    RequestBody reqBody = new FormBody.Builder()
            .add("msg", "form test")
        	.add("others", "other params")
            .build();
    Request request = new Request.Builder()
            .url("http://127.0.0.1:7087/study/test/formMsg")
            .post(reqBody)
            .build();
    Response resp = httpClient.newCall(request).execute();
    System.out.println("Response: " + resp.body().string());
}

文件上传

上传文件时,使用MultipartBody,并可通过MediaType来设定媒体类型:

private static void uploadOneFile() throws IOException {
    File file = new File("D:\\tmp\\Python-learning.md");
    MediaType mediaType = MediaType.parse("application/octet-stream"); //设置类型为八位字节流
    RequestBody reqBody = RequestBody.create(file, mediaType);

    MultipartBody multiBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("id", "0") // for test
            .addFormDataPart("file", file.getName(), reqBody)
            .build();

    Request request = new Request.Builder()
//            .header("Authorization", "Bearer ****************") //添加请求头的身份认证Token
            .url("http://127.0.0.1:7087/study/test/uploadFile")
            .post(multiBody)
            .build();
    Response resp = httpClient.newCall(request).execute();
    System.out.println("Response: " + resp.body().string());
}

要上传多个文件,就需要构造多份文件相关的RequestBody,然后依次添加到MultipartBody中:

private static void uploadMultiFile() throws IOException {
    List<String> lstFile = Lists.newArrayList("D:\\tmp\\Python-learning.md", "D:\\tmp\\WebRTC-Learning.md");
    MediaType mediaType = MediaType.parse("application/octet-stream"); //设置类型为八位字节流

    MultipartBody.Builder multiBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM);
    for(String f : lstFile){
        File file = new File(f);
        RequestBody fileBody = RequestBody.create(file, mediaType);
        multiBody.addFormDataPart("file", file.getName(), fileBody);
    }

    Request request = new Request.Builder()
            .url("http://127.0.0.1:7087/study/test/uploadMultiFile")
            .post(multiBody.build())
            .build();
    Response resp = httpClient.newCall(request).execute();
    System.out.println("Response: " + resp.body().string());
}

Put请求

Put与Post类似,只是Request时用Put标识。

Json内容

以传递Json格式的Body为例:

MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
RequestBody reqBody = RequestBody.create("{\"page\":0,\"query\":\"info to query\",\"size\":0}", mediaType);

Request request = new Request.Builder()
        .url("http://127.0.0.1:7087/study/test/testPut")
        .put(reqBody)
        .build();
Response resp = httpClient.newCall(request).execute();
System.out.println("Response: " + resp.body().string());

参数方式

通过参数来传递请求的内容;但是Put要求必须传递Body,此时可构造一个空Body:

HttpUrl url = buildUrl("http://127.0.0.1:7087/study/test/paramPut",
        new HashMap<String, String>() {{
            put("msg", "Test Msg event");
            put("others", "other params");
        }});
RequestBody reqBody = RequestBody.create(new byte[]{}, null);
Request request = new Request.Builder()
        .url(url)
        .put(reqBody)
        .build();
Response resp = httpClient.newCall(request).execute();
System.out.println("Response: " + resp.body().string());

Delete请求

Request中通过Delete来标识请求,默认是通过参数方式的,也可通过RequestBody来传递。

HttpUrl url = buildUrl("http://127.0.0.1:7087/study/test/testDelete",
        new HashMap<String, String>() {{
            put("id", "delId");
        }});
Request request = new Request.Builder()
        .url(url)
        .delete()
        .build();
Response resp = httpClient.newCall(request).execute();
System.out.println("Response: " + resp.body().string());
上一篇:最新Android开发进阶!斗鱼直播Android开发二面被刷,真香!


下一篇:万字长文带你拥抱OkHttp源码