赞
踩
对整个OkHttp框架的介绍,会分为使用篇和源码分析篇两个部分进行介绍:
这里是使用篇的目录:
(一)-基本使用
(二)-常用类介绍
(三)-Interceptor
OkHttp3(后续简称为OkHttp)是一个处理网络请求的开源库,由Square公司贡献。由于其高效的特性,所以非常流行。
为什么其能被广泛的使用,并且有替代HttpUrlConnetion之势呢,这就不得不说其具有的几个优点:
通过OkHttp进行网络请求的时候,一般会通过如下流程

这里列出流程,只是让大家心里大致有个印象就成。后面的文章会详细分析执行过程。
我们可以看到OkHttp的请求信息(如url,method,header等)都是封装在Request中的,而且也是通过Builder模式进行构建。
在我们发起请求后,通过一系列的Interceptor操作,最终会返回Response里面就封装了返回信息,里面包含了code, message,header,body等信息。
到这里,我们队OkHttp有了一个大致的认识后,就进入主题,怎么样去使用吧。
compile 'com.squareup.okhttp3:okhttp:3.10.0'
OkHttpClient是一个生产Call的工厂,通过Call发起Http请求,并获取返回信息。
而且,绝大多数情况使用一个全局的单例OkHttpClient实例,便可以满足整个应用的Http请求。
这里创建OkHttpClient实例有三种方法:
OkHttpClient client = new OkHttpClient();
new OkHttpClient.Builder()
.addInterceptor(new TokenInterceptor())
.addInterceptor(new CacheHeaderInterceptor())
.addInterceptor(new loggingInterceptor)
.cache(cache)
.connectTimeout(DEFAULT_TIMEOUT.toLong(), TimeUnit.SECONDS)
.build()
这里通过Builder添加了一些自定义的Interceptor、超时时间配置、缓存配置等。具体用法后续会有详细讲解。
OkHttpClient client = createClient(); // 模拟之前创建好的client
……
// 由于某个接口需要重新设置超时时间(其他配置不变)
client.newBuilder()
.connectTimeout(DEFAULT_TIMEOUT.toLong(), TimeUnit.SECONDS)
上述例子中,假设之前通过createClient()方法已经创建了OkHttpClient实例,但是某个接口需要单独设置超时时间,但是其他配置不变,这个时候,我们不需要重新再去配置以便,只需要通过client.newBuilder()方法就能获取Builder对象,并且复用之前的配置,然后再重新配置需要单独处理的配置项即可。
HttpClient创建好之后,就可以发起请求了,后续的例子中很多都是用的官方示例,所以推荐大家还是多看看官方文档
public class GetExample { OkHttpClient client = new OkHttpClient(); String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); try (Response response = client.newCall(request).execute()) { return response.body().string(); } } public static void main(String[] args) throws IOException { GetExample example = new GetExample(); String response = example.run("https://raw.github.com/square/okhttp/master/README.md"); System.out.println(response); } }
这里直接通过new OkHttpClient创建Client对象后,通过newCall(request)获得Call对象,然后通过excute()方法执行请求,获得返回结果。
这里的client.newCall(request).execute()调用就是OkHttpClient发起请求的过程了。
POST请求和GET请求,请求的流程都一样,都是通过client.newCall(request).execute()发起请求。只是在唯一的差别是在构建Request的时候。对比如下:
// GET请求
Request request = new Request.Builder()
.url(url)
.build();
// POST请求
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody)) // 通过post方法讲过请求体封装进Request中。
.build();
接下来我们就针对平时开发中使用POST提交的常用数据格式分别介绍POST请求。
public final class PostString { public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.get("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()); } } public static void main(String... args) throws Exception { new PostString().run(); } }
这里通过RequestBody.create()方法创建请求体。这里第一个参数是MediaType类型,这个就是HTPP协议的知识了,这里不展开说,可以通过MediaType.get()方法获得。
另外,create()方法有多个重载方法,也很简单,这里就不展开介绍了,感兴趣的朋友可以自己去看看。
其实POST请求的整体流程就是这样,
MediaType.get()获取到合适的MediaType类型RequestBody.create()方法,创建请求体(RequestBody)Request.Builder的post()方法,将请求体封装进Request中。public final class PostForm { private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { 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(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); } } public static void main(String... args) throws Exception { new PostForm().run(); } }
这里对比提交字符串,更显简单,直接通过FormBody.Builder()来构建表单提交的内容,然后post提交即可。
public final class PostStreaming { public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.get("text/x-markdown; charset=utf-8"); private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { RequestBody requestBody = new RequestBody() { // 返回合适的MediaType @Override public MediaType contentType() { return MEDIA_TYPE_MARKDOWN; } // 重写该方法,将需要提交的流写入sink中(这里使用Okio的东西,不熟悉的请自行百度) @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()); } } public static void main(String... args) throws Exception { new PostStreaming().run(); } }
流的提交方式稍显复杂一点,一把我们需要自定义一个RequestBody,然后重写RequestBody中的几个方法,具体看上述代码和注释。代码中是直接采用匿名类的形式,你也可以按照你自己的方式自定义。最后也是通过post提交即可。
这里既然可以直接提交流,所以也就可以通过该方式上传文件了。我们只是需要将文件流写入sink即可。
但是该方式上传文件有个注意点,由于该方法会将所有的流写入内存,所以不能上传大文件
public final class PostMultipart { /** * 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 = "9199fdef135c122"; private static final MediaType MEDIA_TYPE_PNG = MediaType.get("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()); } } public static void main(String... args) throws Exception { new PostMultipart().run(); } }
也比较简单,通过MultipartBody.Builder()添加不同部分的请求体,最后通过post提交即可。
public final class PostFile { public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.get("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/markdown/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()); } } public static void main(String... args) throws Exception { new PostFile().run(); } }
起始在上面提交分块提交中,已经使用到了提交文件的东西。就是通过RequestBody.create()这个重载方法(第二个参数是File类型)来创建RequestBody对象,然后通过post提交即可。
这里只是上传了文件,如果你需要制制定该文件流的key的话,还需要通过以下方式进行封装:
MultipartBody.Part.createFormData(String name, @Nullable String filename, RequestBody body)
这里的第一个参数就是你需要为该文件流指定的paramKey, 第二个参数是文件名,第三个参数就是通过RequestBody.create(MEDIA_TYPE_MARKDOWN, file)方法创建的RequestBody对象了。
如果你在上传文件的时候,同时还需要提交其他普通参数,就可以通过2.4.4的分块提交的方式了。
HTTP 头的数据结构是Map<String, List<String>>类型。也就是说,对于每个 HTTP 头,可能有多个值。但是大部分 HTTP 头都只有一个值,只有少部分 HTTP 头允许多个值。至于name的取值说明,可以自行搜索,网上有很多资料。
OkHttp的处理方式是:
使用header(name,value)来设置HTTP头的唯一值,如果请求中已经存在响应的信息那么直接替换掉。
使用addHeader(name,value)来补充新值,如果请求头中已经存在name的name-value,那么还会继续添加,请求头中便会存在多个name相同而value不同的“键值对”。
使用header(name)读取唯一值或多个值的最后一个值
使用headers(name)获取所有值
我们在上面GET和POST请求方式的介绍中,都是直接使用excute()方式发起请求,这个就是OkHttp中的同步请求方式。我们实际开发中大多数时候都是需要异步的,异步请求非常简单,只需要将excute()方法变为enqueue()即可,具体使用参考:
public final class AsynchronousGet { private final OkHttpClient client = new OkHttpClient(); public void run() throws Exception { Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt") .build(); 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()); } } }); } public static void main(String... args) throws Exception { new AsynchronousGet().run(); } }
这里也有个注意点,虽然是异步的,但是其回调并不一定在主线程哦,所以我们在Android开发的时候要特别注意这个问题,不要在回调线程中直接进行UI操作。
这里我们的基本使用就介绍完了,上面在使用的时候涉及到了很多类如(Request,RequestBody,Response,ResponseBody)等,下篇文章就会进步对其一些使用过程中涉及到重要的类做一下说明。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。