最近在看okhttp源码,我们都知道okhttp是用来进行网络请求,下面就从最常用的execute()方法和enqueue()方法切入,来分析okhttp的请求过程。
先来看okhttp的基本用法,之后的分析应该都会以基本用法来进行:
String url = "http://wwww.baidu.com";//这里是URL
OkHttpClient okHttpClient = new OkHttpClient();//先构建okhttp客户端
final Request request = new Request.Builder()//构建request对象
.url(url)
.get()//默认就是GET请求,可以不写
.build();
Call call = okHttpClient.newCall(request);//创建Call对象
Response response = call.execute();//同步方法,会抛异常
call.enqueue(new Callback() {//执行异步请求
@Override
public void onFailure(Call call, IOException e) {
//这里是请求失败的逻辑
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//这里写请求成功的逻辑
}
});
先来看看execute()方法,该方法会立即进行请求。调用该方法时,实际上调用的是RealCall类下的execute()方法。Call本身是一个接口,而RealCall实现了Call接口
@Override public Response execute() throws IOException {
synchronized (this) {//同步检测,看请求是否已经执行
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();//有待研究,和重定向拦截器油管。机翻:捕获调用堆栈跟踪
timeout.enter();
eventListener.callStart(this);//事件监听器
try {
client.dispatcher().executed(this);//实际调用dispatcher来真正执行,在这里会把请求加入执行队列
Response result = getResponseWithInterceptorChain();
//上面这个是重要方法,会调用拦截器来获得请求
if (result == null) throw new IOException("Canceled");//下面这些都是异常情况处理
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);//这里会把请求移出执行队列
}
}
补充点:
dispatcher是okhttp的重要类。他里面维护三个队列,这里涉及到的队列是 runningSyncCalls,即同步执行队列。
其他两个队列分别是异步准备队列,和异步执行队列。
下面看很重要的getResponseWithInterceptorChain()方法,不管是同步请求还是异步请求都会调用该方法。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());//自定义的应用拦截器
interceptors.add(retryAndFollowUpInterceptor);//重定向拦截器
//进行重定向的工作,在这里创建StreamAllocation(很重要的类)
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, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
这里就是okhttp责任链的实现,这里需要搞清楚,还得认真研究每个拦截器,这部分的坑就在下一节再填吧。
总之这个方法就是调用各个拦截器,进行一系列的设置之后向服务器发送请求,并且返回请求结果。
异步请求操作中重要的实现类就是RealCall下的内部类AsyncCall,AsyncCall继承了抽象类NamedRuunble,而抽象类NamedRunnble实现了Runable接口,并重写了run方法。
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();//调用execut()方法开始时执行
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();//需要子类实现
}
//下面是AsyncCall下的execute()方法
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
Response response = getResponseWithInterceptorChain();
//调用责任链来获得请求(上面已经介绍过了)
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
//如果被取消,就执行我们传入的responseCallback的onFailure方法
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
//如果成功,就执行我们传入的responseCallback的onResponse方法
}
} catch (IOException e) {
e = timeoutExit(e);
if (signalledCallback) {
// Do not signal the callback twice!
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);//移出队列
}
}
我们常用的enqueue方法实际调用的同样是RealCall下的enqueue() 方法。这个方法做的其实就是吧请求封装成AsyncCall并加入队列,因为AsyncCall已经间接实现了Runnable的接口,所以这个方法的最终目的是执行AsyncCall线程,在okhttp中会交给线程池处理。
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//以上部分的代码,基本和execute()方法一样,可以理解成请求前的准备工作吧,后面再来把这部分坑补上
client.dispatcher().enqueue(new AsyncCall(responseCallback));
//创建一个异步线程,直接丢进异步准备队列,具体看下面分析
}
//下面是Dispatcher里面的enqeue方法
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);//加入准备队列
}
promoteAndExecute();//异步执行,重要方法
}
不管是execute()还是enqueue()都会调用dispatcher的同名方法。可见dispatcher正如他的类名一样,是okhttp的调度中心
区别就是:
dispatcher的execute()方法加入队列后,方法就结束了
而enqueue()方法只是加入准备队列,把执行交给了promoteAndExecute()去做。
下面看promoteAndExecute()方法:
//顾名思义,把准备队列的请求推到运行队列,并执行
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));//断言,调试用
List<AsyncCall> executableCalls = new ArrayList<>();//需要执行的请求队列
boolean isRunning;//是否在执行的标志
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();//利用迭代器,在准备队列中取出每个请求
if (runningAsyncCalls.size() >= maxRequests) break;
// Max capacity.执行队列已经满了的话,就退出队列
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue;
// Host max capacity. 估计是可同时发送的Host有限制(待研究)
i.remove();//把请求移出准备队列
executableCalls.add(asyncCall);//加入需要执行的请求队列(上面创建的那个)
runningAsyncCalls.add(asyncCall);//加入运行队列
}
isRunning = runningCallsCount() > 0;//count大于0,说明正在执行,isRunning就为true
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);//取出请求
asyncCall.executeOn(executorService());
//创建executorService线程池,并传给给RealCall的内部类asyncall 的excuteOn执行
}
return isRunning;
}
//这里是executorService线程池的创建代码
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));
//同步创建,核心线程为0,最大线程数为Integer.MAX_VALUE,线程空闲存活时间为60s,没有容量的队列
}
return executorService;
}
//这里是AsyncCall的executeOn()方法
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));//断言.方便调试程序?(不确定)
boolean success = false;
try {
executorService.execute(this);//这个this就是AsyncCall,可以理解为异步线程
//异步执行,把请求交给executeService执行,executeService是dispatcher里创建的线程池
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
eventListener.callFailed(RealCall.this, ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
//将它从队列中移出
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
okhttp的execute()方法在调用Dispatch的对应方法,将请求加入同步执行队列后,会直接进行网络请求。
而enqueue()方法就较为复杂一点,会创建一个间接实现了Runnable接口的AsyncCall对象(该对象重写了run方法,并把进行网络请求的逻辑写到run方法中),并把AsyncCall对象加入到异步准备队列中,之后在Dispatcher的promoteAndExecute()方法中又会将异步准备队列里的AsyncCall对象取出,添加到异步执行队列中并把AsyncCall对象交给名为executorService的线程池里内执行。
最后,在请求执行完成后,不管是同步和异步请求,都会将自己从对应的队列中移出。