okHttp框架
OKHttp是一款高效的HTTP客户端,支持连接同一地址的链接共享同一个socket,
通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势
jar包依赖
需要添加两个jar包okhttp和对应版本的okio
本文使用okhttp3.3 okio1.6
GET 方式
建立连接
Request对象用于建立连接
// 首先得有一个OkHttpClient对象
OkHttpClient mOkClient = new OkHttpClient();
// 创建请求对象
Request request = new Builder().url(URL_STR).build();
接收请求
Reponse对象用于接收响应
Response response = mOkClient.newCall(request).execute();
以同步的方式接收请求,会产生阻塞
public class Main {
private static OkHttpClient mOkClient = new OkHttpClient();
private final static String URL_STR = "https://www.baidu.com";
public static void main(String[] args) {
// 创建请求对象
Request request = new Builder().url(URL_STR).build();
try {
// 创建回调并执行响应
Response response = mOkClient.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
异步执行
通过request的对象去构造得到一个Call对象,类似于将你的请求封装成了任务,既然是任务,就会有execute()和cancel()等方法。
将call加入调度队列,然后等待任务执行完成,我们在Callback中即可得到结果。
public class Main {
private static OkHttpClient mOkClient = new OkHttpClient();
private final static String URL_STR = "https://www.baidu.com";
public static void main(String[] args) {
// 创建请求对象
Request request = new Builder().url(URL_STR).build();
// 创建回调对象,用于请求后的回调
Call call = mOkClient.newCall(request);
call.enqueue(new Callback() {
// 收到响应时回调
@Override
public void onResponse(Call call, Response reponse) throws IOException {
String htmlBody = reponse.body().string();
System.out.println(htmlBody);
}
// 没有收到响应时回调,响应失败
@Override
public void onFailure(Call arg0, IOException arg1) {
}
});
}
}
通过response.string(),以字符串的形式 描述响应,response.bytes()以字节数组的形式描述响应
,response.inputstream(),获取该响应的输入流(支持大文件的读取)
如果响应体超过1MB,应该使用流进行存取(string会将文件加载到内存中)
输出结果(两种执行方式均相同)
<html>
<head>
<script>
location.replace(location.href.replace("https://","http://"));
</script>
</head>
<body>
<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>
POST 方式,携带参数
构建POST请求
Request request = new Request.Builder()
.url("https://www.baidu.com")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
此处除了构造request对象略有不同之外,其他与GET请求均相同
public class Main2 {
private static OkHttpClient mOkHttpClient = new OkHttpClient();
private static String URL = "https://www.baidu.com";
public static void main(String[] args) {
// 构造post参数 okhttp3.x中的方法
FormBody body = new FormBody.Builder().add("key", "value").build();
Request request = new Request.Builder().url(URL).build();
// 其他与GET请求相同,这里仅用同步方式测试
try {
Response response = mOkHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.code());
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
打印响应头
// 打印响应头
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
System.out.println(headers.name(i) + " : " + headers.value(i));
}
/*
Server : nginx/1.4.6 (Ubuntu)
Date : Fri, 27 May 2016 01:43:43 GMT
Content-Type : text/plain
Content-Length : 1759
Last-Modified : Tue, 27 May 2014 02:35:47 GMT
Connection : keep-alive
ETag : "5383fa03-6df"
Accept-Ranges : bytes
*/
响应缓存
为了缓存响应,你需要一个你可以读写的缓存目录,和缓存大小的限制。这个缓存目录应该是私有的,不信任的程序应不能读取缓存内容。
一个缓存目录同时拥有多个缓存访问是错误的。大多数程序只需要调用一次new OkHttp(),在第一次调用时配置好缓存,
然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。
响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。
你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600。
public class Main3 {
// 设置缓存目录
private static File CACHE_DIR = new File("./cache");
public static void main(String[] args) {
int cacheSize = 1024 * 1024 * 10; // 1MB
// 创建Okhttpclient对象
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 创建缓存 注意okhttp3.3创建缓存的方法和2.X版本有很大不同,需在Okhttpclient对象中创建
Cache cache = new Cache(CACHE_DIR, cacheSize);
builder.cache(cache);
OkHttpClient client = builder.build();
// 使用新线程开始请求
new Thread() {
public void run() {
try {
execute(client);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
}
/**
* 使用requset建立请求
*/
public static void execute(OkHttpClient client) throws Exception {
Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
Response response1 = client.newCall(request).execute();
if (!response1.isSuccessful())
throw new IOException("Unexpected code " + response1);
String response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
System.out.println("Response 1 cache response: " + response1.cacheResponse());
System.out.println("Response 1 network response: " + response1.networkResponse());
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
// 再次建立请求
Response response2 = client.newCall(request).execute();
if (!response2.isSuccessful())
throw new IOException("Unexpected code " + response2);
String response2Body = response2.body().string();
System.out.println("Response 2 response: " + response2);
System.out.println("Response 2 cache response: " + response2.cacheResponse());
System.out.println("Response 2 network response: " + response2.networkResponse());
// 如果为同一个对象,则缓存有效
System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}
}
/*
结果:
Response 1 response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 1 cache response: null
Response 1 network response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 cache response: Response{protocol=http/1.1, code=200, message=OK, url=https://publicobject.com/helloworld.txt}
Response 2 network response: null
Response 2 equals Response 1? true
*/
此结果表明,缓存被重用
缓存文件
取消操作
网络操作中,经常会使用到对请求的cancel操作,okhttp的也提供了这方面的接口,call的cancel操作。
使用Call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException,
同时可以通过Request.Builder.tag(Object tag)给请求设置一个标签,
并使用OkHttpClient.cancel(Object tag)来取消所有带有这个tag的call。
但如果该请求已经在做读写操作的时候,cancel是无法成功的,会抛出IOException异常。
public class Main4 {
private static OkHttpClient client = new OkHttpClient();
public static void main(String[] args) {
// 这个Http请求会延时2秒
Request request = new Request.Builder().url("http://httpbin.org/delay/2").build();
Call call = client.newCall(request);
long startTime = System.nanoTime(); // 系统当前时间戳
// 在1秒的时候取消请求
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startTime) / 1e9f);
call.cancel();
System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startTime) / 1e9f);
}
// 此处将关闭时间设置5秒左右(视网络情况而定),输出结果为关闭失败
// 此处将关闭时间设置为2秒以内,输出为关闭成功,会捕获到 java.net.SocketException
}, 2000); // second mills
// 开始执行请求
try {
System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startTime) / 1e9f);
Response response = call.execute();
System.out.printf("call is cancel:" + call.isCanceled() + "%n");
System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
(System.nanoTime() - startTime) / 1e9f, response);
} catch (IOException e) {
// 捕获到异常,表示取消请求
System.out.printf("%.2f Call failed as expected: %s%n", (System.nanoTime() - startTime) / 1e9f, e);
}
}
}
// 设置参数不同,此时会有两种可能
/*
关闭失败,此时已结束IO
0.00 Executing call.
call is cancel:false
2.94 Call was expected to fail, but completed:
Response{protocol=http/1.1, code=200, message=OK, url=http://httpbin.org/delay/2}
10.00 Canceling call.
10.00 Canceled call.
*/
/*
关闭成功,此时尚未接收响应
0.00 Executing call.
2.00 Canceling call.
2.00 Canceled call.
2.02 Call failed as expected: java.net.SocketException: Socket operation on nonsocket: configureBlocking
*/
基础的使用先介绍到这里,随后会介绍更复杂的用法。