自从Android4.4的源码中可以看到HttpURLConnection
已经替换成OkHttp
开始( JakeWharton曾在Twitter表示 ) ,OkHttp
+Retrofit
+RxJava
的组合网络请求一直经久不衰,主流app的网络架构基本都是这样的组合模式,存在即合理,说明OkHttp
+Retrofit
+RxJava
的方式确实给开发,用户体验等带来可观的优势,那么这个系列文章围绕Android的网络展开.
OkHttp:An HTTP & HTTP/2 client for Android and Java applications
Android 历史网络库
HttpClient
是 Apache 提供的HTTP网络访问接口,从一开始的时候就被引入到了Android的API中;
HttpURLConnection
是一种多用途, 轻量极的HTTP客户端, 提供的API比较简单, 可以容易地去使用和扩展.
OkHttp优势
支持HTTP/2, HTTP/2通过使用多路复用技术在一个单独的TCP连接上支持并发, 通过在一个连接上一次性发送多个请求来发送或接收数据
如果HTTP/2不可用, 连接池复用技术也可以极大减少延时
支持GZIP, 可以压缩下载体积
响应缓存可以直接避免重复请求
会从很多常用的连接问题中自动恢复
如果您的服务器配置了多个IP地址, 当第一个IP连接失败的时候, OkHttp会自动尝试下一个IP
OkHttp还处理了代理服务器问题和SSL握手失败问题,等等…
基本使用
该系列版本说明
OkHttp版本统一:3.10.0
JDK:1.8+
Gradle包导入
1 2 3 4 implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
关于网络请求
基本网络请求由请求(请求行
,请求头
,请求内容
),响应(响应行
,响应头
,响应内容
)两大部分组成,具体的内容请查看Http VS Https 这篇文章
OkHttp请求
已在Http VS Https 文章中介绍了,HTTP请求相关内容
OkHttp响应
已在Http VS Https 文章中介绍了,HTTP响应相关内容
同步与异步
网络请求执行方式为:同步与异步;同步
和异步
关注的是消息通信机制 (synchronous communication/ asynchronous communication)
同步
就是在发出一个 调用 时,在没有得到结果之前,该 调用 就不返回,但是一旦调用返回,就得到返回值了。
换句话说,就是由 调用者 主动等待这个 调用 的结果。
Okhttp同步(execute()
):Invokes the request immediately, and blocks until the response can be processed or is in error.
1 2 3 4 5 6 7 8 9 10 11 12 13 String url = "https://api.github.com/users/BladeCode" ;OkHttpClient client = new OkHttpClient (); String run (String url) throws IOException { Request request = new Request .Builder().url(url).build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { return response.body().string(); } else { throw new IOException ("Unexpected code " + response); } }
异步
异步 则与同步相反,调用 在发出之后,这个调用就直接返回了,所以没有返回结果。
换句话说,当一个异步过程调用发出后,调用者 不会立刻得到结果。而是在 调用 发出后,被调用者 通过状态、通知来通知 调用者 ,或通过回调函数处理这个调用。
Okhttp同步(enqueue(Callback responseCallback)
):Schedules the request to be executed at some point in the future.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 String url = "https://api.github.com/users/BladeCode" ;OkHttpClient client = new OkHttpClient ();Request request = new Request .Builder().url(url).build();Response response = client.newCall(request).enqueue(new Callback () { @Override public void onFailure (Call call, IOException e) { System.out.println(e.toString()); } @Override public void onResponse (Call call, Response response) throws IOException { System.out.println(response.body().string()); System.out.println(response.body().charStream()); System.out.println(response.body().byteStream()); } });
注意:
响应体太大(超过1MB), 应避免使用 string()方法, 因为它会将把整个文档加载到内存中.
对于超过1MB的响应body, 应使用流的方式来处理响应body. 这和我们处理xml文档的逻辑是一致的, 小文件可以载入内存树状解析, 大文件就必须流式解析
OkHttp Get
1 2 3 4 5 6 7 8 9 10 11 12 13 String url = "https://api.github.com/users/BladeCode" ;OkHttpClient client = new OkHttpClient (); String run (String url) throws IOException { Request request = new Request .Builder().url(url).build(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { return response.body().string(); } else { throw new IOException ("Unexpected code " + response); } }
OkHttp Post
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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(); if (response.isSuccessful()) { return response.body().string(); } else { throw new IOException ("Unexpected code " + response); } }
Posting a String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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(); try (Response response = client.newCall(request).execute()){ if (!response.isSuccessful()) throw new IOException ("Unexpected code " + response); System.out.println(response.body().string()); } }
注意:当提交数据大于1MB,请使用流的方式
Post Streaming
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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(); try (Response response = client.newCall(request).execute()){ if (!response.isSuccessful()) throw new IOException ("Unexpected code " + response); System.out.println(response.body().string()); } }
Posting a File
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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/BladeCode/raw" ) .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)) .build(); try (Response response = client.newCall(request).execute()){ if (!response.isSuccessful()) throw new IOException ("Unexpected code " + response); System.out.println(response.body().string()); } }
Posting form parameters
使用FormEncodingBuilder
来构建和HTML