Okhttp3使用及解析
目錄
Okhttp3使用及解析:https://mp.csdn.net/postedit/83339916
okhttp系統攔截器:https://mp.csdn.net/postedit/83536609
Okhttp的連線池ConnectionPool:https://mp.csdn.net/postedit/83650740
1.使用
依賴: implementation 'com.squareup.okhttp3:okhttp:3.10.0'
1)同步請求
//同步請求 private void sendRequest() { new Thread(new Runnable() { @Override public void run() { OkHttpClient okHttpClient = new OkHttpClient .Builder() .readTimeout(5, TimeUnit.SECONDS)//設定超時... .build(); try { Request request = new Request.Builder() .url("http://www.baidu.com") .get() //請求方式 .build(); //Request封裝成Call物件 Call call = okHttpClient.newCall(request); Response response = call.execute(); Log.e("result: ", response.body().string()); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
2)非同步請求
//非同步請求 private void sendAsyRequest() { OkHttpClient okHttpClient = new OkHttpClient .Builder() .readTimeout(5, TimeUnit.SECONDS)//設定超時... .build(); Request request = new Request.Builder() .url("http://www.baidu.com") .get() //請求方式 .build(); //Request封裝成Call物件 Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Log.e("result: ", response.body().string()); } }); }
除了執行方式為execute與enqueue的區別,execute方式會阻塞當前執行緒,而enqueue方式會自動開啟一個工作執行緒去處理事務,而且onFailure與onResponse回撥都是在工作執行緒中觸發的。
3)提交string
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
String requestBody = "content";
Request request = new Request.Builder()
.url("request url")
.post(RequestBody.create(mediaType, requestBody))
.build();
4)提交流
RequestBody requestBody = new RequestBody() {
@Nullable
@Override
public MediaType contentType() {
return MediaType.parse("text/x-markdown; charset=utf-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("request content");
}
};
Request request = new Request.Builder()
.url("request url")
.post(requestBody)
.build();
5)提交檔案
OkHttpClient okHttpClient = new OkHttpClient();
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
File file = new File("test.md");
Request request = new Request.Builder()
.url("request url")
.post(RequestBody.create(mediaType, file))
.build();
6)提交表單
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("des", "content")
.build();
Request request = new Request.Builder()
.url("request url")
.post(requestBody)
.build();
7)提交分塊請求
OkHttpClient client = new OkHttpClient();
MultipartBody body = new MultipartBody.Builder(" ")
.setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "content"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MediaType.parse("image/png"), new File(" ")))
.build();
Request request = new Request.Builder()
.url("request url")
.post(body)
.build();
8)非系統攔截器
轉自:https://www.jianshu.com/p/ba6e219a0af6
這裡是一個簡單的攔截器,用來打印出去的請求和收到的響應。
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
應用攔截器:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new LoggingInterceptor())
.build();
或者自定義級別:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.BODY;
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.e("日誌攔截器: ", "網路介面資料");
}
});
loggingInterceptor.setLevel(level);
builder.addInterceptor(loggingInterceptor);
okHttpClient = builder.build();
網路攔截器:
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new LoggingInterceptor())
.build();
2.解析執行流程
1)OkHttpClient建立
Builder()中的分發器Dispatcher:分發事務,決定非同步事務是直接處理還是快取等待。
ConnectionPool:儲存請求連結的池,比如請求連結相同,可決定複用等等操作。
public Builder() {
dispatcher = new Dispatcher(); //分發器
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool(); //連線池
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
2)Request建立
Builder()中,指定請求一些引數和請求頭資訊
public Builder() {
this.method = "GET"; //預設get
this.headers = new Headers.Builder(); //請求頭資訊
}
build方法,例項化Request,將請求引數賦值
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
3)Call建立
Call官方解釋:為了執行請求呼叫Call,Call可以取消。Call物件表示單個請求/響應對(流),不能執行兩次。
先看okHttpClient.newCall(request)中newCall:
//準備在將來某個時候執行
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall為Call的實現類,看看newRealCall函式:
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// 安全地將呼叫例項釋出到EventListener。
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}
newRealCall中,例項化RealCall,並設定監聽。看看RealCall構造:
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
可以看到之前建立好的OkHttpClient和Request都被傳入,同時例項化了RetryAndFollowUpInterceptor(重定向攔截器)。
4)execute執行
public interface Call extends Cloneable {
...
Response execute() throws IOException;
...
}
execute()是Call介面中的,具體看它實現類RealCall中的操作:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
//execute只能被執行一次,執行一次後修改標記為true,再次execute拋異常
executed = true;
}
captureCallStackTrace();//捕捉異常資訊
eventListener.callStart(this);//callstart回撥執行
try {
client.dispatcher().executed(this);//分發事務
Response result = getResponseWithInterceptorChain();//攔截器
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);//callfail回撥執行
throw e;
} finally {
client.dispatcher().finished(this); //處理完成後呼叫
}
}
上面有句重點:
client.dispatcher().executed(this);
而dispatcher()只是拿到分發器:
public Dispatcher dispatcher() {
return dispatcher;
}
而executed():
public final class Dispatcher {
//同步請求佇列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
......
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
}
將同步請求的Call新增到佇列runningSyncCalls中。可以看出分發器在execute()過程中起到了很核心的作用,最後看到execute()末尾的client.dispatcher().finished(this),這finished(this):
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(); //傳入promoteCalls==false,不走這個
runningCallsCount = runningCallsCount();//獲取總數
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {//同步佇列無任務且回撥非空
idleCallback.run();
}
}
//獲取同步和非同步執行佇列任務總和
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
作用為將Call從同步執行佇列移除。
總結:將Call的事件,通過分發器分發到到佇列runningSyncCalls中,事件執行完,再通過分發器將這個事件移除。
5)enqueue執行
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
//call只被執行一次
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
重點enqueue函式:
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
//處理請求的執行緒池
private @Nullable ExecutorService executorService;
//儲存不滿足立刻執行條件的非同步請求
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//儲存可立刻執行的非同步請求
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
synchronized void enqueue(AsyncCall call) {
//當前執行的非同步佇列數量小於最大請求數 且 當前主機執行請求數小於主機請求數最大值
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call); //加入可立刻執行的非同步請求佇列
executorService().execute(call); //執行緒池執行單次事務
} else {
readyAsyncCalls.add(call); //不滿足條件,加入快取非同步佇列readyAsyncCalls
}
}
...}
看看enqueue函式的入參AsyncCall:
final class AsyncCall extends NamedRunnable {
......
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
}
是將CallBack(單次事件處理的完成的回撥)賦值給AsyncCall中的Callback。那NamedRunnable是什麼:
public abstract class NamedRunnable implements Runnable {
protected final String name;
protected abstract void execute();
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);
}
}
}
得知單次請求事務的處理,還是交給實現類的execute()方法去處理,即AsyncCall類,看看AsyncCall類的execute()方法:
@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) {
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);//從佇列移除本次請求
}
}
下面看看移除的過程:
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
//呼叫3引數的finish()
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();
}
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
promoteCalls函式具體如下:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // 當前為最大請求數量
if (readyAsyncCalls.isEmpty()) return; //就緒佇列為空
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;
}
}
可以看出:非同步執行佇列的請求執行完,會通過promoteCalls函式,將非同步就緒佇列runningAsyncCalls中滿足條件的請求拿出給執行緒池處理
總結為:將CallBack,也就是單次事務執行完回撥繫結到Runnable,也就是AsyncCall,再將AsyncCall所代表的Runnable給執行緒池處理,最終通過回撥返回結果。