标签:
很多人一看本文的标题,可能就没有看的欲望,因为很多人认为OKhttp现在已经没有当年那么辉煌了,在代码中直接使用也不如以前那么常见,因此,现在有很多人把它当作老的网络请求库,其实这真的是一个误区,OKhttp现在用的其实非常的广泛,只不过一些库对它做了再一次的封装,OKhttp变成了一些库的传输层而已,如Retrofit(默认传输层使用OKhttp),Volley(传输层可以设置为OKhttp)。
现在android发展的很快,没有多长时间就会出一个新的库,就拿网络库来说,现在比较火的算是Retrofit了,而Retrofit+Rxjava+Dagger又是一种架构方式,因此,我想利用博客对android的网络请求库在学习的过程中做一个总结,因为自己也是在学习,如有错误,欢迎指正。
先从OKhttp说起!
什么是OKhttp,OKhttp能干啥?
HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.
OkHttp is an HTTP client that’s efficient by default:
HTTP/2 support allows all requests to the same host to share a socket.
Connection pooling reduces request latency (if HTTP/2 isn’t available).
Transparent GZIP shrinks download sizes.
Response caching avoids the network completely for repeat requests.
OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), and falls back to TLS 1.0 if the handshake fails.
Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.
OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.
这是官网对Okhttp的简要总结,英语还行的可以看看,英语不好的,我给大家简要解释一下,这段英文主要说了:
HTTP / 2支持允许在同一主机的所有请求共享一个socket。
连接池减小了延迟。
GZIP缩小下载的大小。
响应缓存完全避免网络重复请求。
它会悄悄地从常见的连接问题恢复。如果你的服务有多个IP地址,如果第一个连接失败,OkHttp将尝试备用地址。
使用OkHttp很容易。它同时支持同步阻塞调用和回调的异步调用。
OkHttp支持Android 2.3及以上。对于Java,最低要求是1.7 。
Get请求可以分为两种方式:
OkHttpClient mOkHttp = new OkHttpClient();
final Request request = new Request.Builder()
.url("http://www.baidu.com/")
.build();
Call call = mOkHttp.newCall(request);
call.enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String str = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
textview.setText(str);
}
});
Log.i("flag",str);
}
});
以上就是GET请求的异步方式,其中有一点一定要特别的注意,就是onFailure
和onResponse
不是在UI线程中执行的。
OkHttpClient mOkHttp = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com/")
.build();
final Call call = mOkHttp.newCall(request);
new Thread(new Runnable() {
@Override
public void run() {
try {
String str = call.execute().body().string();
Log.i("flag",str);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
此段代码是GET请求的同步方式,也就是阻塞的,说白了,阻塞也就是,在这个线程中,只要我不执行完,谁都别想执行,听到这,读者可能想到了什么,对,这种方式是绝对不能再UI线程中使用的,所以使用这种方式,我们需要自己开一个线程,或者和AsyncTask一起使用。
利用post这种方式,我们可以提交很多的类型的数据,如json,键值对等,并且post方式也分为同步和异步两种方式,只是对于同步和异步来说,get和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();
}
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody formBody = new FormBody.Builder()
.add("para1", "eee")
.add("para2", "aaa")
.add("para3", "123")
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
注意:对于小的文件來說,string()这個方法在请求体里非常方便高效。但是,如果响应体很大,比如說大于1MiB,这样的话最好別用string(),因为这样干会把整个文件加载到内存中去,你果文件很大,那么最好是把响应体以流的形式输出。
//创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//开启一個子线程,实际使用中可以用线程池进行管理
public void run() throws Exception {
//创建一個请求
Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
//获取到返回的相应
Response response = client.newCall(request).execute();
//判断是否成功
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//获取到响应头
Headers responseHeaders = response.headers();
//把响应头的信息以键值对的形式输出
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
//以字符串的形式输出响应体
System.out.println(response.body().string());
}
Download a file on a worker thread, and get called back when the response is readable. The callback is made after the response headers are ready. Reading the response body may still block. OkHttp doesn’t currently offer asynchronous APIs to receive a response body in parts.(官方原文,但是怕翻译不准确,所以只是贴出)。
//创建这個OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//來一個子线程
public void run() throws Exception {
//创建请求
Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
//设置回调
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//失败处理
e.printStackTrace();
}
//处理相应结果
@Override
public void onResponse(Call call, Response response) throws IOException {
//判断响应是否成功
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
//获取到响应头的信息
Headers responseHeaders = response.headers();
//输出响应的信息
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
//输出响体
System.out.println(response.body().string());
}
});
}
头部信息其实指的就是http协议中的请求头,如果读者不了解http,请先移步http详解,现在我们就来添加请求头,遍历响应头。
//老规矩,创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//开启一個子线程
public void run() throws Exception {
//创建一個请求
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues") //这里是请求的Url
.header("User-Agent", "OkHttp Headers.java") //这里是请求的头部信息
.addHeader("Accept", "application/json; q=0.5") //这里为的就是演示使用这個方法添加,会不会覆盖掉,具体看上面的文字描述
.addHeader("Accept", "application/vnd.github.v3+json") //the same
.build();
//获取到响应对象
Response response = client.newCall(request).execute()
//判断响应的结果
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println("Server: " + response.header("Server"));
System.out.println("Date: " + response.header("Date"));
System.out.println("Vary: " + response.headers("Vary"));
}
向服务器提交字符串,但是要注意,因为整个请求体都是在内存中,所以文件不要太大,不要超过1M.
//创建一個常量,这玩意就是用于說明格式和编码的
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
//创建一個OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//创建一個子线程
public void run() throws Exception {
//这就是我们要上传的内容啦。
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
//创建请求,封装需要上传的字符串
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
//获取到响应对象
Response response = client.newCall(request).execute();
//处理结果
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
以流的形式作为请求体提交数据。请求体的内容生成和写的是一样的。下面这個例子直接写入Okio的BufferedSink.在你的项目中更常見的是从BufferedSink.outputStream()方法里获取到这個输出流对象。
//创建这個类型常量
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
//创建这個OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//开启一個子线程
public void run() throws Exception {
//创建请求体对象
RequestBody requestBody = new RequestBody() {
//复写这個上传的方法
@Override
public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
//
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
//构造数据
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
//创建请求
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
//获取到相应对象
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
//创建格式常量
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
//创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//创建子线程
public void run() throws Exception {
//创建file对象
File file = new File("README.md");
//发起请求
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
//获取提交响应结果
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
使用FormBody构建请求体,就像Html的表格标签。这键和值都会用URL编码进行编码。
//创建OkHttpClient实例对象
private final OkHttpClient client = new OkHttpClient();
//搞一個子线程
public void run() throws Exception {
//以参数的形式封装数据
RequestBody formBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
//发起请求
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(formBody)
.build();
//获取请求结果
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
使用MultparBody.Builder 可以构建符合HTML文件上复杂的请求表单。每一個请求体就是它自己的请求体,还可以定义好自己的头部信息,如果已经存在,那些头部信息应该描述请求体,例如它的内容位置(Content-Disposition),内容长度,内容类型,这些都会自动添加。前提是它们是可用的。
//定义客户端ID
private static final String IMGUR_CLIENT_ID = "...";
//提交的数据类型
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
//创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//创建一個子线程
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
//创建可扩展的请求体对象
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Square Logo")
.addFormDataPart("image", "logo-square.png",
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
//发起请求
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
//获取到这個提交结果
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
//定义客户端ID
private static final String IMGUR_CLIENT_ID = "...";
//提交的数据类型
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
//创建OkHttpClient对象
private final OkHttpClient client = new OkHttpClient();
//创建一個子线程
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
//创建可扩展的请求体对象
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Square Logo")
.addFormDataPart("image", "logo-square.png",
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
//发起请求
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
//获取到这個提交结果
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
Gson是一个在JSON和Java对象之间转换非常方便的api。这里我们用Gson来解析Github API的JSON响应。
注意:ResponseBody.charStream()使用响应头Content-Type指定的字符集来解析响应体。默认是UTF-8。
private final OkHttpClient client = new OkHttpClient();
private final Gson gson = new Gson();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://api.github.com/gists/c2a7c39532239ff261be")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue().content);
}
}
static class Gist {
Map<String, GistFile> files;
}
static class GistFile {
String content;
}
缓存响应,首先要有一个允许读和写的缓存目录,这个目录的大小要有一定的限制,还有重要的一点是,这个目录应该是私有的,不可信的应用不能读取其中的内容。
不要让多个缓存访问同一个缓存目录,大多数应用只需要调用一次”new OkHttpClient()”就可以,然后配置好缓存,之后在其他地方使用这个实例就可以,否则,多个缓存实例会相互干扰,是缓存数据出现错误,更严重的还会让你的程序崩溃。
响应缓存可以通过http头来配置,可以增加像”Cache-Control: max-stale=3600”的请求头,OkHttp的缓存会识别它,web服务器可以像Cache-control:max-age=9600这样配置缓存,缓存和头部信息会在网络请求相应地址时强制去返回,后者强制网络以get的形式去返回可用的响应。
private final OkHttpClient client;
public CacheResponse(File cacheDirectory) throws Exception {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
client = new OkHttpClient.Builder()
.cache(cache)
.build();
}
public void run() 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());
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));
}
为了防止使用缓存的响应,可以用CacheControl.FORCE_NETWORK。为了防止它使用网络,使用CacheControl.FORCE_CACHE。需要注意的是:如果您使用FORCE_CACHE和网络的响应需求,OkHttp则会返回一个504提示,告诉你不可满足请求响应。
使用”Cal.cancel()”停止一个正在执行的网络调用,如果当前一个线程正在发送一个请求或者读取一个响应,那么,就会收到一个IOException,使用这种方式来节约网络资源,例如,当用户从应用中离开的时候,那么不论当前是在同步还是异步调用的情况下,调用都可以被取消。
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
final long startNanos = System.nanoTime();
final Call call = client.newCall(request);
// Schedule a job to cancel the call in 1 second.
executor.schedule(new Runnable() {
@Override public void run() {
System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
call.cancel();
System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
}
}, 1, TimeUnit.SECONDS);
try {
System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
Response response = call.execute();
System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
(System.nanoTime() - startNanos) / 1e9f, response);
} catch (IOException e) {
System.out.printf("%.2f Call failed as expected: %s%n",
(System.nanoTime() - startNanos) / 1e9f, e);
}
}
Use timeouts to fail a call when its peer is unreachable. Network partitions can be due to client connectivity problems, server availability problems, or anything between. OkHttp supports connect, read, and write timeouts.
当网络访问不可达的时候,使用超时去终止一个网络访问,出现网络不可达的原因,可能是客户端连接问题,服务器问题,或者客户端到服务器中间的很多问题,OkHttp支持连接,读,和写超时。
private final OkHttpClient client;
public ConfigureTimeouts() throws Exception {
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
Response response = client.newCall(request).execute();
System.out.println("Response completed: " + response);
}
参考:http://square.github.io/okhttp/
http://blog.csdn.net/mynameishuangshuai/article/details/51303446
http://www.tuicool.com/search?kw=okhttp
标签:
原文地址:http://blog.csdn.net/u013240038/article/details/51606152