网络请求框架-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. 缓存

缓存类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 - 完()

你可能感兴趣的:(网络请求框架-OkHttp使用方法总结)