一、okhttp3的使用
1、添加依赖
compile 'com.squareup.okhttp3:okhttp:3.7.0'
compile 'com.squareup.okio:okio:1.12.0'
2、基本用法
(1)get异步请求
private void get(String url){
OkHttpClient client = new OkHttpClient().newBuilder().build();
Request request = new Request.Builder()
.url(url)
.header("","")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
(2)post异步请求(参数为Map)
private void post(String url, Map<String, String> maps) {
OkHttpClient client = new OkHttpClient().newBuilder().build();
if (maps == null) {
maps = new HashMap<>();
}
Request.Builder builder = new Request.Builder()
.url(url);
if (maps != null && maps.size() > 0) {
FormBody formBody = new FormBody.Builder();
for (String key : maps.keySet()) {
body.add(key, paramsMap.get(key));
}
builder.post(formBody.builder());
}
Request request = builder.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
(3)post异步请求(参数为json)
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public void post(String url,JsonObject json) {
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url("")
.post(body)
.build();
Call call = client.newCall(request);
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 {
}
});
}
(4)上传文件 MutipartBody
private void postFile(){
OkHttpClient client = new OkHttpClient().newBuilder().build();
RequestBody requestBody =
RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=utf-8") , new File(""));
String name = "fileName"; //文件名称
try {
name = URLEncoder.encode(name, "UTF-8"); //文件名称编码,防止出现中文乱码
} catch (UnsupportedEncodingException e1) {
//TODO
}
//定义请求体,前面三个为表单中的string类型参数,第四个为需要上传的文件
MultipartBody mBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("fileSize" , "12123")
.addFormDataPart("time" , "234234")
.addFormDataPart("name" , name)
.addFormDataPart("file" , name , requestBody)
.build();
Request request = new Request.Builder().url("").header("","").post(mBody).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
3、异步请求结果
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 responseData=response.body().string();
//如果希望返回的是二进制字节数组
byte[] responseBytes=response.body().bytes();
//如果希望返回的是inputStream,有inputStream我们就可以通过IO的方式写文件.
InputStream responseStream=response.body().byteStream();
}
});
注: 异步请求callback回调是在工作线程中,所以不能直接更新UI,可以通过Looper.myLooper()==Looper.getMainLooper() 进行简单判断,解决方式可以使用Handler
4、Request的参数RequestBody
RequestBody是抽象类,FormBody和MultipartBody是其子类。
Request request = new Request.Builder()
.url("")
.header("", "")
.post(RequestBody body)
.build();
//RequestBody的创建
RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=utf-8") , new File(""));
或
MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
RequestBody imgBody = MultipartBody.create(MEDIA_TYPE_PNG, new Flie());
//FormBody的创建
FormBody body = new FormBody.Builder()
.add("", "")
.build();
//MultipartBody的创建
MultipartBody body=new MultipartBody.Builder()
.addFormDataPart("key","value")
.addFormDataPart("name","fileName",RequestBody body)
.build();
5、自定义拦截器
(1)日志拦截器
public class LogInterceptor implements Interceptor {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
Headers headers = request.headers();
Set<String> names = headers.names();
Iterator<String> iterator = names.iterator();
//打印请求路径
Log.d(getClass().getSimpleName(), "url=" + request.url());
//打印header
Log.d(getClass().getSimpleName(), "=======headers start=====");
while (iterator.hasNext()) {
String next = iterator.next();
Log.d(getClass().getSimpleName(), next + ":" + headers.get(next));
}
Log.d(getClass().getSimpleName(), "=======headers end=====");
//打印post方式请求参数
String method = request.method();
if (method.equals("POST")) {
RequestBody body = request.body();
if (body != null) {
if (body.contentType() != null) {
Log.d(getClass().getSimpleName(), "contentType:" + body.contentType().toString());
}
Log.d(getClass().getSimpleName(), "=======params start=====");
if (body instanceof FormBody) {
FormBody formBody = (FormBody) body;
for (int i = 0; i < formBody.size(); i++) {
Log.d(getClass().getSimpleName(), formBody.name(i) + ":" + formBody.value(i));
}
}
Log.d(getClass().getSimpleName(), "=======params end=====");
}
}
//打印response
Response response = chain.proceed(request);
ResponseBody body = response.body();
if (body != null) {
Log.d(getClass().getSimpleName(), "response:" + body.toString());
}
return response;
}
}
(2)添加header拦截器
/*
* 添加请求头
*/
public class HeadInterceptor implements Interceptor {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader("key", "value")
.build();
Headers headers = request.headers();
Set<String> names = headers.names();
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
Log.d("aaa", next + ":" + headers.get(next));
}
Response response = chain.proceed(request);
return response;
}
}
6、用法扩展
(1)下载文件获取进度条
OkHttpClient client = new OkHttpClient.Builder().build();
okhttp3.Request request = new okhttp3.Request.Builder().url("").build();
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try {
is = response.body().byteStream();
long total = response.body().contentLength();
fos = new FileOutputStream("file");
long sum = 0;
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
sum += len;
int progress = (int) (sum * 1.0f / total * 100);
//更新下载进度progress,这是在工作线程中
}
fos.flush();
//下载成功
} catch (Exception e) {
//下载失败
} finally {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
}
}
});
}
(2)上传进度
interface ProgressListener {
void onProgress(long totalBytes, long remainingBytes, boolean done);
}
public RequestBody createCustomRequestBody(final MediaType contentType,final File file,final ProgressListener listener){
return new RequestBody() {
@Nullable
@Override
public MediaType contentType() {
return contentType;
}
@Override
public long contentLength() throws IOException {
return file.length();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source;
try{
source= Okio.source(file);
Buffer buf=new Buffer();
Long remaining=contentLength();
for(long readCount;(readCount=source.read(buf,2048))!=-1;){
sink.write(buf,readCount);
listener.onProgress(contentLength(),remaining-=readCount,remaining==0);
}
}catch (Exception e){
}
}
};
OkHttpClient client = new OkHttpClient.Builder().build();
MultipartBody.Builder builder = new MultipartBody.Builder();
builder.setType(MultipartBody.FORM);
builder.addFormDataPart("file", "fileName", createCustomRequestBody(MultipartBody.FORM, new File(""), new ProgressListener() {
@Override
public void onProgress(long totalBytes, long remainingBytes, boolean done) {
}
}));
RequestBody requestBody=builder.build();
okhttp3.Request request = new okhttp3.Request.Builder().url("").post(requestBody).build();
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
}
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
}
});
(3)断点续传
Request request = new Request.Builder()
.url("")
.header("RANGE", "bytes="+count)
.post(body)
.build();
(4)json解析
//案例一
JSONObject object=new JSONObject();
try {
object.put("name","value");
JSONArray array=new JSONArray();
array.put("value1");
array.put("value2");
object.put("name1",array);
object.toString();
} catch (JSONException e) {
e.printStackTrace();
}
//案例二
JSONObject object1=new JSONObject(new HashMap());
//案例三
try {
JSONObject object2=new JSONObject("");
} catch (JSONException e) {
e.printStackTrace();
}
(5)上传数据安全性验证
第一步上传指定字段,标识所有参数的string
第二步把第一步返回的字符串+token然后进行加密,上传到服务端
第三步服务端验证
(6)response抽取
public interface BaseResponse{
void onFailure(Request request, Throwable e);
void onSuccess(JsonObject result);
}
call.enqueue(new Callback() {
@Override
public void onFailure(final Request request, final IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
cb.onFailure(request, e);
}
});
}
@Override
public void onResponse(final Response response) throws IOException {
final String responseString = response.body().string();
if (responseString.replaceAll("\\s", "").equals("{}")) {
runOnUiThread(new Runnable() {
@Override
public void run() {
cb.onFailure(response.request(), new Exception("response={}"));
}
});
} else {
final JsonObject responseJson;
try {
responseJson = gson.fromJson(responseString, JsonElement.class).getAsJsonObject();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (responseJson == null || responseJson.isJsonNull()) {
cb.onFailure(response.request(), new Exception("response.isSuccessful():" + response.isSuccessful()));
return;
}
try {
String code = responseJson.get("code").getAsString();
if ("1421".equals(code)) {//抽取特殊状态码
}
cb.onSuccess(responseJson);
} catch (Exception e) {
e.printStackTrace();
cb.onFailure(response.request(), new Exception("Unknown Exception "));
}
}
});
} catch (JsonSyntaxException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
cb.onFailure(response.request(), new Exception("Json Syntax Exception"));
}
});
}
}
}
});
二、Https
1、信任所有证书
OkHttpClient.Builder okhttpClient = new OkHttpClient().newBuilder();
//信任所有服务器地址
okhttpClient.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
//设置为true
return true;
}
});
//创建管理器
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
} };
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
//为OkHttpClient设置sslSocketFactory
okhttpClient.sslSocketFactory(sslContext.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
return okhttpClient.build();
2、自定义证书
/**
* 自定义证书获取SSL
*/
public static SSLContext sslContextForTrustedCertificates(InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取证书流
*/
private static InputStream trustedCertificatesInputStream() {
String certificateAuthority = "";
return new Buffer()
.writeUtf8(certificateAuthority)
.inputStream();
}
/**
* 设置
**/
SSLContext sslContext = sslContextForTrustedCertificates(trustedCertificatesInputStream());
mOkHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
三、源码解析
1、Okhttp3初始化,源码如下:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
final Dispatcher dispatcher;//请求分发
final Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors; //用户自定义拦截器
final List<Interceptor> networkInterceptors; //网络拦截器
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final Cache cache;
final InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory; //https证书
final CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool; //连接池
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int connectTimeout;
final int readTimeout;
final int writeTimeout;
final int pingInterval;
使用builder设计模式初始化OkHttpClient实例,在构造函数中创建了成员变量dispatcher(负责网络请求分发),dispatcher中包含三个成员变量:同步正在执行队列runningSyncCalls,泛型为RealCall;异步正在执行队列runningAsyncCalls,泛型为AsyncCall,长度为64;异步等待队列 readyAsyncCalls,泛型为AsyncCall;源码如下:
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
/** Executes calls. Created lazily. */
private ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
先看一下AsyncCall类, 它是ReayCall的内部类,继承了抽象类NamedRunnable,覆写抽象方法execute(),而NamedRunnable类实现了Runnable接口,因此AsyncCall是Runnable的子类,是多线程类。源码如下:
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@Override
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
2、client.newCall(request)实际上返回的是一个RealCall的实例,RealCall实现了Call接口。RealCall的构造函数是OkHttpClient的实例引用和Request的实例引用,所以RealCall的实例分别持有了okhttpClient和ruquest的引用,源码如下:
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
------------参数为OkHttpClient和request--------
return new RealCall(this, request, false /* for web socket */);
}
RealCall中提供了两个重要的方法,execute()和enqueue(),其中execute是同步执行方法,enqueue是异步请求。源码如下:
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
final EventListener eventListener;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// TODO(jwilson): this is unsafe publication and not threadsafe.
this.eventListener = eventListenerFactory.create(this);
}
@Override
public Request request() {
return originalRequest;
}
---------同步请求-----------------------
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
-----------------异步请求----------------
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
@Override
public void cancel() {
retryAndFollowUpInterceptor.cancel();
}
@Override
public synchronized boolean isExecuted() {
return executed;
}
@Override
public boolean isCanceled() {
return retryAndFollowUpInterceptor.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
@Override
public RealCall clone() {
return new RealCall(client, originalRequest, forWebSocket);
}
StreamAllocation streamAllocation() {
return retryAndFollowUpInterceptor.streamAllocation();
}
/**
* Returns a string that describes this call. Doesn't include a full URL as that might contain
* sensitive information.
*/
String toLoggableString() {
return (isCanceled() ? "canceled " : "")
+ (forWebSocket ? "web socket" : "call")
+ " to " + redactedUrl();
}
String redactedUrl() {
return originalRequest.url().redact();
}
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
}
3、主要看异步请求方法enqueue();首先把callback封装成AsyncCall类,这是个多线程类,然后调用dispatcher.enqueue方法,源码如下:
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
在dispatcher.enqueue(AsyncCall call)方法中,先判断异步正在执行队列是否满了(最多64个),如果满了就添加到异步等待队列中;反之则创建了一个线程池,把这个新任务添加到线程池中。等待这个任务能执行的时候,自会执行自己的execute方法(AsyncCall的execute方法)。下面是源码以及线程池的创建源码:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
---------线程池的创建核心线程数为0,最大线程数是int的最大值-------------------------------------
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
最后看AsyncCall类的execute()方法,在try中调用getResponseWithInteceptorChain()真正的网络请求,在catch中返回错误,在finally中调用dispatcher的finished(AsyncCall)进行扫尾工作。源码如下:
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
我们先看看扫尾工作finished方法,把这个任务从正在执行的异步队列中移除,如果等待队列不为空,则从等待队列中取出一个任务添加到正在执行的异步队列中,并运行新的任务,源码如下:
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
4、重中之重,getResponseWithInterceptorChain()返回的就是数据Response,它采用了责任链设计模式。在该方法中,首先创建拦截器集合,把用户自定义的拦截器和系统添提供的拦截器都添加到这个集合中,然后封装成RealInterceptorChian链,然后调用proceed()方法,参数为request,把结果返回。源码如下:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
RealInterceptorChain类实现了接口Interceptor.Chain,需要实现proceed方法 ,源码如下:
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private int calls;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
@Override public Connection connection() {
return connection;
}
public StreamAllocation streamAllocation() {
return streamAllocation;
}
public HttpCodec httpStream() {
return httpCodec;
}
@Override public Request request() {
return request;
}
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
return response;
}
}
在proceed方法中,取出一个拦截器,调用interceptor.intercept(next),next是新封装的拦截器链RealInterceptorChain,每个拦截器是实现了Interceptor接口,实现intercept方法,在该方法中,可以对ruquest进行再次操作,然后调用chain.proceed(request)那会结果,然后再多response进行操作,然后返回给上一级拦截器interceptor,例:BridgeInterceptor的源码:
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
--------获取提交数据的长度-------
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
--------添加请求头-------------
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
5、系统提供的拦截器
- RetryAndFollowUpInterceptor
处理重试的一个拦截器,里面是一个死循环,它会去尝试处理一些异常,只要不是指致命的异常就会重新发起一次请求,
如果是致命的异常(arouter,io异常)就会抛出来,停止死循环;还会处理一些重定向问题(307)
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), false, request)) {
throw e.getLastConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build();
}
Request followUp = followUpRequest(response);
if (followUp == null) {
if (!forWebSocket) {
streamAllocation.release();
}
return response;
}
closeQuietly(response.body());
if (++followUpCount > MAX_FOLLOW_UPS) {
streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!sameConnection(response, followUp.url())) {
streamAllocation.release();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(followUp.url()), callStackTrace);
} else if (streamAllocation.codec() != null) {
throw new IllegalStateException("Closing the body of " + response
+ " didn't close its backing stream. Bad interceptor?");
}
request = followUp;
priorResponse = response;
}
}
- BridgeInterceptor
设置通用的请求头:Connect-type Connection Content-length Cookie
返回的处理,如果返回的数据被压缩了采用AipSource,保存Cookie
@Override public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
long contentLength = body.contentLength();
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
} else {
requestBuilder.header("Transfer-Encoding", "chunked");
requestBuilder.removeHeader("Content-Length");
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive");
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
boolean transparentGzip = false;
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true;
requestBuilder.header("Accept-Encoding", "gzip");
}
List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
if (!cookies.isEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies));
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", Version.userAgent());
}
Response networkResponse = chain.proceed(requestBuilder.build());
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
Response.Builder responseBuilder = networkResponse.newBuilder()
.request(userRequest);
if (transparentGzip
&& "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
&& HttpHeaders.hasBody(networkResponse)) {
GzipSource responseBody = new GzipSource(networkResponse.body().source());
Headers strippedHeaders = networkResponse.headers().newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build();
responseBuilder.headers(strippedHeaders);
responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
6.3、CacheInterceptor
6.4、ConnectInterceptor
6.5、CallServerInterceptor