OkHttp的基本使用

1、首先在工程的app模块下添加okhttp的依赖

 implementation 'com.squareup.okhttp3:okhttp:3.12.0'

同步GET请求

同步GET的意思是一直等待http请求, 直到返回了响应. 在这之间会阻塞进程, 所以通过get不能在Android的主线程中执行, 否则会报错.

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

OkHttpClient实现了Call.Factory接口, 是Call的工厂类, Call负责发送执行请求和读取响应.
Request代表Http请求, 通过Request.Builder辅助类来构建.client.newCall(request)通过传入一个http request, 返回一个Call调用. 然后执行execute()方法, 同步获得
Response代表Http请求的响应. response.body()是ResponseBody类, 代表响应体, 可以通过responseBody.string()获得字符串的表达形式, 或responseBody.bytes()获得字节数组的表达形式, 这两种形式都会把文档加入到内存. 也可以通过responseBody.charStream()和responseBody.byteStream()返回流来处理.

上述代码完成的功能是下载一个文件, 打印他的响应头, 以string形式打印响应体.
响应体的string()方法对于小文档来说十分方便高效. 但是如果响应体太大(超过1MB), 应避免使用 string()方法, 因为它会将把整个文档加载到内存中.
对于超过1MB的响应body, 应使用流的方式来处理响应body. 这和我们处理xml文档的逻辑是一致的, 小文件可以载入内存树状解析, 大文件就必须流式解析.

异步GET

异步GET是指在另外的工作线程中执行http请求, 请求时不会阻塞当前的线程, 所以可以在Android主线程中使用.
下面是在一个工作线程中下载文件, 当响应可读时回调Callback接口. 当响应头准备好后, 就会调用Callback接口, 所以读取响应体时可能会阻塞. OkHttp现阶段不提供异步api来接收响应体。

      //1.拿到okHttpClient对象,可以设置连接超时等OkHttpClient okHttpClient=new OkHttpClient();//2.构造Request请求对象,可以增加头addHeader等Request.Builder builder = new Request.Builder();//url()中可以放入网址Request request = builder.get().url("http://publicobject.com/helloworld.txt").build();//3.将Request封装为CallCall call = okHttpClient.newCall(request);//4.执行call//方法一Response response=call.execute();//汇抛出IO异常,同步方法//方法二,异步方法,放到队列中,处于子线程中,无法更新UIcall.enqueue(new Callback() {//请求时失败时调用@Overridepublic void onFailure(Call call, IOException e) {}//请求成功时调用@Overridepublic void onResponse(Call call, Response response) throws IOException {//处于子线程中,能够进行大文件下载,但是无法更新UIfinal String res = response.body().string();//请求成功时返回的东西//InputStream is=response.body().byteStream();// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存/*** runOnUiThread方法切换到主线程中,或者用handler机制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交String

下面是使用HTTP POST提交请求到服务. 这个例子提交了一个json字符串到web服务. 因为整个请求体都在内存中, 因此避免使用此api提交大文档(大于1MB)

OkHttpClient okHttpClient=new OkHttpClient();RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "{username:hyman,password:123}");Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "postString").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.执行call//方法一Response response=call.execute();//汇抛出IO异常,同步方法//方法二,异步方法,放到队列中,处于子线程中,无法更新UIcall.enqueue(new Callback() {//请求时失败时调用@Overridepublic void onFailure(Call call, IOException e) {}//请求成功时调用@Overridepublic void onResponse(Call call, Response response) throws IOException {//处于子线程中,能够进行大文件下载,但是无法更新UIfinal String res = response.body().string();//请求成功时返回的东西//InputStream is=response.body().byteStream();// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存/*** runOnUiThread方法切换到主线程中,或者用handler机制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交流

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

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提交文件

File file = new File(Environment.getExternalStorageDirectory(), "banner.png");if (!file.exists()) {return;}//1.拿到okHttpClient对象OkHttpClient okHttpClient = new OkHttpClient();//mime typeRequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "postFile").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.执行call//方法一Response response=call.execute();//汇抛出IO异常,同步方法//方法二,异步方法,放到队列中,处于子线程中,无法更新UIcall.enqueue(new Callback() {//请求时失败时调用@Overridepublic void onFailure(Call call, IOException e) {Log.d("报错了吗", "onFailure: 报了");e.printStackTrace();}//请求成功时调用@Overridepublic void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {//处于子线程中,能够进行大文件下载,但是无法更新UILog.d("执行成功", "onResponse: 成功");final String res = response.body().string();//请求成功时返回的东西//InputStream is=response.body().byteStream();// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存/*** runOnUiThread方法切换到主线程中,或者用handler机制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交表单

使用FormEncodingBuilder来构建和HTML标签相同效果的请求体. 键值对将使用一种HTML兼容形式的URL编码来进行编码.

        //1.拿到okHttpClient对象OkHttpClient okHttpClient = new OkHttpClient();//2.构造Request请求对象//构造requestBody,get请求不需要FormBody.Builder requestBodyBuilder = new FormBody.Builder();//提交到服务器端的请求体RequestBody requestBody = requestBodyBuilder.add("username", "AI").add("password", "12345678").build();Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "login").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.执行call//方法一Response response=call.execute();//汇抛出IO异常,同步方法//方法二,异步方法,放到队列中,处于子线程中,无法更新UIcall.enqueue(new Callback() {//请求时失败时调用@Overridepublic void onFailure(Call call, IOException e) {}//请求成功时调用@Overridepublic void onResponse(Call call, Response response) throws IOException {//处于子线程中,能够进行大文件下载,但是无法更新UIfinal String res = response.body().string();//请求成功时返回的东西//InputStream is=response.body().byteStream();// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存/*** runOnUiThread方法切换到主线程中,或者用handler机制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

Post提交分块请求

MultipartBody.Builder可以构建复杂的请求体, 与HTML文件上传形式兼容. 多块请求体中每块请求都是一个请求体, 可以定义自己的请求头. 这些请求头可以用来描述这块请求, 例如它的Content-Disposition. 如果Content-Length和Content-Type可用的话, 他们会被自动添加到请求头中.

File file = new File(Environment.getExternalStorageDirectory(), "uz_splash_bg.png");if (!file.exists()) {return;}OkHttpClient okHttpClient=new OkHttpClient();//mime typeMultipartBody.Builder multipartBuilder = new MultipartBody.Builder();RequestBody requestBody = multipartBuilder.setType(MultipartBody.FORM).addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "username" + "\""),RequestBody.create(null, "hyman")).addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "password" + "\""),RequestBody.create(null, "123")).addFormDataPart("mPhoto", "hyman.png", RequestBody.create(MediaType.parse("application/octet-stream"), file)).build();//mPhoto是File的一个域,value//显示上传进度,把post的中的requestBody改为countingRequestBody就行了/**CountingRequestBody countingRequestBody=new CountingRequestBody(requestBody, new CountingRequestBody.Listener() {@Override public void onRequestProgress(long byteWritten, long contentLength) {L.e(byteWritten+"/"+contentLength);}});*/Request.Builder builder = new Request.Builder();Request request = builder.url(mBaseUrl + "uploadInfo").post(requestBody).build();Call call = okHttpClient.newCall(request);//4.执行call//方法一Response response=call.execute();//汇抛出IO异常,同步方法//方法二,异步方法,放到队列中,处于子线程中,无法更新UIcall.enqueue(new Callback() {//请求时失败时调用@Overridepublic void onFailure(Call call, IOException e) {}//请求成功时调用@Overridepublic void onResponse(Call call, Response response) throws IOException {//处于子线程中,能够进行大文件下载,但是无法更新UIfinal String res = response.body().string();//请求成功时返回的东西//InputStream is=response.body().byteStream();// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存/*** runOnUiThread方法切换到主线程中,或者用handler机制也可以*/runOnUiThread(new Runnable() {@Overridepublic void run() {// 更新ui}});}});

设置响应头和提取响应头

典型的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访问.

private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {// 请求头的设置// header()会覆盖之前已设置的同名请求头,而addHeader()不会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();try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);// response.header(headerName)获取的是响应头对应字段的最新值System.out.println("Server: " + response.header("Server"));System.out.println("Date: " + response.header("Date"));// 获取响应头集合System.out.println("Vary: " + response.headers("Vary"));}}

使用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 MiBCache 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响应.

Force a Network Response

有些时候, 比如用户刚刚点击刷新按钮, 这时必须跳过缓存, 直接从服务器抓取数据. 为了强制全面刷新, 我们需要添加no-cache指令:

connection.addRequestProperty("Cache-Control", "no-cache");

这样就可以强制每次请求直接发送给源服务器, 而不经过本地缓存版本的校验, 常用于需要确认认证的应用和严格要求使用最新数据的应用.

Force a Cache Response

有时你会想立即显示资源. 这样即使在后台正下载着最新资源, 你的客户端仍然可以先显示原有资源, 毕竟有个东西显示比没有东西显示要好.
如果需要限制让请求优先使用本地缓存资源, 需要增加only-if-cached指令:

try {connection.addRequestProperty("Cache-Control", "only-if-cached");InputStream cached = connection.getInputStream();// the resource was cached! show itcatch (FileNotFoundException e) {// the resource was not cached}
}

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

每个call的配置

使用OkHttpClient, 所有的HTTP Client配置包括代理设置、超时设置、缓存设置. 当你需要为单个call改变配置的时候, 调用OkHttpClient.newBuilder(). 这个api将会返回一个builder, 这个builder和原始的client共享相同的连接池, 分发器和配置.
下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。
每一个Call(其实现是RealCall)只能执行一次,否则会报异常,具体参见 RealCall#execute()

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

处理验证

这部分和HTTP AUTH有关.

HTTP AUTH

使用HTTP AUTH需要在server端配置http auth信息, 其过程如下:

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

一次典型的访问场景是:

  1. 浏览器发送http请求(没有Authorization header)
  2. 服务器端返回401页面
  3. 浏览器弹出认证对话框
  4. 用户输入帐号密码,并点确认
  5. 浏览器再次发出http请求(带着Authorization header)
  6. 服务器端认证通过,并返回页面
  7. 浏览器显示页面

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

拦截器-interceptor

OkHttp的拦截器链可谓是其整个框架的精髓,用户可传入的 interceptor 分为两类:

  • ①一类是全局的 interceptor,该类 interceptor 在整个拦截器链中最早被调用,通过OkHttpClient.Builder#addInterceptor(Interceptor) 传入;
  • ②另外一类是非网页请求的 interceptor,这类拦截器只会在非网页请求中被调用,并且是在组装完请求之后,真正发起网络请求前被调用,所有的 interceptor 被保存在List interceptors 集合中,按照添加顺序来逐个调用,具体可参考RealCall#getResponseWithInterceptorChain() 方法。通过
    OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 传入;

这里举一个简单的例子,例如有这样一个需求,我要监控App通过 OkHttp 发出的所有原始请求,以及整个请求所耗费的时间,针对这样的需求就可以使用第一类全局的 interceptor 在拦截器链头去做。

OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(new LoggingInterceptor()).build();
Request request = new Request.Builder().url("http://www.publicobject.com/helloworld.txt").header("User-Agent", "OkHttp Example").build();
okHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure: " + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {ResponseBody body = response.body();if (body != null) {Log.d(TAG, "onResponse: " + response.body().string());body.close();}}
});
public class LoggingInterceptor implements Interceptor {private static final String TAG = "LoggingInterceptor";@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();long startTime = System.nanoTime();Log.d(TAG, String.format("Sending request %s on %s%n%s",request.url(), chain.connection(), request.headers()));Response response =  chain.proceed(request);long endTime = System.nanoTime();Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",response.request().url(), (endTime - startTime) / 1e6d, response.headers()));return response;}
}

针对这个请求,打印出来的结果

Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp ExampleReceived response for https://publicobject.com/helloworld.txt in 1265.9ms
Server: nginx/1.10.0 (Ubuntu)
Date: Wed, 28 Mar 2018 08:19:48 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes

注意到一点是这个请求做了重定向,原始的 request url 是 http://www.publicobject.com/helloworld.tx,而响应的 request url 是 https://publicobject.com/helloworld.txt,这说明一定发生了重定向,但是做了几次重定向其实我们这里是不知道的,要知道这些的话,可以使用 addNetworkInterceptor()去做。更多的关于 interceptor的使用以及它们各自的优缺点,请移步OkHttp Interceptors 拦截器

自定义dns服务

Okhttp默认情况下使用的是系统

其他

推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。当然,也可以使用如下的方式来创建一个新的 OkHttpClient 实例,它们共享连接池、线程池和配置信息。

   OkHttpClient eagerClient = client.newBuilder().readTimeout(500, TimeUnit.MILLISECONDS).build();Response response = eagerClient.newCall(request).execute();

资源释放

OkHttp中被挂起的线程和连接都将会在保持空闲时自动回收。如果我们想要主动释放资源,可以使用如下方式,之后所有的Call对象执行请求都将被拒绝。

 client.dispatcher().executorService().shutdown();

清理连接池可以使用如下方式(连接池的守护线程可能不会立即退出):

 client.connectionPool().evictAll();

如果希望关闭缓存,可以使用如下方式:

client.cache().close();

补充一点

MediaType媒体类型:决定浏览器将以什么形式、什么编码对资源进行解析

Content-Type:也属于MediaType媒体类型,主要用于在请求头中指定资源的MediaType
一、MediaType类型
类型 描述
text/html HTML格式
text/plain 纯文本格式,空格转换为 “+” 加号,但不对特殊字符编码
text/xml XML格式
text/x-markdown Markdown格式
image/gif gif图片格式
image/jpeg jpg图片格式
image/png png图片格式
application/xhtml+xml XHTML格式
application/xml XML数据格式
application/json 用来告诉服务端,消息主体是序列化后的JSON字符串
application/pdf pdf格式
application/msword Word文档格式
application/octet-stream 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded 参数为键值对形式,在发送前编码所有字符(默认)。浏览器的原生 <form encType=”” 表单提交类型,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据
multipart/form-data 不对字符编码,发送大量二进制数据或包含non-ASCII字符的文本,application/x-www-form-urlencoded是效率低下的(需要用更多字符表示一个non-ASCII字符)。需要设定“ <form enctype=‘multipart/form-data’”

二、MediaType对象解析

MediaType对象包含了三种信息:type 、subtype、charset,一般将这些信息传入parse()方法中,这样就可以解析出MediaType对象

例子1:

text/x-markdown; charset=utf-8

type值是text,表示是文本这一大类;
/ 后面的x-markdown是subtype,表示是文本这一大类下的markdown这一小类;
charset=utf-8 则表示采用UTF-8编码

参考文章
Android OkHttp完全解析 是时候来了解OkHttp了
OkHttp使用教程
okhttp3使用总结
Okhttp3基本使用
OkHttp使用完全教程

OkHttp用法详解相关推荐

  1. Fresco用法详解

    版权声明:本文为延成原创文章,转载请标明出处 Fresco用法详解 经过在实际项目中多次的使用,本人在这做了一下简单总结,希望对初次使用和正在使用的你们有所帮助. 官方地址 官方github地址:ht ...

  2. python argv 详解_Python3 sys.argv[ ]用法详解

    sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个"外部"很关键,因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可 ...

  3. oracle中的exists 和 not exists 用法详解

    from:http://blog.sina.com.cn/s/blog_601d1ce30100cyrb.html oracle中的exists 和 not exists 用法详解 (2009-05- ...

  4. ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)

    ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多) https://blog.csdn.net/qq_25221835/article/details/82762416 post ...

  5. python的继承用法_【后端开发】python中继承有什么用法?python继承的用法详解

    本篇文章给大家带来的内容是关于python中继承有什么用法?python继承的用法详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 面向对象三大特征 1.封装:根据职责将属性和方法 ...

  6. C++中substr()函数用法详解

    C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...

  7. php theme_path,PHP_Yii2主题(Theme)用法详解,本文实例讲述了Yii2主题(Theme) - phpStudy

    Yii2主题(Theme)用法详解 本文实例讲述了Yii2主题(Theme)用法.分享给大家供大家参考,具体如下: 首先看看主要的配置方式: 'components' => [ 'view' = ...

  8. LayoutInflater的inflate函数用法详解

    LayoutInflater的inflate函数用法详解 LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 获取LayoutInflater的方法有如下三种: ...

  9. Ext.Net学习笔记22:Ext.Net Tree 用法详解

    上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat="server"><Root> ...

最新文章

  1. python图像配准的原理_python利用sift和surf进行图像配准
  2. 《ArcGIS Runtime SDK for Android开发笔记》——(4)、基于Android Studio构建ArcGIS Android开发环境...
  3. Servlet的Filter的使用
  4. python语言介绍-Python语言的简介
  5. 2021第一波新年(春节)中国风插画设计,为年底储备素材
  6. linux监控进程资源,linux系统资源监控命令
  7. jupyterlab nb_conda 增加 删除_Jupyter lab
  8. java砖头铺路面试题,Java基础知识面试题
  9. 解决libcrypto.so.1.0.0 动态库找不到的问题
  10. Unity资源加载简析(二)AssetsBundle
  11. 优启通安装linux系统,利用U盘启动盘优启通(pe)安装centos到旧笔记本上
  12. javaweb调用qq认证登录接口
  13. Html5中鼠标经过图片,图片在盒子内部实现放大效果。
  14. linux下木马程序病原体的制作和运行
  15. R学习之统计实验(四)--蒲丰投针(R语言编程)-----数模
  16. 数据库系统教程(第二版何玉洁)课后数据库上机实验答案
  17. ecshop常用修改
  18. 【Vant Weapp】van-tab 标签页
  19. hp 笔记本 linux 系统 bios 功能不能用,Ubuntu 17.10 Respin ISO发布下载,解决某些笔记本电脑BIOS问题...
  20. IC ,CC系列,单片机芯片MSP430F

热门文章

  1. 实体店看过来!无人零售来了,如何破局看这里!
  2. 综合评价与决策方法一:优劣解距离法(TOPSIS法)
  3. 实践教程|GPU 利用率低常见原因分析及优化
  4. 天文相关Python资料收集
  5. 飞腾FT-2000/4处理器+复旦微FPGA+国产操作系统解决方案
  6. 关于Navicat Premium输入拼音会闪退的解决办法
  7. 记录一次elasticsearch挂掉之后无法启动 kibana Status: Red -分析过程
  8. Flutter开发(二十二):Flutter本地数据存储
  9. 微信数据库最新的解密方式,C++代码解密微信数据库信息
  10. shell 知:bash