OkHttp3 详解及开发实例文章浏览阅读1.2k次,点赞2次,收藏4次。OkHttp3 详解https://doker.blog.csdn.net/article/details/128219193
二、方法
Posting a multipart request
MultipartBody.Builder 可以构建与HTML文件上传表单兼容的复杂请求主体。多部分请求正文的每个部分本身都是请求正文,并且可以定义自己的标头。如果存在,这些标头应该描述部件主体,例如其内容处置。内容长度和内容类型标头将自动添加(如果可用)。
/*** The imgur client ID for OkHttp recipes. If you're using imgur for anything other than running* these examples, please request your own client ID! https://api.imgur.com/oauth2*/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/imageRequestBody 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();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}
Parse a JSON Response With Moshi
Moshi是一个方便的API,用于在JSON和Java对象之间进行转换。这里,我们使用它来解码来自GitHub API的JSON响应。
请注意,ResponseBody.charStream()使用Content-Type响应标头来选择解码响应正文时要使用的字符集。如果未指定字符集,则默认为UTF-8。
private final OkHttpClient client = new OkHttpClient();private final Moshi moshi = new Moshi.Builder().build();private final JsonAdapter<Gist> gistJsonAdapter = moshi.adapter(Gist.class);public void run() throws Exception {Request request = new Request.Builder().url("https://api.github.com/gists/c2a7c39532239ff261be").build();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);Gist gist = gistJsonAdapter.fromJson(response.body().source());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;}
Response Caching
要缓存响应,您需要一个可以读取和写入的缓存目录,以及对缓存大小的限制。缓存目录应该是私有的,不受信任的应用程序应该无法读取其内容!
让多个缓存同时访问同一缓存目录是错误的。大多数应用程序应该只调用一次新的OkHttpClient(),用它们的缓存配置它,并在任何地方使用相同的实例。否则,两个缓存实例将相互踩踏,损坏响应缓存,并可能使程序崩溃。
响应缓存对所有配置使用HTTP标头。您可以添加请求标头,如Cache-Control:max-state=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 MiBCache 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();String response1Body;try (Response response1 = client.newCall(request).execute()) {if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);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());}String response2Body;try (Response response2 = client.newCall(request).execute()) {if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);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,并且响应需要网络,OkHttp将返回504 Unsatisfailable Request响应。
Canceling a Call
使用Call.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);System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);try (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);}}
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();try (Response response = client.newCall(request).execute()) {System.out.println("Response completed: " + response);}}
Per-call Configuration
所有HTTP客户端配置都存在于OkHttpClient中,包括代理设置、超时和缓存。当您需要更改单个调用的配置时,请调用OkHttpClient.newBuilder()。这将返回一个与原始客户端共享相同连接池、分派器和配置的构建器。在下面的示例中,我们发出一个超时500毫秒的请求,另一个超时3000毫秒。
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();// Copy to customize OkHttp for this request.OkHttpClient client1 = client.newBuilder().readTimeout(500, TimeUnit.MILLISECONDS).build();try (Response response = client1.newCall(request).execute()) {System.out.println("Response 1 succeeded: " + response);} catch (IOException e) {System.out.println("Response 1 failed: " + e);}// Copy to customize OkHttp for this request.OkHttpClient client2 = client.newBuilder().readTimeout(3000, TimeUnit.MILLISECONDS).build();try (Response response = client2.newCall(request).execute()) {System.out.println("Response 2 succeeded: " + response);} catch (IOException e) {System.out.println("Response 2 failed: " + e);}}
Handling authentication
OkHttp可以自动重试未经身份验证的请求。当响应为401未授权时,将要求验证者提供凭据。实现应构建包含缺少的凭据的新请求。如果没有可用的凭据,则返回null以跳过重试。
使用Response.challenges()获取任何身份验证挑战的方案和领域。在完成基本质询时,使用Credentials.Basic(用户名、密码)对请求头进行编码。
private final OkHttpClient client;public Authenticate() {client = new OkHttpClient.Builder().authenticator(new Authenticator() {@Override public Request authenticate(Route route, Response response) throws IOException {if (response.request().header("Authorization") != null) {return null; // Give up, we've already attempted to authenticate.}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();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}}
为了避免在身份验证不工作时进行多次重试,可以返回null放弃。例如,当已经尝试了这些确切的凭据时,您可能希望跳过重试:
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.}
上述代码依赖于此responseCount()方法:
private int responseCount(Response response) {int result = 1;while ((response = response.priorResponse()) != null) {result++;}return result;}