OkHttp的使用
1.直接使用okhttp连接网络
先创建一个request对象
Request request = new Request.Builder()
.url(url)
.build();
同步请求
Response response = new OkHttpClient().newCall(request).execute();
InputStream inputStream = response.body().byteStream();//得到输入流
// String jsonData = response.body().string(); //得到json字符串
异步请求
new OkHttpClient().newCall(request).enqueue(new Callback(){
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
ResponseBody responseBody = response.body();
InputStream inputStream = responseBody.byteStream();//得到输入流
//String jsonData = responseBody.string(); //获取json字符串
}
});
首先我们来看一下这其中涉及到的几个类
OkHttpClient
Request,RequestBody
RealCall(Call的实现类)
Response,ResponseBody
OkHttpClient
首先看一下okHttpClient的一部分类注释
/**
* Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their
* responses.
*
* <h3>OkHttpClients should be shared</h3>
*
* <p>OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for
* all of your HTTP calls. This is because each client holds its own connection pool and thread
* pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a
* client for each request wastes resources on idle pools.
okHttpClient就是一个用来发送request,接收response的一个工厂,其中很重要的一点就是okHttpClient最好做成一个单例模式,因为它其中是维护有一个自己的连接池和线程池的。
如果我们要给我们创造的一个okHttpClient对象添加一些比如说网络超时读写超时的东西我们又该怎么去构造呢?
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeOut( , )
.readTimeout( , )
.build();
很明显这是一个构造者模式,传入一些自己定义的参数。
OkHttpClient.Builder 这个类有几个自己认为还比较重要的属性
Dispatcher dispatcher; //分发器
final List<Interceptor> interceptors = new ArrayList<>(); //application级拦截器
final List<Interceptor> networkInterceptors = new ArrayList<>(); //低一级的拦截器
关于分发器和拦截器属于okhttp比较核心的东西,会单独开一章来重温一下。
Request
Request主要也是采取的构造者模式,我们直接看它的Builder类
public static class Builder {
@Nullable HttpUrl url; //url
String method; //post,get..等
Headers.Builder headers; //头部信息
@Nullable RequestBody body; //请求体
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
...
}
从Request.Builder的构造方法可以看出只要不设置method,默认使用的是"GET"访问服务器。
1.Header
我们向一个request添加头部信息调用的方法就是
//Request.java,heads里面其实就是这一个request对象所含有的自己定义的头部信息
public Builder header(String name, String value) {
headers.set(name, value);
return this;
}
也就是说我们创建一个Request对象想添加头部信息应该这么写
Request request = new Request.Builder()
.url(url)
.header("name","value")
.build();
一个http请求的头部信息应该就是这样的
最下面的json字符串其实就是RequestBody里面的东西。
RequstBody
public abstract class RequestBody {
public abstract @Nullable MediaType contentType();
...
public abstract void writeTo(BufferedSink sink) throws IOException;
...
//传一个string字符串
public static RequestBody create(@Nullable MediaType contentType, String content){...}
//传一个UTF-8字符串
public static RequestBody create(final @Nullable MediaType contentType, final ByteString content){...}
//传一个字符数组
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content){...}
//传一个字符数组
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,final int offset, final int byteCount){...}
//传一个file文件
public static RequestBody create(final @Nullable MediaType contentType, final File file){...}
MediaType分类
MediaType mediaType = MediaType.parse(type/subtype;charset);
//type 类别 ,subtype 子类别 , charset 采用什么编码
//例子
MediaType mediaType = MediaType.parse("application/json;UTF-8");
//就是告诉服务器请求体是一个序列化后的json字符串,采用的是UTF-8编码
类型 描述
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’”。
所以当我们请求体需要添加一个json,并且只能接收序列化json响应的时候我们的Request对象就可以这样构建:
Request request = new Request.Builder()
.url(url)
.header("Accept","application/json")
.post(RequestBody.create(MediaType.parse("application/json;UTF-8"),json))
.build();
当然当你调用了post方法传入了一个RequestBody对象,method就变成了"POST"。
RealCall(Call的实现类)
Call call = new OkHttpClient().newCall(request);
OkHttpClient的newCall()方法其实就是创造一个RealCall对象,那么这个Call或者说是RealCall对象是用来干什么的呢
我们经常可以看到call.execute()和call.enqueue(),其实call对象就像把我们的request包装了一下,是一个真正要去发出http请求的一个东西。
那么我们call.execute()和call.enqueue()到底发生了什么,可以去得到我们的Response对象,这其实就和上面提过的OkHttpClient中的分发器和拦截器有关,后面单独会开一章进行讲解。
Response
//同步
Response response = call.execute();
//异步
call.enqueue(new Callback(){
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
}
});
毫无疑问,response也是采用的建造者模式,我们来看一下其中比较常用的几个属性
public final class Response implements Closeable {
final Request request; //持有一个请求对象的引用
...
final int code; //状态码
final String message; // 消息
final Headers headers; //响应头
final @Nullable ResponseBody body; //响应体
...
}
上图是很早之前保存的,最下面的json字符串就是responseBody中的东西
Header
可以根据Header里面的值取自己需要的东西
ResponseBody
//获取总长度,RealResponseBody等会实现这个方法
public abstract long contentLength();
//得到一个字节流
public final InputStream byteStream(){...}
// 得到一个字节数组
public final byte[] bytes() throws IOException{...}
// 得到一个BomAwareReader对象(不常用)
public final Reader charStream() {...}
// 得到一个字符串
public final String string() throws IOException{...}
比如说我们想获取服务器传回来的json字符串
String json = response.body().string();