Android 4.4之后,HttpURLConnection底层实现已被OkHttp替换。可以见得OkHttp的性能已经被Google所认同。对于为何会想深入了解该库的原因:因为它的最底层走到了java的Socket;利用向Socket写入特定的Http协议数据包,实现网络通信。学习该开源项目,对于网络的学历大有益处,除此之外OkHttp使用了缓存和线程池概念。总之个人觉得OkHttp开源项目可以作为学习网络通信一篇很好的教科书。
构造器也很简单,就是对RealCall中的OkHttpClient和Request域进行赋值。
接下来看一下RealCall类中的execute和enqueue方法
该方法完成一个同步请求,首先将这次的同步请求call添加到Dispatcher的工作队列中,随后调用getResponseWithInterceptorChain方法获取request对应的response。最后返回得到的response。
enqueue则是将请求包装成一个异步请求,异步请求继承自Runnable的子接口,实现了一个execute异步方法,Dispatcher会在合适的时间调用该方法,注意这里的execute方法和前面的execute不是同一个方法。在execute方法内部会调用 getResponseWithInterceptorChain方法获得网络请求的返回值,随后利用回调方法,将结果发送给客户。
该方法很简单,就是将Recall请求添加到runningSyncCalls集合中。该集合在构造Dispatcher的时候就初始化好了Deque<RealCall> runningSyncCalls = new ArrayDeque<>();该集合代表着当前正在线程中运行的请求。有的童鞋是不是已经方了?这就完了?我们的请求在哪执行!!??不要忘了我们调用的RealCall的execute方法中,在this.client.getDispatcher().executed(this);语句后面继续调用Response result = this.getResponseWithInterceptorChain(false);方法,而实际的网络请求是在这里进行的。可以说Dispatch主要负责对客户的请求进行管理,留存,备份;重点在于利用线程池对异步请求的处理,同步请求它基本不干活。下面接着看enqueue方法。
这个方法代码相对于execute就多了一些代码,不过逻辑很简单,首先判断集合runningSyncCalls的大小,即当前运行的请求数是否小于maxRequest;同时判断该请求的hostname对应的的请求个数是否小于maxRequestsPerHost。如果条件全为真,则直接将该请求加入到集合runningSyncCalls中,随后调用executorService().execute(call);对异步任务进行处理。否则将异步请求加入到等待异步执行队列readyAsyncCalls中。下面看看executorService方法
该方法返回了一个线程池,很熟悉吧;类似通过Executors.newCachedThreadPool()方法得到一个线程池。那么调用executorService().execute(call);方法最终会执行异步请求的execute方法,而异步请求的execute方法内部会调用getResponseWithInterceptorChain()方法获得response。
方法很简单,将请求从runningAsyncCalls中移除出去,随后执行 promoteCalls()方法,接着看该方法源码
在当前运行任务数大于maxRequests和等待执行异步任务数为空的两种情况下直接返回不进行任何操作。否则
从等待执行异步请求集合中获取到请求,判断该请求的hostname对应的的请求个数是否小于maxRequestsPerHost,为真则将该任务从等待执行异步请求集合中移出,存入runningAsyncCalls集合中,最后调用线程池执行器执行该异步请求的execute方法。到此为止我们对于Dispatcher的介绍就到此为止了。
OkHttp的特点:
- 支持HTTP2/SPDY黑科技
- socket自动选择最好路线,并支持自动重连
- 拥有自动维护的socket连接池,减少握手次数
- 拥有队列线程池,轻松写并发
- 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
- 实现基于Headers的缓存策略
OkHttp的使用:
- 创建OkHttpClient对象:OkHttpClient client = new OkHttpClient();
- 创建网络请求:Request request = new Request.Builder() .url("http://sethfeng.github.io/index.html") .build();
- 得到Call对象:Call call = client.newCall(request); //实际创建的是一个RealCall对象,RealCall中有一个对client对象的引用
- 发送同步请求:Response response = call.excute();
- 发送异步请求:
-
- call.enqueue(new Callback() {
- @Override
- public void onFailure(Request request, IOException e) {
- ...
- }
- @Override
- public void onResponse(Response response) throws IOException {
- ...
- }
- });
-
OkHttpClient工作原理初步分析
RealCall.class
先来看一下通过 client.newCall(request)得到的Call对象
newCall(request)@OkHttpClient.class
- Call newCall(Request request) {
- return new RealCall(this, request);
- }
方法很简单就是利用调用newCall方法的OkHttpClient对象和newCall的输入参数构造一个RealCall对象。接下来看一下RealCall的构造器。
RealCall()@RealCall.class
- protected RealCall(OkHttpClient client, Request originalRequest) {
- this.client = client;
- this.originalRequest = originalRequest;
- }
execute@RealCall.class
- public Response execute() throws IOException
- {
- try {
- this.client.getDispatcher().executed(this); //同步请求不排队
- //方法内部会执行synchronized void executed(Call call) { this.executedCalls.add(call); } 即把这次请求加入到分发器里
- Response result = this.getResponseWithInterceptorChain(false);
- if(result == null) {
- throw new IOException("Canceled");
- }
- var2 = result;
- } finally {this.client.getDispatcher().finished(this); }
- ...
- return var2;
- }
enqueue@RealCall.class
- public void enqueue(Callback responseCallback) {
- enqueue(responseCallback, false);
- }
- 实际调用下面方法
- void enqueue(Callback responseCallback, boolean forWebSocket) {
- synchronized (this) {
- if (executed) throw new IllegalStateException("Already Executed");
- executed = true;
- }
- client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket)); //异步请求会排队!
- }
- 内部类AsyncCall@RealCall.class
- final class AsyncCall extends NamedRunnable{
- protected void execute() {
- Response response = getResponseWithInterceptorChain(forWebSocket);
- if (canceled) {
- signalledCallback = true;
- responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
- } else {
- signalledCallback = true;
- responseCallback.onResponse(RealCall.this, response);
- }
- this.client.getDispatcher().finished(this);
- }
- }
Dispatcher负责对客户请求进行处理,因此接下来分析一下Dispatcher的execute和enqueue方法。将在下一节分析getResponseWithInterceptorChain方法的底层实现。
Dispatcher.class
OkHttpClient中有一个Dispatcher类型的域,在构造OkHttpClient的时候会调用new Dispatcher()方法获得一个Dispatcher对象,因此我们直接看Dispatcher的源码。
Dispatcher()@Dispatcher.class
public Dispatcher() {
}
看到这个方法一脸的懵逼。什么也没做?不要方,既然这里没有做任何工作,那么有两种可能,Dispatcher的相关域要么在类加载时或者声明时就进行了初始化,要么就会在使用的时候再临时进行初始化,即延迟初始化,这也是一种优化。那我们直接看execute方法和enqueue方法
executed@Dispatcher.class
- synchronized void executed(RealCall call) {
- runningSyncCalls.add(call);
- }
enqueue()@Dispatcher.class
- synchronized void enqueue(AsyncCall call) {
- if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
- // maxRequestsPerHost默认为5 maxRequests默认64
- runningAsyncCalls.add(call);
- executorService().execute(call); //交给线程池去执行 call中的execute方法
- } else {
- readyAsyncCalls.add(call);
- //存入等待队列
- //对于存入这里的请求,在方法promoteCalls()中会被取出,进行执行;
- //任务执行完成后,调用finished的promoteCalls()函数,不管是异步还是同步请求,它们在执行完execute方法过后都会调用Dispatcher的finished方法
- }
- }
executorService()@Dispatcher.class
- 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;
- }
那么到此为止对于execute方法和enqueue方法的介绍就结束了。如果好奇点的童鞋,可能会问,如果异步请求被添加到readyAsyncCalls集合中,那么它何时会被执行呢?注意到,在RealCall的execute和enqueue方法执行完后都会执行this.client.getDispatcher().finished(this); 这样一条语句。那么我们来看下Dispatcher的finished方法。
finished()@Dispatcher.class
- synchronized void finished(AsyncCall call) {
- if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
- promoteCalls();
- }
finished()@Dispatcher.class
- 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.
- }
- }
对Dispatcher的总结如下:
- 该类中有两个集合分别为:runningAsyncCalls、readyAsyncCalls前者存放正在执行的请求,后者存放等待执行的请求
- 该类中有一个newCachedThreadPool线程执行器,利用该执行器来执行异步请求的execute方法。也就是说异步请求发送在非当前工作线程,即创建异步请求的线程,而是从线程池中获取一条线程执行网络请求。同步请求则直接是在当前工作线程中执行。
- 该类对异步请求的管理是通过maxRequests、maxRequestsPerHost进行控制的,前者控制线程池中同时运行的最大请求数,防止同时运行线程过多,造成OOM。后者限制了同一hostname下的请求数,防止一个应用占用的网络资源过多,优化用户体验。
文章的最后我们对okhttp中使用过程中遇到的Request、Response、OkHttpClient这几个类进行一下介绍。
Request.class
该类中有如下域
- private final HttpUrl url; //目标地址
- private final String method; //方法
- private final Headers headers; //请求头,Headers.class里面维护了一个private final String[] namesAndValues;数据集
- private final RequestBody body; //请求表单
- private final Object tag; //标签
- private volatile URI javaNetUri; // Lazily initialized.
- private volatile CacheControl cacheControl; // Lazily initialized.
只能通过Builder方法构建Request对象。
Builder()@Builder.class@Request.class
- public Builder() {
- this.method = "GET";
- this.headers = new Headers.Builder();
- }
默认创建的是Get方法
Build()@Builder.class@Request.class
- public Request build() {
- if (url == null) throw new IllegalStateException("url == null");
- return new Request(this);
- }
调用Request构造器,创建Request对象。
Response.class
类中有如下域
- private final Request request; //对应的request
- private final Protocol protocol; //对应的Http协议
- private final int code; //返回状态码
- private final String message; //Http状态对应的消息
- private final Handshake handshake; //TLS握手协议Transport Layer Security
- private final Headers headers; //返回响应头
- private final ResponseBody body; //Http表单
- private Response networkResponse; //来源于网络的Response,如果响应来自缓存,则该值为null
- private Response cacheResponse; //来自缓存的响应
- private final Response priorResponse; //在redirect或者授权改变的时候,该结果不为空
- private volatile CacheControl cacheControl; // Lazily initialized.
Okhttp中有client.internalCache()和client.connectionPool()两个重要的概念,前者管理网络访问的缓存信息,后者用于存储已链接的RealConnection(该RealConnection已经跟对应的hostname完成了三次握手)。下面我们看一下创建Cache和ConnectionPool这两个对象的OkHttpClient对象。
OkHttpClient.class
Internal.instance@OkHttpClient.class
- static {
- Internal.instance = new Internal() {
- @Override public void addLenient(Headers.Builder builder, String line) {
- builder.addLenient(line);
- }
- @Override public void addLenient(Headers.Builder builder, String name, String value) {
- builder.addLenient(name, value);
- }
- @Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) {
- builder.setInternalCache(internalCache);
- }
- @Override public InternalCache internalCache(OkHttpClient client) {
- return client.internalCache();
- }
- @Override public boolean connectionBecameIdle(
- ConnectionPool pool, RealConnection connection) {
- return pool.connectionBecameIdle(connection);
- }
- @Override public RealConnection get(
- ConnectionPool pool, Address address, StreamAllocation streamAllocation) {
- return pool.get(address, streamAllocation);
- }
- @Override public void put(ConnectionPool pool, RealConnection connection) {
- pool.put(connection);
- }
- @Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) {
- return connectionPool.routeDatabase;
- }
- @Override
- public void callEnqueue(Call call, Callback responseCallback, boolean forWebSocket) {
- ((RealCall) call).enqueue(responseCallback, forWebSocket);
- }
- @Override public StreamAllocation callEngineGetStreamAllocation(Call call) {
- return ((RealCall) call).engine.streamAllocation;
- }
- @Override
- public void apply(ConnectionSpec tlsConfiguration, SSLSocket sslSocket, boolean isFallback) {
- tlsConfiguration.apply(sslSocket, isFallback);
- }
- @Override public HttpUrl getHttpUrlChecked(String url)
- throws MalformedURLException, UnknownHostException {
- return HttpUrl.getChecked(url);
- }
- };
- }
这一段代码用static修饰,表明在加载OkHttpClient类时就会对Internal.instance进行初始化操作。
internalCache() @OkHttpClient.class
- InternalCache internalCache() {
- return cache != null ? cache.internalCache : internalCache;
- }
cache域在我们构造OkHttpClient的时候是没有被初始化的,因此如果我们没有通过调用Builder的cache方法设置cache值的话,该方法返回的对象实际上是一个不支持任何缓存操作的对象,说着说该对象的所有方法为空。因此如果需要OkHttpClient支持缓存,需要我们写一个Cache对象并在构造OkHttpClient的时候将其传给OkHttpClient。
cache()@Builder.class@OkHttpClient.class
- public Builder cache(Cache cache) {
- this.cache = cache;
- this.internalCache = null;
- return this;
- }
完成cache的初始化,如果不调用该方法那么OkHttpClient默认不提供Cache功能。对于Cahce更为详细的介绍在后面的章节我们会进行详细介绍。《OkHttp深入学习(三)——Cache》
connectionPool()@OkHttpClient.class
- public ConnectionPool connectionPool() {
- return connectionPool;
- }
connectionPool的初始化是在构建OkHttpClient时创建的,调用的构造器为new ConnectionPool()。对于ConnectionPool更为详细的介绍在后面的章节我们会进行详细介绍。《
OkHttp深入学习(二)——网络》