一、使用OkHttp
OkHttp发送请求后,可以通过同步或异步地方式获取响应。下面就同步和异步两种方式进行介绍。
1.1、同步方式
发送请求后,就会进入阻塞状态,知道收到响应。下面看一个下载百度首页的例子:
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build()
Request request = new Request.Builder().url("http://www.baidu.com")
.get().build()
Call call = client.newCall(request)
try {
Response response = call.execute()
System.out.println(response.body().string())
} catch (IOException e) {
e.printStackTrace()
}
上面的代码先创建OkHttpClient和Request对象,两者均使用了Builder模式;然后将Request封装成Call对象,然后调用Call的execute()同步发送请求,最后打印响应。
1.2、异步方式
异步方式是在回调中处理响应的,同样看下载百度首页的例子:
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build()
Request request = new Request.Builder().url("http://www.baidu.com")
.get().build()
Call call = client.newCall(request)
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("Fail")
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string())
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
同样是创建OkHttpClient、Request和Call,只是调用了enqueue方法并在回调中处理响应。
上面介绍了同步、异步获取请求的步骤,都是比较简单的。
1.3、Request、Response、Call
上面的代码中涉及到几个常用的类:Request、Response和Call。下面分别介绍:
Request
每一个HTTP请求包含一个URL、一个方法(GET或POST或其他)、一些HTTP头。请求还可能包含一个特定内容类型的数据类的主体部分。
Response
响应是对请求的回复,包含状态码、HTTP头和主体部分。
重写请求
当将Request提交给OkHttp后,出于正确性和效率的考虑,OkHttp在传输请求之前会重写请求。
OkHttp可能会在请求中添加缺少的请求头,包括”Content-Length”,”Transfer-Encoding”,”User-Agent”,”HOST”,”Connection”和”Content-Type”等。
有些请求可能有缓存的响应。当缓存响应过时时,OkHttp可以做一个额外的GET请求获取最新的响应。这要求”If-Modified-Since”和”If-None-Match”头被添加。
重写响应
如果使用了透明压缩,OkHttp会丢弃”Content-Encoding”和”Content-Length”头,因为和解压后的响应主体不匹配。
如果一个额外的GET请求成功了,那么网络和缓存中的响应将会合并。
请求重定向
当请求的URL移动了,web服务器会返回一个302的状态码并指明文件的新地址。OkHttp将会重定向获取最终的响应。
请求重试
有时连接会失败,那么OkHttp会重试别的路由。
Call
当重写、重定向等时,一个请求可能会产生多个请求和响应。OkHttp使用Call抽象出一个满足请求的模型,尽管中间可能会有多个请求或响应。执行Call有两种方式,同步或异步,这在上面已经介绍过了。
Call可以在任何线程被取消。
二、拦截器
拦截器是一个监视、重写、重试请求的强有力机制。拦截器可以串联。
从图中可以看出,拦截器分为应用拦截器和网络拦截器两种。应用拦截器是在发送请求之前和获取到响应之后进行操作的,网络拦截器是在进行网络获取前进行操作的。
2.1、应用拦截器
下面定义一个应用拦截器,用于在请求发送前打印URL以及接受到响应后打印内容。
public class LogInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
System.out.println(request.toString());
Response response = chain.proceed(request);
System.out.println(response);
return response;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
上面的代码中,LogInterceptor实现了Interceptor接口。首先从chain中得到请求,然后打印请求;然后调用proceed方法处理请求得到响应,然后打印响应。调用代码如下:
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LogInterceptor()).build()
Request request = new Request.Builder().url("http://www.baidu.com")
.get().build()
Call call = okHttpClient.newCall(request)
try {
call.execute()
} catch (IOException e) {
e.printStackTrace()
}
可以看到通过调用addInterceptor方法添加应用拦截器。
2.2、网络拦截器
网络拦截器的使用和应用拦截器类似,只是调用OkHttpClient的addNetworkInterceptor方法即可。
OkHttpClient okHttpClient = new OkHttpClient.Builder().addNetworkInterceptor(new LogInterceptor()).build()
Request request = new Request.Builder().url("http://www.taobao.com")
.get().build()
Call call = okHttpClient.newCall(request)
try {
call.execute()
} catch (IOException e) {
e.printStackTrace()
}
下面是运行结果:
Request{method=GET, url=http://www.taobao.com/, tag=Request{method=GET, url=http://www.taobao.com/, tag=null}}
Response{protocol=http/1.1, code=302, message=Found, url=http://www.taobao.com/}
Request{method=GET, url=https://www.taobao.com/, tag=Request{method=GET, url=http://www.taobao.com/, tag=null}}
Response{protocol=http/1.1, code=200, message=OK, url=https://www.taobao.com/}
可以发现,拦截器运行了两次。一次是初始请求”http://www.taobao.com“,一次是请求重定向”https://www.taobao.com“。
2.3、应用拦截器和网络拦截器的比较
每个拦截器由它各自的优势。
应用拦截器
- 不需要考虑中间状态的响应,比如重定向或者重试。
- 只会被调用一次,甚至于HTTP响应保存在缓存中。
- 观察应用程序的原意。
- 允许短路,可以不调用Chain.proceed()方法
- 允许重试和发送多条请求,调用Chain.proceed()方法
网络拦截器
- 可以操作中间状态的响应,比如重定向和重试
- 不调用缓存的响应
- 可以观察整个网络上传输的数据
- 获得携带请求的Connection
2.4、重写请求
拦截器可以添加、移除或者替换请求的头信息,也可以改变传输的主体部分。下面的一个拦截器对请求主体进行Gzip压缩。
final class GzipRequestInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
return chain.proceed(originalRequest);
}
Request compressedRequest = originalRequest.newBuilder()
.header("Content-Encoding", "gzip")
.method(originalRequest.method(), gzip(originalRequest.body()))
.build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) {
return new RequestBody() {
@Override public MediaType contentType() {
return body.contentType();
}
@Override public long contentLength() {
return -1;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
2.5、重写响应
同样地,拦截器可以重写响应的头部以及主体部分。但是
/** Dangerous interceptor that rewrites the server's cache-control header. */
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=60")
.build();
}
};
备注:
备注:
-
同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;
-
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。
-
区别:一个需要等待,一个不需要等待,在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。
-
哪些情况建议使用同步交互呢?比如银行的转账系统,对数据库的保存操作等等,都会使用同步交互操作,其余情况都优先使用异步交互。
三、总结
本篇文章主要介绍了OkHttp进行GET的同步、异步请求,对于HTTP其他方法,比如POST等都是可以进行的,这儿就不过多介绍了,想了解的朋友可以到OkHttp Github地址查看.