网络请求框架-OkHttp使用方法总结

OkHttp

2019-2-18 17:45 - QG2017移动组 - 张艺隽
OkHttp是一个HTTP+HTTP/2的客户端。
  • 来自官方的说明:
An HTTP+HTTP/2 client for Android and Java applications.
目前OkHttp在github上star的数量高达30k+,仅次于Retrofit,很多大型公司及APP都开始或已经对OkHttp进行封装。Retrofit2.0开始内置OkHttp框架。
学习和使用OkHttp的原因:
  1. 自动为你的网络请求添加缺失的header(例如 Content-Length, Transfer-Encoding, User-Agent等);
  2. 对重定向(302)的连接进行跟进访问;
  3. 使用连接池减少延迟、增加网络吞吐量;
  4. 使用TSL协议为网络访问保障安全及数据完整性;
0. 参考资料 官方wiki
Okhttp3基本使用
1. 开始使用 添加依赖
implementation("com.squareup.okhttp3:okhttp:3.13.1")

2. GET 1. 同步 使用OkHttpClient.newCall(Requset).execute()来同步获取Response
private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("https://publicobject.com/helloworld.txt") .build(); try (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()); } }

2. 异步 使用OkHttpClient.newCall(Requset).enqueue(Callback)来异步获取Response
client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); }@Override public void onResponse(Call call, Response response) throws IOException { try (ResponseBody responseBody = response.body()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); Headers responseHeaders = response.headers(); for (int i = 0, size = responseHeaders.size(); i < size; i++) { System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i)); }System.out.println(responseBody.string()); } } });

3. POST 1. posting a String
MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8"); String postBody = "content"; Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody)) .build();

2. posting Stream
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"); } }; Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(requestBody) .build(); }

3. posting a File
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();

4. posting form parameters
RequestBody formBody = new FormBody.Builder() .add("search", "Jurassic Park") .build(); Request request = new Request.Builder() .url("https://en.wikipedia.org/w/index.php") .post(formBody) .build();

5. posting a multipart request
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 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()); } }

常用的MediaType:
  • json : application/json
  • xml : application/xml
  • png : image/png
  • jpg : image/jpeg
  • gif : imge/gif
4. Header
  • 对于Request:
  1. 使用header()会将上一次header()设置的value取代
  2. 对于想叠加的value,使用addHeader()来实现
  • 对于Response:
  1. 使用header(key)来获取对应的value,如果一个key对应一个value,则返回最后一个value;如果没有对应的value,则返回null
  2. 对于一个key对应多个value的情况,使用headers(key)获得value的列表(List)
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(); try (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. 缓存 【网络请求框架-OkHttp使用方法总结】缓存类java文档:Javadoc-Class Cache
wiki-Response Caching
public CacheResponse(File cacheDirectory) throws Exception { int cacheSize = 10 * 1024 * 1024; // 10 MiB Cache cache = new Cache(cacheDirectory, cacheSize); client = new OkHttpClient.Builder() .cache(cache) .build(); }

  • OkHttpClient应使用单例模式,如果多个client同时对缓存目录进行访问将会抛出异常,甚至导致程序崩溃。
6. 超时 设置连接以及读写的超时:
public ConfigureTimeouts() throws Exception { client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); }

7. 修改单次配置 有时候需要在网络访问中修改client的某些属性,这时候可以用到OkHttpClient.newBuilder() 来产生新的OkHttpClient实例。新的实例和原来的client配置一致,并且共享连接池。
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); } }

8. 授权证书 测试地址
wiki-Handling authentication
使用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 { 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(); }

9. 拦截器 OkHttp 拦截器的一些骚操作
链接介绍了一些不错的小技巧,例如解决Retrofit更改path比较麻烦的缺点、Header的统一添加、日志抓取等。
结语:OkHttp真的是十分强大的开源框架,配合Retrofit更是锦上添花。尽管如此,目前仍需要复习基础,熟记Android原生网络请求写法。
#OkHttp - 完()

    推荐阅读