OkHttp是一个高效的HTTP客户端,能有效的提高数据加载的速度,节省带宽。
1. HTTP/2 支持同一主机的所有请求共享同一个socket。
2. 当HTTP/2 不可用时,使用连接池减少请求的延迟。
3. 透明的GZIP 压缩减少下载数据的大小。
4. 缓存响应避免重复的网络请求。
OkHttp 在网络性能不好的情况下能够很好的工作,避免常见的网络连接问题。如果你的HTTP服务有多个IP地址,OkHttp 在第一次连接失败时,会尝试其他的地址。这对于 IPv4+IPv6 以及托管在冗余数据中心的服务来说是必要的。OkHttp 使用TLS 特性 (SNI, ALPN)初始化HTTP连接,并在握手失败时降级使用TSL1.0 尝试初始化连接。
OkHttp支持同步和异步请求,在 Android 2.3版本,Java 1.7 版本之上可以使用。
一、 基本使用
1. 创建OkHttpClient
//使用默认的设置参数创建OkHttpClient
public final OkHttpClient mClient = new OkHttpClient()
//使用Builder自定义设置
public final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
.readTimeout(5, TimeUnit.SECONDS)
.build();
由于每个OkHttpClient 对象都持有自己的线程池和连接池,所以应用中应该创建一个client 对象,然后所有的HTTP 请求复用这个对象发送请求,从而减少网络延迟,节省内存。
使用newBuilder() 方法可以个性化共享的OkHttpClient, 这个client 和原有client可以共享连接池和线程池。
OkHttpClient eagerClient = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();默认OkHttpClient 是不需要Shutdown,持有的线程和连接会在空闲时自动释放。
//关闭线程池
client.dispatcher().executorService().shutdown();
//关闭连接池
client.connectionPool().evictAll();
//关闭缓存
client.cache().close();
停止线程池,以后发送到这个client 的Call 将直接被丢弃; 关闭缓存时如果正在创建Call,会导致Crash 。
2. 同步请求:
public void syncRequest() {
//创建Request对象
Request mRequest = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
//创建Call 对象
Call mCall = mClient.newCall(mRequest);
//调用Call 的execute()发送同步请求
try {
Response response = mCall.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}发送同步请求后,就会进入阻塞状态,直到收到响应。
3. 异步请求
public void asyncRequest() {
//创建Request对象
Request request = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
//将Request 封装成Call 对象
Call call = mClient.newCall(request);
//调用Call的enqueue方法进行异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//子线程返回
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
//子线程返回
}
});
}4. POST请求
1)post提交json数据
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class PostExample {
//声明 MediaType 为JSON
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
final OkHttpClient mOkHttpClient = new OkHttpClient();
String post(String url, String data) throws IOException {
//创建 RequestBody对象
RequestBody body = RequestBody.create(JSON, data);
//通过post()方法添加requstbody
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
//发送请求
try (Response response = mOkHttpClient.newCall(request).execute()) {
return response.body().string();
}
}
String bowlingJson(String player1, String player2) {
return "{'winCondition':'HIGH_SCORE',"
+ "'name':'Bowling',"
+ "'round':4,"
+ "'lastSaved':1367702411696,"
+ "'dateStarted':1367702378785,"
+ "'players':["
+ "{'name':'" + player1 + "','history':[10,8,6,7,8],'color':-13388315,'total':39},"
+ "{'name':'" + player2 + "','history':[6,10,5,10,10],'color':-48060,'total':41}"
+ "]}";
}
public static void main(String[] args) throws IOException {
PostExample example = new PostExample();
String json = example.bowlingJson("Jesse", "Jake");
String response = example.post("http://www.roundsapp.com/post", json);
System.out.println(response);
}
}
2) 提交key-value
String post(String url) throws IOException {
RequestBody body = new FormBody.Builder()
.add("platform", "android")
.addEncoded("site", URLEncoder.encode("https://www.baidu.com/"))
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = mOkHttpClient.newCall(request).execute()) {
return response.body().string();
}
}RequestBody是一个抽象类,提供了提交 String字符串,byte[] 字节数组, File文件的静态方法,可以返回一个RequestBody对象,
public static RequestBody create(MediaType contentType, String content)
public static RequestBody create(MediaType contentType, byte[] content)
public static RequestBody create(MediaType contentType, byte[] content,
int offset, int byteCount)
public static RequestBody create(MediaType contentType, final File file)
FormBody继承自RequestBody,提供了Builder类来创建FormBody对象;同时还有MultipartBody也继承了RequestBody,并提供了Builder返回一个MultipartBody对象。
5. ResponseBody 与 Response
每个响应体都由一个有限的资源支持,比如套接字(网络请求)或打开的文件(用于缓存的响应)。
ResponseBody 和 Response都实现了Closeable 接口,如果使用了Call.execute() 或者实现了Callback.onResponseI() 一定要close它,可以通过下面的任意方法关闭:
Response.close()
Response.body().close()
Response.body().source().close()
Response.body().charStream().close()
Response.body().byteString().close()
Response.body().bytes()
Response.body().string()或者使用带资源的try语句:
Call call = client.newCall(request);
try (Response response = call.execute()) {
... // Use the response.
} Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response) throws IOException {
//带资源的try语句保证try块退出或存在异常时,都会自动调用res.close()
try (ResponseBody responseBody = response.body()) {
... // Use the response.
}
}
public void onFailure(Call call, IOException e) {
... // Handle the failure.
}
});bytes()方法,string()方法会将 Response全部加载到内存,source()方法,byteStream()方法,charStream()会按流读入,不会将整个响应读入内存,可以读取比较大的响应;