前言:
HTTP是现代应用网络的方式。有效地进行HTTP使交互传输加载更快,并节省带宽。OkHttp是默认情况下是高效的HTTP客户端:HTTP/2支持允许对同一主机的所有请求共享套接字。连接池减少请求延迟(HTTP / 2不可用)。透明GZIP缩小下载大小。响应缓存可以避免重复请求的网络。OkHttp启动与现代TLS功能(SNI,ALPN)的新连接,如果握手失败,则返回到TLS 1.0。使用OkHttp很容易它的请求/响应API设计有流畅的构建器和不变性。它支持同步阻塞调用和具有回调的异步调用。
OkHttp对于Java,最低要求是JDK1.7。
概述:
依赖:
<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>
<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更简单易上手。
Request:OkHttp访问的请求类,请求类可以包含:方法、请求头列表、请求提
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);
}
异步获取:
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 将使备用地址);