一、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