okHttp 基础应用
来源:互联网 发布:世界地图gis数据 编辑:程序博客网 时间:2024/06/05 23:46
1.简介
OKHttp是一款高效的HTTP客户端,支持连接同一地址的链接共享同一个socket,通过连接池来减小响应延迟,还有透明的GZIP压缩,请求缓存等优势
2.配置要求
支持Android 2.3及其以上版本,要求Java JDK1.7以上
3.引入
eclipse引入
Android Studio 引入
MAVEN
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>(insert latest version)</version>
</dependency>
GRADLE
compile 'com.squareup.okhttp3:okhttp:(insert latest version)'
4.应用(官方文档解释)
同步get
下载一个文件,打印他的响应头,以string形式打印响应体。响应体的 string() 方法对于小文档来说十分方便、高效。但是如果响应体太大(超过1MB),应避免适应 string() 方法 ,因为他会将把整个文档加载到内存中。
对于超过1MB的响应body,应使用流的方式来处理body。
public void sy_get() throws IOException {
Request request = new Request.Builder().
url("http://publicobject.com/helloworld.txt").
build();
Response response = client.newCall(request).execute();
Log.e("test", "成功");
if (response.isSuccessful()) {
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": "
+ responseHeaders.value(i));
}
Log.e("test", response.body().string());
} else {
throw new IOException("Unexpected code " + response);
}
异步get
在一个工作线程中下载文件,当响应可读时回调Callback接口。读取响应时会阻塞当前线程。OkHttp现阶段不提供异步api来接收响应体。
public void as_get() 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 arg0, IOException arg1) {
}
@Override
public void onResponse(Call arg0, Response response) throws IOException {
if (response.isSuccessful()) {
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": "
+ responseHeaders.value(i));
}
Log.e("test", response.body().string());
} else {
throw new IOException("Unexpected code " + response);
}
}
});
提取响应头
典型的HTTP头 像是一个 Map<String, String> :每个字段都有一个或没有值。但是一些头允许多个值,像Guava的 Multimap 。例如:HTTP响应里面提供的 Vary 响应头,就是多值的。OkHttp的api试图让这些情况都适用。
当写请求头的时候,使用 header(name, value) 可以设置唯一的name、value。如果已经有值,旧的将被移除,然后添加新的。使用 addHeader(name, value) 可以添加多值(添加,不移除已有的)。
当读取响应头时,使用 header(name) 返回最后出现的name、value。通常情况这也是唯一的name、value。如果没有值,那么 header(name) 将返回null。如果想读取字段对应的所有值,使用 headers(name) 会返回一个list。
为了获取所有的Header,Headers类支持按index访问。
/**
* 提取响应头
* @throws Exception
*/
public void get_header() throws Exception {
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Log.e("test", "Server: " + response.header("Server"));
Log.e("test", "Date: " + response.header("Date"));
Log.e("test", "Vary: " + response.headers("Vary"));
} else {
throw new IOException("Unexpected code " + response);
}
}
Post方式提交String
使用HTTP POST提交请求到服务。这个例子提交了一个markdown文档到web服务,以HTML方式渲染markdown。因为整个请求体都在内存中,因此避免使用此api提交大文档(大于1MB)。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
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());
}
Post方式提交流
以流的方式POST提交请求体。请求体的内容由流写入产生。这个例子是流直接写入Okio的BufferedSink。你的程序可能会使用OutputStream,你可以使用BufferedSink.outputStream()来获取。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
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());
}
Post方式提交文件
以文件作为请求体是十分简单的。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
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());
}
Post方式提交表单
使用FormEncodingBuilder来构建和HTML<form>标签相同效果的请求体。键值对将使用一种HTML兼容形式的URL编码来进行编码。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody formBody = new FormEncodingBuilder()
.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());
}
Post方式提交分块请求
MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头。这些请求头可以用来描述这块请求,例如他的Content-Disposition。如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
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 MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
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响应
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 OkHttp(),在第一次调用时配置好缓存,然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。
响应缓存使用HTTP头作为配置。你可以在请求头中添加Cache-Control: max-stale=3600 ,OkHttp缓存会支持。你的服务通过响应头确定响应缓存多长时间,例如使用Cache-Control: max-age=9600。
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();
client.setCache(cache);
}
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));
}
取消一个Call
使用Call.cancel()可以立即停止掉一个正在执行的call。如果一个线程正在写请求或者读响应,将会引发IOException。当call没有必要的时候,使用这个api可以节约网络资源。例如当用户离开一个应用时。不管同步还是异步的call都可以取消。
你可以通过tags来同时取消多个请求。当你构建一请求时,使用RequestBuilder.tag(tag)来分配一个标签。之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call。
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);
}
}
超时
没有响应时使用超时结束call。没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西。OkHttp支持连接,读取和写入超时。
private final OkHttpClient client;
public ConfigureTimeouts() throws Exception {
client = new OkHttpClient();
client.setConnectTimeout(10, TimeUnit.SECONDS);
client.setWriteTimeout(10, TimeUnit.SECONDS);
client.setReadTimeout(30, TimeUnit.SECONDS);
}
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);
}
每个call的配置
使用OkHttpClient,所有的HTTP Client配置包括代理设置、超时设置、缓存设置。当你需要为单个call改变配置的时候,clone 一个OkHttpClient。这个api将会返回一个浅拷贝(shallow copy),你可以用来单独自定义。下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/1") // This URL is served with a 1 second delay.
.build();
try {
Response response = client.clone() // Clone to make a customized OkHttp for this request.
.setReadTimeout(500, TimeUnit.MILLISECONDS)
.newCall(request)
.execute();
System.out.println("Response 1 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 1 failed: " + e);
}
try {
Response response = client.clone() // Clone to make a customized OkHttp for this request.
.setReadTimeout(3000, TimeUnit.MILLISECONDS)
.newCall(request)
.execute();
System.out.println("Response 2 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 2 failed: " + e);
}
}
处理验证
这部分和HTTP AUTH有关。
相关资料:HTTP AUTH 那些事 - 王绍全的博客 - 博客频道 - CSDN.NET
OkHttp会自动重试未验证的请求。当响应是401 Not Authorized时,Authenticator会被要求提供证书。Authenticator的实现中需要建立一个新的包含证书的请求。如果没有证书可用,返回null来跳过尝试。
public List<Challenge> challenges()
Returns the authorization challenges appropriate for this response's code.
If the response code is 401 unauthorized,
this returns the "WWW-Authenticate" challenges.
If the response code is 407 proxy unauthorized, this returns the "Proxy-Authenticate" challenges.
Otherwise this returns an empty list of challenges.
当需要实现一个Basic challenge, 使用Credentials.basic(username, password)来编码请求头。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
client.setAuthenticator(new Authenticator() {
@Override public Request authenticate(Proxy proxy, Response response) {
System.out.println("Authenticating for response: " + response);
System.out.println("Challenges: " + response.challenges());
String credential = Credentials.basic("jesse", "password1");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
@Override public Request authenticateProxy(Proxy proxy, Response response) {
return null; // Null indicates no attempt to authenticate.
}
});
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
5.应用(常见应用)
POST提交Json数据
使用Request的post方法来提交请求体RequestBody
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();
f (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
POST提交键值对
很多时候我们会需要通过POST方式把键值对数据传送到服务器。 OkHttp提供了很方便的方式来做这件事情。
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody formBody = new FormEncodingBuilder()
.add("platform", "android")
.add("name", "bug")
.add("subject", "XXXXXXXXXXXXXXX")
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
6.浅谈封装
在前面熟悉了OkHttp的用法之后,为了简化用法同时适用于我的项目,我针对OkHttp进行了更进一步的封装(源码及其Demo地址在https://github.com/huyongli/TigerOkHttp),封装之后其支持的功能特性包括如下:
1.一般的get同步阻塞请求和异步请求
2.一般的post同步阻塞请求和异步请求
3.实现了文件上传功能(包含文件上传进度回调显示)
4.实现了大文件下载功能,只需要指定文件下载路径即可,也包含了下载进度的回调显示
5.实现了请求结果的自动解析,用户也可以根据需求扩展自定义结果解析类
6.对所有请求都支持直接将结果解析转换为JavaBean对象或集合
7.支持对返回结果结构的自定义,例如设置返回结果结构为:{flag:1|0,error:错误信息,result:请求结果},结果解析的时候会按照此结构进行结果解析
8.支持取消某个请求
如果还不知道如何使用OkHttp请参考我的上一篇文章 Android中OkHttp的使用 进行了解。
初始化配置
所有的请求在使用之前,先在Application里执行TigerOkHttp.init(new TigerHttpConfig(getApplicationContext()));
进行TigerOkHttp的初始化操作。
TigerHttpConfig
中主要是设置请求的全局性配置:
1. TigerHttpConfig.readTimeOut
:设置全局请求的数据读取超时时间,默认为30s
2.TigerHttpConfig.writeTimeOut
:设置全局请求的数据写入超时时间,默认为15s
3.TigerHttpConfig.connectTimeOut
:设置全局请求的连接超时时间,默认为15s
4.TigerHttpConfig.cacheSize
:设置全局请求的缓存大小,默认为10M
5.TigerHttpConfig.cacheDirectory
:设置全局请求的缓存存储路径,默认为系统给应用分配的缓存路径
6.TigerHttpConfig.isWrapperResult
:设置全局请求结果是否对结果进行了封装,默认为true
7.TigerHttpConfig.wrapperJsonResult
:设置全局请求结果的结构类型,在isWrapperResult为true时才起作用
WrapperJson
主要自定义设置全局请求结果的结构类型:
1.WrapperJson.code_name
:设置请求成功与否的状态码字段,默认为:flag
2.WrapperJson.result_name
:设置请求成功后结果字段,默认为:result
3.WrapperJson.error_name
:设置请求失败后的错误信息字段(仅在请求失败时才有值),默认为:error
4.WrapperJson.code_error_value
:设置请求失败的状态码值,当请求状态码与此值相等时代表请求失败
Get请求
上面即为一般Get同步阻塞请求和异步请求的方式,对这两个请求需要说明几点:
1.首先根据请求URL构造一个请求对象TigerJsonRequest
,如果为同步阻塞请求必须调用具有两个参数的构造函数以设置请求地址和设置请求返回的结果类型,如果是异步请求则只需要调用一个参数的构造函数设置请求地址即可(异步请求的返回结果类型会自动根据泛型判断)
2.在请求对象添加此请求相关的请求参数
3.如果是异步请求,在请求对象中添加异步请求回调接口RequestCallback
4.根据是否为异步请求在TigerOkHttp
中选择对应的请求方法开始请求操作
5.如果解析后返回Model对象话,服务端返回的结果必须是JSON字符串,如果解析后返回Model集合的话,服务端返回的结果必须是JSON数组字符串
Post请求
这里的Post同步、异步请求和上面的Get的同步异步请求方式基本上一样,构造请求对象TigerJsonRequest
的方式是一模一样的,唯一的区别只是在最后发起请求操作上更换了请求方法
1.post同步阻塞请求的发起方式为:T result = TigerOkHttp.postSync(request);
2.post异步请求的发起方式为:TigerOkHttp.postAsync(request);
自定义结果解析器
当TigerJsonRequest
自带的默认解析器TigerJsonParser
无法满足你的功能需求时,我们只需要简单的两步即可为某次请求实现自定义解析器:
1. 继承TigerParser<T>
(会自动根据设置的全局结果结构进行解析判断请求是否成功、拿到请求的真正结果数据)这个解析基类然后实现public abstract T parser(BufferedSource source) throws TigerHttpException;
方法
2.通过TigerJsonRequest
类中的方法setDataParser(TigerParser dataParser)
设置此次请求的结果解析器
上传文件
上传文件模块我是在TigerJsonRequest
基础上扩展出了一个专门针对上传文件的请求对象TigerUploadRequest
,具体的上传文件实现方式如下:
下载文件
下载文件功能需要用到另外一个请求对象TigerFileRequest
取消某个请求
取消某个请求只需要调用TigerOkHttp.cancel(request.getTag())
即可,如果没有手动设置请求对象request的tag值,请求的时候会自动为这次请求生成一个唯一的tag
- okHttp 基础应用
- okhttp应用
- okhttp-应用
- OKhttp基础
- okhttp简单的应用
- okhttp初级应用
- okHttp简单应用
- OkHttp框架的应用
- okhttp应用拦截器
- Android 网络okHttp基础
- okHttp基础分析
- OkHttp 基础(1)
- okHttp 基础及封装
- okhttp使用基础
- OkHttp缓存功能的应用
- okhttp框架解析与应用
- Android中OkHttp基础用法
- Okhttp Get请求的简单应用
- js闭包的理解以及闭包中this的理解
- HDOJ 5753 (2016多校联合训练 Training Contest 3) Permutation Bo
- 计算机网络面试相关
- java中trim的理解
- Struts2 - 常用的constant总结
- okHttp 基础应用
- Disruptor原理
- Java Web学习(22): 阶段小项目实现商品浏览记录
- 色达
- 学String类 有感
- 深入理解RxJava的Side Effect Methods
- LocNet: Improving Localization Accuracy for Object Detection
- 实时更新线上App:JSPatch
- 一些不错的文档