最近对应用的网络模块进行重构,从原有HttpClient切换到OkHttp;在此对OkHttp的使用做一个简单的总结,方便后续查阅。
官网地址:http://square.github.io/okhttp/
GitHub地址:https://github.com/square/okhttp
OkHttp框架是一个基于http协议(http协议介绍)的网络请求框架,实现的主要功能,
- 网络请求的调度
- 请求线程的复用
- 请求连接的复用
- 缓存策略的实现
- 网络路由的实现
- 拦截器
本文站在Http的角度对OkHttp的使用做一个简单的介绍。
OkHttp的配置
gradle配置
compile 'com.squareup.okhttp3:okhttp:3.8.1'
ProGuard配置
-dontwarn okio.**
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault
Request
每一个Http请求都包含一个请求行和请求头,也可能包含请求正文。在OkHttp中通过Request类表示一个http请求,采用构建者设计模式通过一个Request.Builder来构建一个Request.
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final RequestBody body;
final Object tag;
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
...省略代码...
}
通过Builder构建一个Request
Request.Builder builder = new Request.Builder();
...构建步骤url/method等...
Request request = builder.build();
请求行
builder.url(url);//设置url
builder.get();//设置请求方法为GET,也可以通过builder.method("GET",null)进行设置
请求头Headers
builder.addHeader("User-Agent","android 8.0");//设置User-Agent
builder.removeHeader("Cache-Control");//从Headers中移除Cache-Control
请求正文RequestBody
//请求正文为String类型
String content = "XXXXXXXXXXXXX";//请求正文内容
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"),content);
//请求正文为byte类型
byte[] bytes;//请求正文内容,需要初始化复制哈
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),bytes);
//请求正文为File类型(一般指上传文件之类的)
File file = new File("pathXXX");
RequestBody requestBody = RequestBody.create(MediaType.parse(" image/png"),bytes);
构建一个完整的请求Request
String content = "构建一个完整的OkHttp Request";
//构建RequestBody
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"),content);
Request.Builder builder = new Request.Builder();
//设置url、method、header等
builder.url("http://XXX").post(requestBody).addHeader("Cache-Control","no-cache");
//构建Request
Request request = builder.build();
Call
在OkHttp中,通过Call来执行一个Request,其真正的实现在RealCall中实现,具体逻辑到后面的源码分析阶段在分析。
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
Call提供了同步和异步两种执行方式。
Call异步执行
Call异步执行,调用enqueue(callback)函数,将请求加入到请求队列当中去,通过callback来监控请求的执行进度
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}});
onFailure:请求失败,这里的失败是指OkHttp执行失败,而不是服务端对请求返回code表示失败。
onResponse:表示与服务端请求通信成功,通过解析返回的response来解析服务端返回的具体结果
Call同步执行
Call同时提供了同步执行,调用execute()函数进行同步执行,同步执行会阻塞当前的线程,直到请求结果返回,因此要注意不要在UI线程中执行。execute()的返回值是Response;若请求失败为null,请求成功则对response进行解析。
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
//对response进行解析
Call取消请求
Call同时支持取消,对正在执行的请求进行取消操作
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
//突然不想执行了
call.cancel();
执行cancel()后,若是通过enqueue(callback)进行异步执行,则会调用callback的onFailure;若是同步执行,call.execute()返回的结果为null.
Response
Http请求与服务端正常通信之后会对客户端进行响应,在OkHttp中,将服务端的响应封装在Response中返回给上层,上层通过解析Response来获取服务端返回的数据。
public final class Response implements Closeable {
final Request request;//对应的请求
final Protocol protocol;
final int code;
final String message;
final Handshake handshake;
final Headers headers;
final ResponseBody body;
final Response networkResponse;
final Response cacheResponse;
final Response priorResponse;
final long sentRequestAtMillis;//发送请求的时间
final long receivedResponseAtMillis;//接受服务端数据的时间
...省略代码若干...
}
状态行
Response中的code和message分别表示状态行的状态码与状态码描述
final int code;
final String message;
判断服务端是否成功响应客户端的请求有两种方式:
第一种:通过response.code的值,来表示判断此次请求服务端是否正常响应
第二种:调用response.isSuccessful(),其内部也是通过比较code的值,在[200,300)之间返回true
响应头
Response将响应头的信息封装在Headers中,通过header(name)读取响应头中的信息
Response response = call.execute();
//读取Cache-Control信息
String cacheControl = response.header("Cache-Control");
//读取Expires信息
String expires = response.header("Expires");
响应正文
Response将响应正文的信息封装在ResponseBody中,通过ResponseBody读取响应正文中的信息,读取信息有三种方式,
Response response = call.execute();
//第一种方式:通过bytes()函数获取字节数组
byte[] data = response.body().bytes();
//第二种方式:通过string()获取字符串
String result = response.body().string();
//第三种方式:通过byteStream()获取InputStream,从流中读取数据
InputStream stream = response.body().InputStream();
注意事项:
1.第一种方式获取字节数组,会将整个响应正文都加载到内存;但对于比较大的响应正文(例如100M的文件),很有可能会导致OutOfMemoryError,此种情况下应该使用InputStream()代替。
2.第二种方式是直接将响应正文转化为字符串返回,但是对于非字符串或者某些加密过的字符串,则不能直接调用byte()来代替。同时所有数据也会被加载到内存当中,存在第一种方式引发OutOfMemoryError的可能。
3.第三种方式一般适合于文件等比较大,不适合全部都加载到内存的响应正文,例如文件下载等。
完整代码片段示例
Request.Builder builder = new Request.Builder();
Request request = builder.url("https://www.baidu.com/").get().build();
OkHttpClient okHttpClient = new OkHttpClient();
Call call = okHttpClient.newCall(request);
call.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
//抓取失败处理
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//判断是否抓取成功
if(response != null && response.isSuccessful()) {
String content = response.body().string();
//content为百度主页的内容
}
}});
若要了解文件的上传下载、表单等等的实现,可以参考OkHttp在Github上的WiKi。