Android为我们提供了两种HTTP交互的方式: HttpURLConnection 和 Apache HTTP Client,虽然两者都支持HTTPS,流的上传和下载,配置超时,IPv6和连接池,已足够满足我们各种HTTP请求的需求。但更高效的使用HTTP可以让您的应用运行更快、更节省流量。而OkHttp库就是为此而生。 OkHttp是一个高效的HTTP库。
Android中网络请求的进化图:
OkHttp是一个高效的HTTP库:
1. 支持 SPDY ,共享同一个Socket来处理同一个服务器的所有请求
2. 如果SPDY不可用,则通过连接池来减少请求延时
3. 无缝的支持GZIP来减少数据流量
4. 缓存响应数据来减少重复的网络请求
优点:
OkHttp会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。
使用 OkHttp 无需重写您程序中的网络代码。OkHttp实现了几乎和java.net.HttpURLConnection一样的API。如果您用了 Apache HttpClient,则OkHttp也提供了一个对应的okhttp-apache 模块
Okhttp的基本使用,从以下五方面讲解:
1、Get请求(同步和异步)
2、POST请求表单(key-value)
3、POST请求提交(JSON/String/文件等)(这个有待研究)
4、文件下载
5、请求超时设置
OkHttp脑图:
OkHttp3设计思路:
添加库依赖
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.github.franmontiel:PersistentCookieJar:v0.9.3' // Cookies持久化库
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
1、GET
//同步GET请求
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
return response.body().string();
}
//异步GET请求
OkHttpClient client=new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {}
@Override
public void onResponse(Response response) throws IOException {}
});
2、POST
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();
Response response = client.newCall(request).execute();
return response.body().string();
}
3、异步调用
使用enqueue方法,将call放入请求队列,然后okHttp会在线程池中进行网络访问
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://github.com").build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d("onResponse", "response.body(): "+response.body().string());
}
});
4、HTTP头部的设置和读取
HTTP 头的数据结构是 Map<String, List<String>>
类型。
对于每个HTTP头,可能有多个值。但是大部分HTTP头都只有一个值,只有少部分HTTP头允许多个值。OkHttp的处理方式是:
Request:
header(name,value) 来设置HTTP头的唯一值
addHeader(name,value) 来补充新值
Response:
header(name) 读取唯一值或多个值的最后一个值
headers(name) 获取所有值
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://github.com")
.header("User-Agent", "super agent")
.addHeader("Accept", "text/html")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("error: " + response);
}
System.out.println(response.header("Server"));
System.out.println(response.headers("Set-Cookie"));
5、表单提交
RequestBody formBody = new FormEncodingBuilder()
.add("query", "Hello")
.build();
6、文件上传
指定 MultipartBuilder.FORM 类型并通过addPart方法添加不同的Part(每个Part由Header和RequestBody两部分组成),最后调用builde()方法构建一个RequestBody。
MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "input txt"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"file\""),
RequestBody.create(MEDIA_TYPE_TEXT, new File("input.txt")))
.build();
7、使用流的方式发送POST请求
OkHttpClient client = new OkHttpClient();
final MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");
final String postBody = "Hello World";
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MEDIA_TYPE_TEXT;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8(postBody);
}
@Override
public long contentLength() throws IOException {
return postBody.length();
}
};
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("eror: " + response);
}
System.out.println(response.body().string());
8、缓存控制CacheControl
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder().noCache().build()) // 强制不缓存
.url("http://publicobject.com/helloworld.txt")
.build();
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder().maxAge(0, TimeUnit.SECONDS).build()) // 缓存策略由服务器指定
.url("http://publicobject.com/helloworld.txt")
.build();
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder().onlyIfCached().build()) //强制缓存
.url("http://publicobject.com/helloworld.txt")
.build();
Response forceCacheResponse = client.newCall(request).execute();
if (forceCacheResponse.code() != 504) {
// The resource was cached! Show it.
} else {
// The resource was not cached.
}
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build()) // 允许使用旧的缓存
.url("http://publicobject.com/helloworld.txt")
.build();
9、拦截器
可以监视,重写(重写请求、重写响应)和重试
拦截器可以注册为应用拦截器和网络拦截器
应用拦截器:
不需要关心像重定向和重试这样的中间响应
总是调用一次,即使HTTP响应从缓存中获取服务
监视应用原始意图。不关心OkHttp注入的像If-None-Match头
允许短路并不调用Chain.proceed()
允许重试并执行多个Chain.proceed()调用
网络拦截器:
可以操作像重定向和重试这样的中间响应
对于短路网络的缓存响应不会调用
监视即将要通过网络传输的数据
访问运输请求的Connection
重写请求
//重写响应
private static final Interceptor 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();
}
};
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor() // 应用拦截器
.addNetworkInterceptor(interceptor) // 网络拦截器
.setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
10、缓存设置
File cacheDirectory = new File(getApplicationContext().getCacheDir().getAbsolutePath(), "Cache");
Cache cache = new Cache(cacheDirectory, 10 * 1024 * 1024)); // 创建缓存类
OkHttpClient client = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS) // 连接超时设置
.readTimeout(10,TimeUnit.SECONDS); // 读超时设置
.writeTimeout(10,TimeUnit.SECONDS); // 写超时设置
.cache(cache) // 设置缓存,okhttp默认是没有缓存,且没有缓存目录的
.build();
9、Cookies缓存:由CookieJar统一管理Cookies,只需要对OkHttpClient的cookieJar进行设置即可
OkHttpClient mHttpClient = new OkHttpClient.Builder().cookieJar(new CookieJar() {
private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
}).build();
10、Cookies持久化:PersistentCookieJar
ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cookieJar(cookieJar)
.build();