深入浅出OkHttp:全面掌握HTTP客户端使用指南

更新:11-13 现代故事 我要投稿 纠错 投诉

其实深入浅出OkHttp:全面掌握HTTP客户端使用指南的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享深入浅出OkHttp:全面掌握HTTP客户端使用指南的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!

1. 历史上Http请求库优缺点

在讲OkHttp之前,我们先来看看在没有OkHttp的时代我们是如何完成http请求的。

在OkHttp 之前的日子里,我们使用HttpURLConnection 或HttpClient。那么两者各有什么优点和缺点呢?为什么不继续使用它们呢?

HttpClient 是Apache 基金会的一个开源网络库。它具有非常强大的功能和大量的API。然而,正是由于API 数量庞大,我们很难在不破坏兼容性的情况下对其进行升级和扩展,所以Android 团队对于HttpClient 的改进和优化态度并不积极。

HttpURLConnection 是一个多用途、极其轻量级的HTTP 客户端。提供的API比较简单,易于使用和扩展。然而,在Android 2.2版本之前,HttpURLConnection总是存在一些烦人的bug。例如,对于一个可读的InputStream调用close()方法时,可能会导致连接池失败。那么我们通常的解决方案就是直接禁用连接池功能:

私有无效disableConnectionReuseIfNecessary(){

//这是2.2版本之前的bug

if (Integer.parseInt(Build.VERSION.SDK) Build.VERSION_CODES.FROYO) {

System.setProperty("http.keepAlive", "false");

}

因此,一般建议使用2.2之前的HttpClient,因为它的bug较少。 2.2之后,推荐使用HttpURLConnection,因为API简单,体积小,并且有压缩和缓存机制,并且Android团队未来还会继续优化HttpURLConnection。

不过,上面两个类库相对于OkHttp来说就比较弱了,因为OkHttp不仅拥有高效的请求效率,而且还针对网络问题提供了很多开箱即用的解决方案。

支持HTTP/2。 HTTP/2 通过使用多路复用技术通过在连接上一次发送多个请求来发送或接收数据,从而支持单个TCP 连接上的并发。如果HTTP/2不可用,连接池复用技术也可以大大减少延迟。支持GZIP,可以压缩下载量。响应缓存可以直接避免重复请求并自动从许多常见的连接问题中恢复。如果你的服务器配置了多个IP地址,当第一个IP连接失败时,OkHttp会自动尝试下一个IP。 OkHttp 还处理代理服务器问题和SSL 握手失败。使用OkHttp 不需要在程序中重写网络代码。 OkHttp 实现了与java.net.HttpURLConnection 几乎相同的API。如果使用Apache HttpClient,OkHttp也提供了相应的okhttp-apache模块。

还有好消息。从Android 4.4开始,其内部实现HttpURLConnection已更改为OkHttp。您可以参考这两个网页:保真网和推特。

2. OkHttp类与http请求响应的映射

在讲解OkHttp的使用之前,我们先看一下我们的Http请求和响应的组成部分。

2.1 http请求

因此,一个类库要完成一次http请求,需要包括请求方法、请求地址、请求协议、请求头、请求体五个部分。这些都体现在okhttp3.Request 类中,该类代表了http 请求的类。参见下图:

其中,HttpUrl类代表请求地址,String method代表请求方法,Headers代表请求头,RequestBody代表请求体。 Object标签是用于取消http请求的标志。我们暂时忽略这一点。也许你在这里想知道,请求协议呢?为什么没有请求协议对应的类?我慢慢告诉你,下面我就来说说这个问题。

2.1.1 请求协议的协商升级

目前,Http/1.1 在世界范围内广泛使用。直接放弃跳到http/2肯定是不行的。现实。并非每个用户的浏览器都支持http/2,也不是每个服务器都打算支持http/2。如果我们直接发送http/2格式的协议,而服务器不支持的话,也不会失败。不可能维护一个全世界网站的列表,标明哪些支持http/2,哪些不支持?

为了解决这个问题,在更高的层面上,让新协议更容易部署,HTTP/1.1引入了Upgrade机制。该机制在RFC7230的“6.7升级”部分中有详细描述。

简单来说,我想先问你支持http/2吗?如果你支持的话我就用http/2来和你聊天。如果你不支持,那我还是用原来的http/1.1来和你聊天。

1. 客户端在请求头中指定Connection和Upgrade字段来发起HTTP/1.1协议升级。 HTTP/2 的协议名称是h2c,代表HTTP/2 ClearText。

获取/HTTP/1.1

Host: example.com

Connection: 升级,HTTP2 设置

升级: h2c

HTTP2-设置:2。如果服务器不同意升级或者不支持升级中列出的协议,则忽略它(将其视为HTTP/1.1请求并以HTTP/1.1响应)。

HTTP/1.1 200 好

内容长度: 243

内容类型: 文本/html

.如果服务器同意升级,它需要这样响应:

HTTP/1.1 101 切换协议

连接:升级

升级: h2c

[ HTTP/2 连接. ] HTTP Upgrade 响应的状态码为101,响应正文可以使用新协议定义的数据格式。

这样就可以完成从http/1.1到http/2的升级了。还可以从http/1.1升级到WebSocket。

这样你就明白为什么OkHttp没有指定具体的请求协议了。因为OkHttp采用的是请求协议的协商升级,无论是1.1还是2,首先只作为1.1发送,并且协议升级包含在发送的信息头中。场地。其次要看服务器是否支持协议升级。 OkHttp使用的协议升级字段是ALPN。如果您有兴趣,可以更深入地查看相关信息。

2.1.2 OkHttp请求

接下来我们构造一个http请求,并检查请求的具体内容。

最终请求request=new Request.Builder().url("https://github.com/").build();我们看看这个请求在内存中是什么样子的,是否是我们上面说的以及请求方法。请求地址、请求头、请求体一一对应。

2.2 http响应

让我们看看下一个http响应由哪些部分组成。我们先看一下响应构成图:

可以看出,一般由响应第一行、响应头、响应体组成。然而,响应的第一行表达了太多信息。 HTTP/1.1代表访问协议,200是响应代码,OK是描述状态的消息。根据单一责任,我们不应该用响应的第一行来代表这么多的内容。在这种情况下,我们的响应应该由访问协议、响应代码、描述信息、响应头和响应正文组成。

2.2.1 OkHttp响应

让我们看看OkHttp库如何表示它一个响应:

可以看到,在Response类中,Protocol代表请求协议,int code代表响应码,String message代表描述信息,Headers代表响应头,ResponseBody代表响应体。当然,另外,Request代表持有的请求,Handshake代表SSL/TLS握手协议验证时的信息。我们暂时不会询问这些附加信息。有了刚才提到的OkHttp响应的类组成,我们来看看OkHttp请求后内存中响应的内容。

最终请求request=new Request.Builder().url("https://github.com/").build();

响应response=client.newCall(request).execute();

可以看到,和我们的分析是非常吻合的。讲完OkHttp中的请求类和响应类,我们就可以直接讲OkHttp的使用了。

3 HTTP GET

3.1 同步GET

同步GET表示等待http请求,直到返回响应。在此期间,进程会被阻塞,所以get不能在Android主线程中执行,否则会报错。

私有最终OkHttpClient 客户端=new OkHttpClient();

公共无效运行()抛出异常{

请求request=new Request.Builder()

.url("http://publicobject.com/helloworld.txt")

。建造();

响应response=client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("意外代码" + response);

标头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());

}OkHttpClient实现了Call.Factory接口,它是Call的工厂类。 Call 负责发送执行请求并读取响应。

Request代表一个Http请求,通过Request.Builder辅助类构建。

client.newCall(request) 通过传入http请求返回Call调用。然后执行execute()方法同步获取

Response代表Http请求的响应。 response.body() 是ResponseBody 类,它表示响应正文。可以通过responseBody.string()获取字符串的表达形式,也可以通过responseBody.bytes()获取字节数组的表达形式。这两种形式都会将文档添加到内存中。它还可以通过responseBody.charStream() 和responseBody.byteStream() 返回流进行处理。

上面代码完成的功能是下载一个文件,打印其响应头,并以字符串形式打印响应体。

响应体的string() 方法对于小文档来说非常方便和高效。但是,如果响应正文太大(超过1MB),则应避免使用string() 方法,因为它会将整个文档加载到内存中。

对于超过1MB的响应体,应使用流式处理方式。这和我们处理xml文档的逻辑是一致的。小文件可以加载到内存中进行树解析,而大文件必须以流式方式解析。

3.2 异步GET

异步GET是指在另一个工作线程中执行http请求。当前线程在请求过程中不会被阻塞,因此可以在Android主线程中使用。

下面是工作线程中的文件下载,当响应可读时调用Callback接口。当响应头准备好后,会调用Callback接口,因此读取响应体可能会被阻塞。 OkHttp现阶段不提供异步API。接收响应正文。

私有最终OkHttpClient 客户端=new OkHttpClient();

公共无效运行()抛出异常{

请求request=new Request.Builder()

.url("http://publicobject.com/helloworld.txt")

。建造();

client.newCall(请求).enqueue(new Callback() {

@Override public void onFailure(Request request, Throwable throwable) {

throwable.printStackTrace();

}

@Override public void onResponse(Response response) 抛出IOException {

if (!response.isSuccessful()) throw new IOException("意外代码" + response);

标头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());

}

});

}

4 HTTP POST

4.1 Post方式提交String

下面使用HTTP POST 向服务提交请求。此示例向Web 服务提交一个Markdown 文档并以HTML 形式呈现该Markdown。由于整个请求正文都在内存中,因此请避免使用此API 提交大文件。文档(大于1MB)。

公共静态最终媒体类型MEDIA_TYPE_MARKDOWN

=MediaType.parse("text/x-markdown; charset=utf-8");

私有最终OkHttpClient 客户端=new OkHttpClient();

公共无效运行()抛出异常{

字符串postBody=""

+ "发布n"

+ "--------n"

+ "n"

+ " * _1.0_ 2013 年5 月6 日n"

+ " * _1.1_ 2013 年6 月15 日n"

+ " * _1.2_ 2013 年8 月11 日n";

请求request=new Request.Builder()

.url("https://api.github.com/markdown/raw")

.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))

。建造();

响应response=client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("意外代码" + response);

System.out.println(response.body().string());

}

4.2 Post方式提交流

通过POST 作为流提交请求正文。请求体的内容是通过流写入的方式生成的。这个例子是直接写入Okio的BufferedSink的流。你的程序可能会使用OutputStream,你可以使用BufferedSink.outputStream() 来获取。 OkHttp底层的对流和字节操作基于Okio库。 Okio库也是Square开发的另一个IO库,用于填补I/O和NIO的空缺。目的是提供一个简单易用的接口来操作IO。

公共静态最终媒体类型MEDIA_TYPE_MARKDOWN

=MediaType.parse("text/x-markdown; charset=utf-8");

私有最终OkHttpClient 客户端=new OkHttpClient();

公共无效运行()抛出异常{

RequestBody requestBody=new RequestBody() {

@Override public MediaType contentType() {

返回MEDIA_TYPE_MARKDOWN;

}

@Override public void writeTo(BufferedSink sink) 抛出IOException {

sink.writeUtf8("数字n");

ink.writeUtf8("--------n");

for (int i=2; i=997; i++) {

sink.writeUtf8(String.format(" * %s=%sn", i, 因子(i)));

}

}

私有字符串因子(int n){

for (int i=2; i n; i++) {

int x=n/i;

if (x * i==n) 返回因子(x) + " " + i;

}

返回Integer.toString(n);

}

};

请求request=new Request.Builder()

.url("https://api.github.com/markdown/raw")

.post(请求正文)

。建造();

响应response=client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("意外代码" + response);

System.out.println(response.body().string());

}

4.3 Post方式提交文件

使用文件作为请求正文非常简单。

公共静态最终媒体类型MEDIA_TYPE_MARKDOWN

=MediaType.parse("text/x-markdown; charset=utf-8");

私有最终OkHttpClient 客户端=new OkHttpClient();

公共无效运行()抛出异常{

文件file=new File("README.md");

请求request=new Request.Builder()

.url("https://api.github.com/markdown/raw")

.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, 文件))

。建造();

响应response=client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("意外代码" + response);

System.out.println(response.body().string());

}

4.4 Post方式提交表单

使用FormEncodingBuilder 构建请求正文,其效果与HTML 标签相同。键值对将使用HTML 兼容的URL 编码形式进行编码。

私有最终OkHttpClient 客户端=new OkHttpClient();

公共无效运行()抛出异常{

RequestBody formBody=new FormBody.Builder()

.add("搜索", "侏罗纪公园")

。建造();

请求request=new Request.Builder()

.url("https://en.wikipedia.org/w/index.php")

.post(formBody)

。建造();

响应response=client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("意外代码" + response);

System.out.println(response.body().string());

}

4.5 Post方式提交分块请求

MultipartBody.Builder 可以构建复杂的请求正文,与HTML 文件上传表单兼容。多块请求体中的每个请求都是一个请求体,并且可以定义自己的请求头。这些请求头可以用来描述这个Block请求,比如它的Content-Disposition。如果Content-Length和Content-Type可用,它们将自动添加到请求标头中。

私有静态最终字符串IMGUR_CLIENT_ID=".";

私有静态最终MediaType MEDIA_TYPE_PNG=MediaType.parse("image/png");

私有最终OkHttpClient 客户端=

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()); }

5. 其他用法

5.1 提取响应头

典型的HTTP头像是一个Map: 每个字段都有一个或没有值. 但是一些头允许多个值, 像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访问. 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") .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()) 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")); }

5.2 使用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.Entryentry : gist.files.entrySet()) { System.out.println(entry.getKey()); System.out.println(entry.getValue().content); } } static class Gist { Mapfiles; } static class GistFile { String content; }

5.3 响应缓存

为了缓存响应, 你需要一个你可以读写的缓存目录, 和缓存大小的限制. 这个缓存目录应该是私有的, 不信任的程序应不能读取缓存内容. 一个缓存目录同时拥有多个缓存访问是错误的. 大多数程序只需要调用一次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)); }如果需要阻值response使用缓存, 使用CacheControl.FORCE_NETWORK. 如果需要阻值response使用网络, 使用CacheControl.FORCE_CACHE. 警告: 如果你使用FORCE_CACHE, 但是response要求使用网络, OkHttp将会返回一个504 Unsatisfiable Request响应.

5.3.1 Force a Network Response

有些时候, 比如用户刚刚点击刷新按钮, 这时必须跳过缓存, 直接从服务器抓取数据. 为了强制全面刷新, 我们需要添加no-cache指令: connection.addRequestProperty("Cache-Control", "no-cache");这样就可以强制每次请求直接发送给源服务器, 而不经过本地缓存版本的校验, 常用于需要确认认证的应用和严格要求使用最新数据的应用.

5.3.2 Force a Cache Response

有时你会想立即显示资源. 这样即使在后台正下载着最新资源, 你的客户端仍然可以先显示原有资源, 毕竟有个东西显示比没有东西显示要好. 如果需要限制让请求优先使用本地缓存资源, 需要增加only-if-cached指令: try { connection.addRequestProperty("Cache-Control", "only-if-cached"); InputStream cached = connection.getInputStream(); // the resource was cached! show it catch (FileNotFoundException e) { // the resource was not cached } }

5.4 取消一个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); } }

5.5 超时

没有响应时使用超时结束call. 没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西. 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); }

5.6 每个call的配置

使用OkHttpClient, 所有的HTTP Client配置包括代理设置、超时设置、缓存设置. 当你需要为单个call改变配置的时候, 调用OkHttpClient.newBuilder(). 这个api将会返回一个builder, 这个builder和原始的client共享相同的连接池, 分发器和配置. 下面的例子中,我们让一个请求是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 { // Copy to customize OkHttp for this request. OkHttpClient copy = client.newBuilder() .readTimeout(500, TimeUnit.MILLISECONDS) .build(); Response response = copy.newCall(request).execute(); System.out.println("Response 1 succeeded: " + response); } catch (IOException e) { System.out.println("Response 1 failed: " + e); } try { // Copy to customize OkHttp for this request. OkHttpClient copy = client.newBuilder() .readTimeout(3000, TimeUnit.MILLISECONDS) .build(); Response response = copy.newCall(request).execute(); System.out.println("Response 2 succeeded: " + response); } catch (IOException e) { System.out.println("Response 2 failed: " + e); } }

5.7 处理验证

这部分和HTTP AUTH有关.

5.7.1 HTTP AUTH

使用HTTP AUTH需要在server端配置http auth信息, 其过程如下: 客户端发送http请求服务器发现配置了http auth, 于是检查request里面有没有"Authorization"的http header如果有, 则判断Authorization里面的内容是否在用户列表里面, Authorization header的典型数据为"Authorization: Basic jdhaHY0=", 其中Basic表示基础认证, jdhaHY0=是base64编码的"user:passwd"字符串. 如果没有,或者用户密码不对,则返回http code 401页面给客户端.标准的http浏览器在收到401页面之后, 应该弹出一个对话框让用户输入帐号密码; 并在用户点确认的时候再次发出请求, 这次请求里面将带上Authorization header.一次典型的访问场景是: 浏览器发送http请求(没有Authorization header)服务器端返回401页面浏览器弹出认证对话框用户输入帐号密码,并点确认浏览器再次发出http请求(带着Authorization header)服务器端认证通过,并返回页面浏览器显示页面

5.7.2 OkHttp认证

OkHttp会自动重试未验证的请求. 当响应是401 Not Authorized时,Authenticator会被要求提供证书. Authenticator的实现中需要建立一个新的包含证书的请求. 如果没有证书可用, 返回null来跳过尝试. 使用Response.challenges()来获得任何authentication challenges的 schemes 和 realms. 当完成一个Basic challenge, 使用Credentials.basic(username, password)来解码请求头. private final OkHttpClient client; public Authenticate() { client = new OkHttpClient.Builder() .authenticator(new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { 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(); } }) .build(); } public void run() throws Exception { 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()); }当认证无法工作时, 为了避免多次重试, 你可以返回空来放弃认证. 例如, 当exact credentials已经尝试过, 你可能会直接想跳过认证, 可以这样做: if (credential.equals(response.request().header("Authorization"))) { return null; // If we already failed with these credentials, don"t retry. }当重试次数超过定义的次数, 你若想跳过认证, 可以这样做: if (responseCount(response) >= 3) { return null; // If we"ve failed 3 times, give up. } private int responseCount(Response response) { int result = 1; while ((response = response.priorResponse()) != null) { result++; } return result;

用户评论

秘密

这篇文章刚好我需要!最近在学习安卓开发,数据请求一直卡着点

    有5位网友表示赞同!

将妓就计

OkHttp好像挺常用的吧?之前没用过,看看这篇教程能不能上手

    有18位网友表示赞同!

伤离别

喜欢看干货满满的教程,希望能学到真正实用的东西

    有15位网友表示赞同!

我绝版了i

我记得以前用volley框架搞网络请求,这个OkHttp怎么样比较呢?

    有13位网友表示赞同!

回忆未来

学习OkHttp感觉可以让我写更健壮的安卓应用

    有20位网友表示赞同!

浅嫣婉语

终于有人来总结OkHttp使用方法了!太棒了

    有19位网友表示赞同!

有恃无恐

之前看一些文档都太抽象了,希望这篇教程讲解清晰易懂

    有6位网友表示赞同!

颓废i

这篇文章能让我更快地完成我的项目吗?期待学习

    有12位网友表示赞同!

初阳

安卓开发必备技能啊!要认真学习一下

    有18位网友表示赞同!

暮染轻纱

看了很多资料还是有点懵,这篇教程希望能给我指明方向

    有11位网友表示赞同!

夏至离别

希望这个教程包含OkHttp的进阶使用方法,比如拦截器什么的

    有8位网友表示赞同!

あ浅浅の嘚僾

学习Android一直追求高效,希望能从OkHttp中学到优化数据请求的方法

    有15位网友表示赞同!

入骨相思

最近在想实现一些异步操作,OkHttp是不是一个不错的选择?

    有13位网友表示赞同!

盲从于你

看完教程后一定要试试自己动手搭建一下OkHttp网络请求

    有17位网友表示赞同!

你瞒我瞒

这篇教程能解决我之前遇到的网络通信请求问题吗?

    有6位网友表示赞同!

凉凉凉”凉但是人心

这个OkHttp框架听起来很厉害的样子!希望能学会用它

    有12位网友表示赞同!

素颜倾城

加油!希望这篇文章能让更多人学习Android开发

    有6位网友表示赞同!

一纸愁肠。

关注一下作者,期待作者发布更多高质量的教程

    有7位网友表示赞同!

终究会走-

分享给我的朋友,他们都在学安卓开发呢!

    有9位网友表示赞同!

【深入浅出OkHttp:全面掌握HTTP客户端使用指南】相关文章:

1.动物故事精选:寓教于乐的儿童故事宝库

2.《寓教于乐:精选动物故事助力儿童成长》

3.探索动物旅行的奇幻冒险:专为儿童打造的童话故事

4.《趣味动物刷牙小故事》

5.探索坚韧之旅:小蜗牛的勇敢冒险

6.传统风味烤小猪,美食探索之旅

7.探索奇幻故事:大熊的精彩篇章

8.狮子与猫咪的奇妙邂逅:一场跨界的友谊故事

9.揭秘情感的力量:如何影响我们的生活与决策

10.跨越两岸:探索彼此的独特世界

上一篇:深度解析:文本特征与应用领域 下一篇:迎接双胞胎喜悦:你的家庭新篇章