Android-网络请求库okhttp源码阅读随笔

一:先看看okhttp简单的配置以及使用:

1. 在app的module中先配置依赖  implementation'com.squareup.okhttp3:okhttp:3.10.0'

2.okhttp执行网络请求调用方式:

    异步请求调用Call.enqueue();同步请求调用Call.execute()

Activity

二:框架基本流程源码剖析

由于OkhttpClient内部有非常复杂且多的参数配置,作为一个框架来说,为了让用户使用起来比较友好,采用了建造者模式,来构建OkhttpClient所需要的一些重要的参数配置项,这里就不用多说了。

然后我们先分析下一步代码Call call = okHttpClient.newCall(request);这里通过newCall得到了一个Call对象,根据代码调用链可以看出,newCall实际创建的是一个名叫RealCall的对象。        

/**

* Prepares the {@code request} to be executed at some point in the future.

*/

@Override public CallnewCall(Request request) {

return RealCall.newRealCall(this, request, false /* for web socket */);

}

static RealCallnewRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {

// Safely publish the Call instance to the EventListener.

  RealCall call =new RealCall(client, originalRequest, forWebSocket);

  call.eventListener = client.eventListenerFactory().create(call);

  return call;

}

然后我们继续跟进,先看下enqueue这个异步请求究竟是怎么处理的

@Override public void enqueue(Callback responseCallback) {

synchronized (this) {

if (executed)throw new IllegalStateException("Already Executed");

    executed =true;

  }

captureCallStackTrace();

  eventListener.callStart(this);

  client.dispatcher().enqueue(new AsyncCall(responseCallback));

}

这里我们可以看到,他在这里做了个判断,如果同一个Call对象调用了多次enqueue,这里回抛出异常。接下来看关键代码,最终okhttp框架将Callback(也就是我们请求的时候传进来的回调函数)包装为了一个AysncCall(这个AysncCall实际是一个Runnable,最终由Dispatcher类中维护的线程池中的线程执行),交给了一个叫做Dispatcher的类,我们继续跟进...

synchronized void enqueue(AsyncCall call) {

if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost){

runningAsyncCalls.add(call);

    executorService().execute(call);

  }else {

readyAsyncCalls.add(call);

  }

}

Dispatcher

这里可以看到Dispatcher类里面维护了三个队列,包含运行中的异步请求队列runningAysncCalls,运行中的同步请求队列runningSyncCalls,以及待运行的异步请求队列readyAsyncCalls。在调用enqueue的时候,框架层先判断正在运行中的异步队列runningAysncCalls的个数是否小于最大请求数maxRequests(默认64),并且判断这个即将添加的请求的host在runningAysncCalls中是否小于maxRequestsPerHost(默认5),都满足则将这个封装由请求信息的Call对象添加到runningAysncCalls,并且交给线程池executorService执行,不满足则添加到准备队列readyAsyncCalls。刚才已经说过了AsyncCall是一个Runnable,也就是最终由线程执行到AsyncCall的execute方法,继续...


RealCall$$AsyncCall

这里可以看到execute方法最终是通过getResponseWithInterceptorChain()得到Response,然后通过我们在enqueue时传进来的那个Callback将结果回调出去。追踪同步请求代码调用链也发现,最终也是这个方法返回得到Response,那么接下来看看getResponseWithInterceptorChain

RealCall

从getResponseWithInterceptorChain这个方法,框架是将一些Interceptor添加到一个list中,然后创建了一个RealInterceptorChain,调用了它的proceed方法

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,

    RealConnection connection)throws IOException {

//************省略代码

// 这里又创建了一个RealInterceptorChain,不过这里有个关键参数不一样,那就是index +1,这里逻辑是先处理拦截器集合的Interceptor的intercept方法,处理每个拦截器自己的逻辑,然后通过index+1,取下一个拦截器执行interceptor.intercept

  RealInterceptorChain next =new RealInterceptorChain(interceptors, streamAllocation, httpCodec,

      connection, index +1, request, call, eventListener, connectTimeout, readTimeout,

      writeTimeout);

  Interceptor interceptor =interceptors.get(index);

  Response response = interceptor.intercept(next);

//************省略代码

return response;

}

其实这里是一个责任链模式的一个应用,okhttp框架将网络请求的一些步骤封装成了好几层(也就是拦截器interceptor),根据之前上面getResponseWithInterceptorChain的截图可以看到,有以下,简单描述下各自负责的内容:

RetryAndFollowUpInterceptor:网络请求重试以及重定向请求

BridgeInterceptor:主要处理请求里面Header的相关,包括gzip的压缩解压缩,cookie等

CacheInterceptor:这里做了缓存相关策略,比如没网络时但有缓存数据,可以直接返回,还比如说后台返回的数据给了时效性,下次请求的时候看到缓存数据有效,这个时候直接返回缓存数据,节省了网络开销

ConnectInterceptor:这里面有连接池connectionPool,okhttp是基于socket的一个封装,这里有socket连接的缓存

CallServerInterceptor:这里做了最终的网络请求操作与服务端交互

当然,这里可以根据自己的需求自定义拦截器,实现自己的逻辑。然后最终的响应Response通过回调返回后(代码在AsyncCall#execute()),在finally中调用了client.dispatcher().finished(this);这个方法所做的事是从runningAysncCalls中移除这个已经完成的请求,如果条件满足,将readyAsyncCalls中的请求添加到runningAysncCalls队列中并执行

private void finished(Deque 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 i =readyAsyncCalls.iterator(); i.hasNext(); ) {

AsyncCall call = i.next();

    if (runningCallsForHost(call)

i.remove();

      runningAsyncCalls.add(call);

      executorService().execute(call);

    }

if (runningAsyncCalls.size() >=maxRequests)return; // Reached max capacity.

  }

}

至此,关于okhttp一个完整的请求,基本梳理完成。内部的一些拦截器相关内容,后续有时间再深挖。

这里有一些点需要注意:

1.关于okhttp里面Dispatcher的线程池创建,这里采用的是SynchronousQueue,采用这个同步队列的原因是希望更快的将runnable交给线程池里面的线程去处理,一般来说,SynchronousQueue的size<=1。

2.Dispatcher的线程池线程数量最大为Integer.MAX_VALUE,疑问:这里不设上限,会不会有性能问题?答:这里其实不会的,虽然这里没做控制但是runningAsyncCalls这个执行中队列有做上限处理,所以不用担心。

你可能感兴趣的:(Android-网络请求库okhttp源码阅读随笔)