1. OkHttp网络请求框架
OkHttp框架中几个比较重要的对象: OkHttpClient,Request,Call,Response。
1.1. Example
// step1: 创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.SECONDS)
.build();
// step2: 创建Request,该对象存储了该次请求所需要传递到服务器的信息,以及http请求的一些设置。
Request request = new Request.Builder()
.url("http://10.0.2.2:3000/users/1")
.addHeader("Authorization", "uid=10010")
.method("GET", null)
.build();
// step3: 使用client根据request创建Call对象
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "onFailure: " + e);
}
@Override
public void onResponse(Call call, okhttp3.Response response) {
// step4: 获取网络请求结果
boolean success = response.isSuccessful();
Log.e(TAG, "onResponse: " + success);
String responseString = response.body().string();
Log.e(TAG, "onResponse: " + responseString);
}
});
通常一个应用中,OkHttpClient 实例是使用单例来存储,整个应用使用同一个 client 实例。
1.2. OkHttpClient
OkHttpClient表示的全局的网络请求配置,通过client创建的每一次请求都使用相同的配置。配置项可以通过 OkHttpClient.Builder 来快速创建配置。
File sdcache = getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
通过上面的三个 xxxTimeout 方法可以设置相应的超时时间,以及缓存文件存放位置。
除此之外,还有以下常用方法:
- retryOnConnectionFailure : 设置失败重连;
- addNetworkInterceptor : 添加拦截器,可以在拦截器中添加进行debug输入,添加请求头等;
- addInterceptor: 添加拦截器,和addNetworkInterceptor相比,使用此方法添加的拦截器拦截到的整个网络的生命时间更长;
1.3. Request
Request表示一次请求的详细参数,比如请求方式,请求参数,Header等信息。使用Request.Builder可以快速的创建一个请求配置Request对象。
method
使用 Request.Builder 指定请求方式,有两种方式,一是使用 Request.Builder 的 method 方法,二是使用请求方式对应的方法名。
builder.method("GET", null); // 第二个参数为 RequestBody,可携带需要传给服务器的数据
// or
builder.get();
其它请求方式 POST、DELETE、HEAD、PUT、PATCH 等都有相对应的方法;
注意:GET 请求和 HEAD 请求,如果使用 method 来指定,则第二个参数必须为 null。
addHeader
使用 addHeader 方法可以为请求添加请求头信息,也可以使用 header 或者 headers 方法指定请求头。
builder.addHeader("Authorization", "uid=10010");
// or
builder.header("Authorization", "uid=10010");
RequestBody
在使用 POST、DELETE 等请求时,需要使用 RequestBody 来传递参数到后台服务器,RequestBody 是一个抽象类,可以使用 FormBody来快速创建一个参数信息表单。
RequestBody body = new FormBody.Builder()
.addEncoded("username", "ccf")
.build();
这样创建的一个 FormBody 表单对象,在请求的时候就会被传递到服务器。
public final class FormBody extends RequestBody {
private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");
private final List<String> encodedNames;
private final List<String> encodedValues;
FormBody(List<String> encodedNames, List<String> encodedValues) {
this.encodedNames = Util.immutableList(encodedNames);
this.encodedValues = Util.immutableList(encodedValues);
}
public static final class Builder {
private final List<String> names = new ArrayList<>();
private final List<String> values = new ArrayList<>();
private final Charset charset;
public Builder() {
this(null);
}
public Builder addEncoded(String name, String value) {
names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, true, false, true, true, charset));
values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, true, false, true, true, charset));
return this;
}
}
}
除了FormBody之外,RequestBody还提供了几个重载的create方法,用于快速创建RequestBody;
/**
* Returns a new request body that transmits {@code content}.
* and lacks a charset, this will use UTF-8.
*/
public static RequestBody create(@Nullable MediaType contentType, String content) {
Charset charset = Util.UTF_8;
if (contentType != null) {
charset = contentType.charset();
if (charset == null) {
charset = Util.UTF_8;
contentType = MediaType.parse(contentType + "; charset=utf-8");
}
}
byte[] bytes = content.getBytes(charset);
return create(contentType, bytes);
}
/** Returns a new request body that transmits {@code content}. */
public static RequestBody create(
final @Nullable MediaType contentType, final ByteString content) {
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() throws IOException {
return content.size();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.write(content);
}
};
}
/** Returns a new request body that transmits {@code content}. */
public static RequestBody create(@Nullable MediaType contentType, byte[] content) {
return create(contentType, content, 0, content.length);
}
/** Returns a new request body that transmits {@code content}. */
public static RequestBody create(@Nullable MediaType contentType, byte[] content,
final int offset, final int byteCount) {
}
/** Returns a new request body that transmits the content of {@code file}. */
public static RequestBody create(final @Nullable MediaType contentType, final File file) {
return new RequestBody() {
@Override public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
sink.writeAll(source);
} finally {
Util.closeQuietly(source);
}
}
};
}
File-Upload
文件上传也是一个POST请求,不过此时需要使用RequestBody,而不能直接使用FormBody,比如上传一个txt文件。
File file = ...;
RequestBody.Builder builder = new RequestBody.Builder();
// ...
builder.post(RequestBody.create(MediaType.parse("text/plain"), file));
相对应的,如果上传其它文件,则把MediaType更改成对应的type即可。
File-Download
文件下载和普通的 GET 请求在使用上没有区别,只是在 Callback#onResponse方法处理上,需要做一些调整。
@Override
public void onResponse(Call call, Response response) {
InputStream inputStream = response.body().byteStream();
// TODO 保存文件
saveFile(inputStream);
}
MultipartBody
有时在上传文件的同时还需要传其他参数,此时可以使用 MultipartBody 上传文件和参数。
File file = ...;
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Awesome picture")
.addFormDataPart("image", "awesome_picture.jpg",
RequestBody.create(MediaType.parse("image/*"), file))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + "...")
.url(...)
.post(requestBody)
.build();
1.4. Call
Call对象用于发起请求,发起请求有两种方式:
- enqueue(Callback) 发起异步请求,使用Worker线程
- execute 发起同步请求,使用当前代码所在的线程
当一个请求发起之后,可以使用 cancel()方法取消该请求。
使用client.newCall(request);
创建一个Call对象。
1.5. Response
网络请求完成后,服务器的响应结果即Response。它包含了请求响应的状态码,响应头等信息。
public final class Response implements Closeable {
final Request request;
final Protocol protocol;
final int code;
final String message;
final @Nullable Handshake handshake;
final Headers headers;
final @Nullable ResponseBody body;
final @Nullable Response networkResponse;
final @Nullable Response cacheResponse;
final @Nullable Response priorResponse;
final long sentRequestAtMillis;
final long receivedResponseAtMillis;
}