implementation 'com.squareup.okhttp3:okhttp:3.12.0'
同步GET的意思是一直等待http请求, 直到返回了响应. 在这之间会阻塞进程, 所以通过get不能在Android的主线程中执行, 否则会报错.
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
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());
}
OkHttpClient实现了Call.Factory接口, 是Call的工厂类, Call负责发送执行请求和读取响应.
Request代表Http请求, 通过Request.Builder辅助类来构建.client.newCall(request)通过传入一个http request, 返回一个Call调用. 然后执行execute()方法, 同步获得
Response代表Http请求的响应. response.body()是ResponseBody类, 代表响应体, 可以通过responseBody.string()获得字符串的表达形式, 或responseBody.bytes()获得字节数组的表达形式, 这两种形式都会把文档加入到内存. 也可以通过responseBody.charStream()和responseBody.byteStream()返回流来处理.
上述代码完成的功能是下载一个文件, 打印他的响应头, 以string形式打印响应体.
响应体的string()方法对于小文档来说十分方便高效. 但是如果响应体太大(超过1MB), 应避免使用 string()方法, 因为它会将把整个文档加载到内存中.
对于超过1MB的响应body, 应使用流的方式来处理响应body. 这和我们处理xml文档的逻辑是一致的, 小文件可以载入内存树状解析, 大文件就必须流式解析.
异步GET是指在另外的工作线程中执行http请求, 请求时不会阻塞当前的线程, 所以可以在Android主线程中使用.
下面是在一个工作线程中下载文件, 当响应可读时回调Callback接口. 当响应头准备好后, 就会调用Callback接口, 所以读取响应体时可能会阻塞. OkHttp现阶段不提供异步api来接收响应体。
//1.拿到okHttpClient对象,可以设置连接超时等
OkHttpClient okHttpClient=new OkHttpClient();
//2.构造Request请求对象,可以增加头addHeader等
Request.Builder builder = new Request.Builder();
//url()中可以放入网址
Request request = builder.
get().
url("http://publicobject.com/helloworld.txt")
.build();
//3.将Request封装为Call
Call call = okHttpClient.newCall(request);
//4.执行call
//方法一Response response=call.execute();//汇抛出IO异常,同步方法
//方法二,异步方法,放到队列中,处于子线程中,无法更新UI
call.enqueue(new Callback() {
//请求时失败时调用
@Override
public void onFailure(Call call, IOException e) {
}
//请求成功时调用
@Override
public void onResponse(Call call, Response response) throws IOException {
//处于子线程中,能够进行大文件下载,但是无法更新UI
final String res = response.body().string();//请求成功时返回的东西
//InputStream is=response.body().byteStream();
// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存
/**
* runOnUiThread方法切换到主线程中,或者用handler机制也可以
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新ui
}
});
}
});
下面是使用HTTP POST提交请求到服务. 这个例子提交了一个json字符串到web服务. 因为整个请求体都在内存中, 因此避免使用此api提交大文档(大于1MB)
OkHttpClient okHttpClient=new OkHttpClient();
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "{username:hyman,password:123}");
Request.Builder builder = new Request.Builder();
Request request = builder
.url(mBaseUrl + "postString")
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
//4.执行call
//方法一Response response=call.execute();//汇抛出IO异常,同步方法
//方法二,异步方法,放到队列中,处于子线程中,无法更新UI
call.enqueue(new Callback() {
//请求时失败时调用
@Override
public void onFailure(Call call, IOException e) {
}
//请求成功时调用
@Override
public void onResponse(Call call, Response response) throws IOException {
//处于子线程中,能够进行大文件下载,但是无法更新UI
final String res = response.body().string();//请求成功时返回的东西
//InputStream is=response.body().byteStream();
// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存
/**
* runOnUiThread方法切换到主线程中,或者用handler机制也可以
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新ui
}
});
}
});
以流的方式POST提交请求体. 请求体的内容由流写入产生. 这个例子是流直接写入Okio的BufferedSink. 你的程序可能会使用OutputStream, 你可以使用BufferedSink.outputStream()来获取. OkHttp的底层对流和字节的操作都是基于Okio库, Okio库也是Square开发的另一个IO库, 填补I/O和NIO的空缺, 目的是提供简单便于使用的接口来操作IO.
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
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");
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();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
File file = new File(Environment.getExternalStorageDirectory(), "banner.png");
if (!file.exists()) {
return;
}
//1.拿到okHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
//mime type
RequestBody requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
Request.Builder builder = new Request.Builder();
Request request = builder
.url(mBaseUrl + "postFile")
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
//4.执行call
//方法一Response response=call.execute();//汇抛出IO异常,同步方法
//方法二,异步方法,放到队列中,处于子线程中,无法更新UI
call.enqueue(new Callback() {
//请求时失败时调用
@Override
public void onFailure(Call call, IOException e) {
Log.d("报错了吗", "onFailure: 报了");
e.printStackTrace();
}
//请求成功时调用
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
//处于子线程中,能够进行大文件下载,但是无法更新UI
Log.d("执行成功", "onResponse: 成功");
final String res = response.body().string();//请求成功时返回的东西
//InputStream is=response.body().byteStream();
// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存
/**
* runOnUiThread方法切换到主线程中,或者用handler机制也可以
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新ui
}
});
}
});
使用FormEncodingBuilder来构建和HTML标签相同效果的请求体. 键值对将使用一种HTML兼容形式的URL编码来进行编码.
//1.拿到okHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
//2.构造Request请求对象
//构造requestBody,get请求不需要
FormBody.Builder requestBodyBuilder = new FormBody.Builder();
//提交到服务器端的请求体
RequestBody requestBody = requestBodyBuilder
.add("username", "AI")
.add("password", "12345678")
.build();
Request.Builder builder = new Request.Builder();
Request request = builder
.url(mBaseUrl + "login")
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
//4.执行call
//方法一Response response=call.execute();//汇抛出IO异常,同步方法
//方法二,异步方法,放到队列中,处于子线程中,无法更新UI
call.enqueue(new Callback() {
//请求时失败时调用
@Override
public void onFailure(Call call, IOException e) {
}
//请求成功时调用
@Override
public void onResponse(Call call, Response response) throws IOException {
//处于子线程中,能够进行大文件下载,但是无法更新UI
final String res = response.body().string();//请求成功时返回的东西
//InputStream is=response.body().byteStream();
// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存
/**
* runOnUiThread方法切换到主线程中,或者用handler机制也可以
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新ui
}
});
}
});
MultipartBody.Builder可以构建复杂的请求体, 与HTML文件上传形式兼容. 多块请求体中每块请求都是一个请求体, 可以定义自己的请求头. 这些请求头可以用来描述这块请求, 例如它的Content-Disposition. 如果Content-Length和Content-Type可用的话, 他们会被自动添加到请求头中.
File file = new File(Environment.getExternalStorageDirectory(), "uz_splash_bg.png");
if (!file.exists()) {
return;
}
OkHttpClient okHttpClient=new OkHttpClient();
//mime type
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
RequestBody requestBody = multipartBuilder.setType(MultipartBody.FORM)
.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "username" + "\""),
RequestBody.create(null, "hyman"))
.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "password" + "\""),
RequestBody.create(null, "123"))
.addFormDataPart("mPhoto", "hyman.png", RequestBody.create(MediaType.parse("application/octet-stream"), file))
.build();
//mPhoto是File的一个域,value
//显示上传进度,把post的中的requestBody改为countingRequestBody就行了
/**CountingRequestBody countingRequestBody=new CountingRequestBody(requestBody, new CountingRequestBody.Listener() {
@Override public void onRequestProgress(long byteWritten, long contentLength) {
L.e(byteWritten+"/"+contentLength);
}
});*/
Request.Builder builder = new Request.Builder();
Request request = builder
.url(mBaseUrl + "uploadInfo")
.post(requestBody)
.build();
Call call = okHttpClient.newCall(request);
//4.执行call
//方法一Response response=call.execute();//汇抛出IO异常,同步方法
//方法二,异步方法,放到队列中,处于子线程中,无法更新UI
call.enqueue(new Callback() {
//请求时失败时调用
@Override
public void onFailure(Call call, IOException e) {
}
//请求成功时调用
@Override
public void onResponse(Call call, Response response) throws IOException {
//处于子线程中,能够进行大文件下载,但是无法更新UI
final String res = response.body().string();//请求成功时返回的东西
//InputStream is=response.body().byteStream();
// 执行IO操作时,能够下载很大的文件,并且不会占用很大内存
/**
* runOnUiThread方法切换到主线程中,或者用handler机制也可以
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新ui
}
});
}
});
典型的HTTP头像是一个Map<String, String> : 每个字段都有一个或没有值. 但是一些头允许多个值, 像Guava的Multimap.
例如: HTTP响应里面提供的Vary响应头, 就是多值的. OkHttp的api试图让这些情况都适用.
当写请求头的时候, 使用header(name, value)可以设置唯一的name、value. 如果已经有值, 旧的将被移除, 然后添加新的. 使用addHeader(name, value)可以添加多值(添加, 不移除已有的).
当读取响应头时, 使用header(name)返回最后出现的name、value. 通常情况这也是唯一的name、value. 如果没有值, 那么header(name)将返回null. 如果想读取字段对应的所有值, 使用headers(name)会返回一个list.
为了获取所有的Header, Headers类支持按index访问.
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// 请求头的设置
// header()会覆盖之前已设置的同名请求头,而addHeader()不会
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);
// response.header(headerName)获取的是响应头对应字段的最新值
System.out.println("Server: " + response.header("Server"));
System.out.println("Date: " + response.header("Date"));
// 获取响应头集合
System.out.println("Vary: " + response.headers("Vary"));
}
}
Gson是一个在JSON和Java对象之间转换非常方便的api库. 这里我们用Gson来解析Github API的JSON响应.
注意: ResponseBody.charStream()使用响应头Content-Type指定的字符集来解析响应体. 默认是UTF-8.
private final OkHttpClient client = new OkHttpClient();
private final Gson gson = new Gson();
public void run() throws Exception {
Request request = new Request.Builder()
.url("https://api.github.com/gists/c2a7c39532239ff261be")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue().content);
}
}
static class Gist {
Map<String, GistFile> files;
}
static class GistFile {
String content;
}
为了缓存响应, 你需要一个你可以读写的缓存目录, 和缓存大小的限制. 这个缓存目录应该是私有的, 不信任的程序应不能读取缓存内容.
一个缓存目录同时拥有多个缓存访问是错误的. 大多数程序只需要调用一次new OkHttp(), 在第一次调用时配置好缓存, 然后其他地方只需要调用这个实例就可以了. 否则两个缓存示例互相干扰, 破坏响应缓存, 而且有可能会导致程序崩溃.
响应缓存使用HTTP头作为配置. 你可以在请求头中添加Cache-Control: max-stale=3600 , OkHttp缓存会支持. 你的服务通过响应头确定响应缓存多长时间, 例如使用Cache-Control: max-age=9600.
private final OkHttpClient client;
public CacheResponse(File cacheDirectory) throws Exception {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
client = new OkHttpClient();
client.setCache(cache);
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Response response1 = client.newCall(request).execute();
if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
String response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
System.out.println("Response 1 cache response: " + response1.cacheResponse());
System.out.println("Response 1 network response: " + response1.networkResponse());
Response response2 = client.newCall(request).execute();
if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
String response2Body = response2.body().string();
System.out.println("Response 2 response: " + response2);
System.out.println("Response 2 cache response: " + response2.cacheResponse());
System.out.println("Response 2 network response: " + response2.networkResponse());
System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}
如果需要阻值response使用缓存, 使用CacheControl.FORCE_NETWORK. 如果需要阻值response使用网络, 使用CacheControl.FORCE_CACHE.
警告: 如果你使用FORCE_CACHE, 但是response要求使用网络, OkHttp将会返回一个504 Unsatisfiable Request响应.
有些时候, 比如用户刚刚点击刷新按钮, 这时必须跳过缓存, 直接从服务器抓取数据. 为了强制全面刷新, 我们需要添加no-cache指令:
connection.addRequestProperty("Cache-Control", "no-cache");
这样就可以强制每次请求直接发送给源服务器, 而不经过本地缓存版本的校验, 常用于需要确认认证的应用和严格要求使用最新数据的应用.
有时你会想立即显示资源. 这样即使在后台正下载着最新资源, 你的客户端仍然可以先显示原有资源, 毕竟有个东西显示比没有东西显示要好.
如果需要限制让请求优先使用本地缓存资源, 需要增加only-if-cached指令:
try {
connection.addRequestProperty("Cache-Control", "only-if-cached");
InputStream cached = connection.getInputStream();
// the resource was cached! show it
catch (FileNotFoundException e) {
// the resource was not cached
}
}
使用Call.cancel()可以立即停止掉一个正在执行的call. 如果一个线程正在写请求或者读响应, 将会引发IOException. 当call没有必要的时候, 使用这个api可以节约网络资源. 例如当用户离开一个应用时, 不管同步还是异步的call都可以取消.
你可以通过tags来同时取消多个请求. 当你构建一请求时, 使用RequestBuilder.tag(tag)来分配一个标签, 之后你就可以用OkHttpClient.cancel(tag)来取消所有带有这个tag的call.
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
final long startNanos = System.nanoTime();
final Call call = client.newCall(request);
// Schedule a job to cancel the call in 1 second.
executor.schedule(new Runnable() {
@Override public void run() {
System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);
call.cancel();
System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);
}
}, 1, TimeUnit.SECONDS);
try {
System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);
Response response = call.execute();
System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
(System.nanoTime() - startNanos) / 1e9f, response);
} catch (IOException e) {
System.out.printf("%.2f Call failed as expected: %s%n",
(System.nanoTime() - startNanos) / 1e9f, e);
}
}
没有响应时使用超时结束call. 没有响应的原因可能是客户点链接问题、服务器可用性问题或者这之间的其他东西. OkHttp支持连接超时, 读取超时和写入超时.
private final OkHttpClient client;
public ConfigureTimeouts() throws Exception {
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
Response response = client.newCall(request).execute();
System.out.println("Response completed: " + response);
}
使用OkHttpClient, 所有的HTTP Client配置包括代理设置、超时设置、缓存设置. 当你需要为单个call改变配置的时候, 调用OkHttpClient.newBuilder(). 这个api将会返回一个builder, 这个builder和原始的client共享相同的连接池, 分发器和配置.
下面的例子中,我们让一个请求是500ms的超时、另一个是3000ms的超时。
每一个Call(其实现是RealCall)只能执行一次,否则会报异常,具体参见 RealCall#execute()
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();
try {
// Copy to customize OkHttp for this request.
OkHttpClient copy = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Response response = copy.newCall(request).execute();
System.out.println("Response 1 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 1 failed: " + e);
}
try {
// Copy to customize OkHttp for this request.
OkHttpClient copy = client.newBuilder()
.readTimeout(3000, TimeUnit.MILLISECONDS)
.build();
Response response = copy.newCall(request).execute();
System.out.println("Response 2 succeeded: " + response);
} catch (IOException e) {
System.out.println("Response 2 failed: " + e);
}
}
这部分和HTTP AUTH有关.
使用HTTP AUTH需要在server端配置http auth信息, 其过程如下:
一次典型的访问场景是:
OkHttp会自动重试未验证的请求. 当响应是401 Not Authorized时,Authenticator会被要求提供证书. Authenticator的实现中需要建立一个新的包含证书的请求. 如果没有证书可用, 返回null来跳过尝试.
使用Response.challenges()来获得任何authentication challenges的 schemes 和 realms. 当完成一个Basic challenge, 使用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 {
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();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/secrets/hellosecret.txt")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
当认证无法工作时, 为了避免多次重试, 你可以返回空来放弃认证. 例如, 当exact credentials已经尝试过, 你可能会直接想跳过认证, 可以这样做:
if (credential.equals(response.request().header("Authorization"))) {
return null; // If we already failed with these credentials, don't retry.
}
当重试次数超过定义的次数, 你若想跳过认证, 可以这样做:
if (responseCount(response) >= 3) {
return null; // If we've failed 3 times, give up.
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
OkHttp的拦截器链可谓是其整个框架的精髓,用户可传入的 interceptor 分为两类:
这里举一个简单的例子,例如有这样一个需求,我要监控App通过 OkHttp 发出的所有原始请求,以及整个请求所耗费的时间,针对这样的需求就可以使用第一类全局的 interceptor 在拦截器链头去做。
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
Request request = new Request.Builder()
.url("http://www.publicobject.com/helloworld.txt")
.header("User-Agent", "OkHttp Example")
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
ResponseBody body = response.body();
if (body != null) {
Log.d(TAG, "onResponse: " + response.body().string());
body.close();
}
}
});
public class LoggingInterceptor implements Interceptor {
private static final String TAG = "LoggingInterceptor";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.nanoTime();
Log.d(TAG, String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long endTime = System.nanoTime();
Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (endTime - startTime) / 1e6d, response.headers()));
return response;
}
}
针对这个请求,打印出来的结果
Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example
Received response for https://publicobject.com/helloworld.txt in 1265.9ms
Server: nginx/1.10.0 (Ubuntu)
Date: Wed, 28 Mar 2018 08:19:48 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes
注意到一点是这个请求做了重定向,原始的 request url 是 http://www.publicobject.com/helloworld.tx,而响应的 request url 是 https://publicobject.com/helloworld.txt,这说明一定发生了重定向,但是做了几次重定向其实我们这里是不知道的,要知道这些的话,可以使用 addNetworkInterceptor()去做。更多的关于 interceptor的使用以及它们各自的优缺点,请移步OkHttp Interceptors 拦截器
Okhttp默认情况下使用的是系统
推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。当然,也可以使用如下的方式来创建一个新的 OkHttpClient 实例,它们共享连接池、线程池和配置信息。
OkHttpClient eagerClient = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Response response = eagerClient.newCall(request).execute();
OkHttp中被挂起的线程和连接都将会在保持空闲时自动回收。如果我们想要主动释放资源,可以使用如下方式,之后所有的Call对象执行请求都将被拒绝。
client.dispatcher().executorService().shutdown();
清理连接池可以使用如下方式(连接池的守护线程可能不会立即退出):
client.connectionPool().evictAll();
如果希望关闭缓存,可以使用如下方式:
client.cache().close();
MediaType媒体类型:决定浏览器将以什么形式、什么编码对资源进行解析
Content-Type:也属于MediaType媒体类型,主要用于在请求头中指定资源的MediaType
一、MediaType类型
类型 描述
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’”
二、MediaType对象解析
MediaType对象包含了三种信息:type 、subtype、charset,一般将这些信息传入parse()方法中,这样就可以解析出MediaType对象
例子1:
text/x-markdown; charset=utf-8
type值是text,表示是文本这一大类;
/ 后面的x-markdown是subtype,表示是文本这一大类下的markdown这一小类;
charset=utf-8 则表示采用UTF-8编码
参考文章
Android OkHttp完全解析 是时候来了解OkHttp了
OkHttp使用教程
okhttp3使用总结
Okhttp3基本使用
OkHttp使用完全教程