OkHttp的实现原理(一)之同步

来源:互联网 发布:民航网络信息安全 编辑:程序博客网 时间:2024/05/17 23:43

最近我做的一个项目的网络框架就是选用的OkHttp,仅仅只是调用一下Api当然是不够的,想要驾驭它并灵活的运用则需要了解它的实现原理,那么就需要去看它的源码了。
Okhttp有两种请求方式:
1. 同步请求: execute();
2. 异步请求 :

public void enqueue(Callback responseCallback) {        this.enqueue(responseCallback, false);    }

不管是同步请求还是异步请求,最先开始的都是需要有一个Request对象,然后通过OkhttpClient实例会生成一个Call对象

1)  post请求 Request request = new Request.Builder().url(url).post(getRequestBody()).build();       get请求  Request request = new Request.Builder().url(url).get().build();(2Call call = client.newCall(request);

这两步操作对于同步请求和异步请求是相同的,当然你可以往request对象里面加入请求头参数,缓存的设置等,这就看你自己的需求了。


我们先分析同步请求的方式:

public Response execute() throws IOException {        synchronized(this) {            if(this.executed) {                throw new IllegalStateException("Already Executed");            }            this.executed = true;        }        Response var2;        try {            this.client.getDispatcher().executed(this);            Response result = this.getResponseWithInterceptorChain(false);            if(result == null) {                throw new IOException("Canceled");            }            var2 = result;        } finally {            this.client.getDispatcher().finished(this);        }        return var2;    }

首先会先判断这一次请求是否正在处理,如果是就抛出一个异常,如果不是才往下走,可见OkHttp是不允许对一个还没处理完的请求再次发送请求的情况出现的。接下来会看到这段代码this.client.getDispatcher().executed(this); 看语意是得到了一个分发器,这个分发器是在new OkHttpClient对象的时候初始化的

  public OkHttpClient() {        this.routeDatabase = new RouteDatabase();        this.dispatcher = new Dispatcher();    }

得到了分发器后调用executed(this);这个this就是我们一开始生成的Call对象,

 synchronized void executed(Call call) {        this.executedCalls.add(call);    }

是将这个call对象添加到了executedCalls队列中(底层使用数组实现的),我们继续往下面看

  Response result = this.getResponseWithInterceptorChain(false);`            if(result == null) {                throw new IOException("Canceled");            }            var2 = result;        } finally {            this.client.getDispatcher().finished(this);        }        return var2;

这段代码是将result返回,看来这个result就是我们从服务器拿到的数据,好吧,那么这个getResponseWithInterceptorChain(false); 方法就很关键了啊

 private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {        Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket);        return chain.proceed(this.originalRequest);    }

首先构造了一个ApplicationInterceptorChain对象chain ,看这写法就知道是Call的内部类,这个originalRequest其实就是我们之前的request对象,而forWebSocket = false 是我们调用这个方法的时候传进去的,然后看proceed(this.originalRequest)这个方法,这个方法就是发送请求并获得响应的方法了。

 public Response proceed(Request request) throws IOException {            if(this.index < Call.this.client.interceptors().size()) {                Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket);                Interceptor interceptor = (Interceptor)Call.this.client.interceptors().get(this.index);                Response interceptedResponse = interceptor.intercept(chain);                if(interceptedResponse == null) {                    throw new NullPointerException("application interceptor " + interceptor + " returned null");                } else {                    return interceptedResponse;                }            } else {                return Call.this.getResponse(request, this.forWebSocket);            }        }

因为这个index一开始传入的是0,所以先判断okHttpClient对象中是否有拦截器,如果有,就首先创建一个新的ApplicationInterceptorChain对象,跟之前的比就是将index+1传入,然后去interceptors集合里面取出拦截器,通过 Response interceptedResponse = interceptor.intercept(chain);放大获取响应并返回,这个方法是当你给OkHttpClient设置拦截器的时候会调用的方法,这个方法得由开发者自己去写。如果没有拦截器,那么就会调用Call.this.getResponse(request, this.forWebSocket);这个方法去获取响应了。

Response getResponse(Request request, boolean forWebSocket) throws IOException {        RequestBody body = request.body();        if(body != null) {            Builder followUpCount = request.newBuilder();            MediaType releaseConnection = body.contentType();            if(releaseConnection != null) {                followUpCount.header("Content-Type", releaseConnection.toString());            }            long response = body.contentLength();            if(response != -1L) {                followUpCount.header("Content-Length", Long.toString(response));                followUpCount.removeHeader("Transfer-Encoding");            } else {                followUpCount.header("Transfer-Encoding", "chunked");                followUpCount.removeHeader("Content-Length");            }            request = followUpCount.build();        }        this.engine = new HttpEngine(this.client, request, false, false, forWebSocket, (StreamAllocation)null, (RetryableSink)null, (Response)null);        int var20 = 0;        while(!this.canceled) {            boolean var21 = true;            boolean var15 = false;            StreamAllocation streamAllocation;            label173: {                label172: {                    try {                        HttpEngine followUp;                        try {                            var15 = true;                            this.engine.sendRequest();                            this.engine.readResponse();                            var21 = false;                            var15 = false;                            break label173;                        } catch (RequestException var16) {                            throw var16.getCause();                        } catch (RouteException var17) {                            followUp = this.engine.recover(var17);                            if(followUp == null) {                                throw var17.getLastConnectException();                            }                        } catch (IOException var18) {                            followUp = this.engine.recover(var18, (Sink)null);                            if(followUp != null) {                                var21 = false;                                this.engine = followUp;                                var15 = false;                                break label172;                            }                            throw var18;                        }                        var21 = false;                        this.engine = followUp;                        var15 = false;                    } finally {                        if(var15) {                            if(var21) {                                StreamAllocation streamAllocation1 = this.engine.close();                                streamAllocation1.release();                            }                        }                    }                    if(var21) {                        streamAllocation = this.engine.close();                        streamAllocation.release();                    }                    continue;                }                if(var21) {                    streamAllocation = this.engine.close();                    streamAllocation.release();                }                continue;            }            if(var21) {                StreamAllocation var23 = this.engine.close();                var23.release();            }            Response var22 = this.engine.getResponse();            Request var24 = this.engine.followUpRequest();            if(var24 == null) {                if(!forWebSocket) {                    this.engine.releaseStreamAllocation();                }                return var22;            }            streamAllocation = this.engine.close();            ++var20;            if(var20 > 20) {                streamAllocation.release();                throw new ProtocolException("Too many follow-up requests: " + var20);            }            if(!this.engine.sameConnection(var24.httpUrl())) {                streamAllocation.release();                streamAllocation = null;            }            this.engine = new HttpEngine(this.client, var24, false, false, forWebSocket, streamAllocation, (RetryableSink)null, var22);        }        this.engine.releaseStreamAllocation();        throw new IOException("Canceled");    }

首先判断该请求是否是Post请求,如果是,就会对该request对象做一些处理,随后会创建一个HttpEngine对象,将okHttpClient对象和request对象forWebSocket = false作为参数传入进去了。

this.engine = new HttpEngine(this.client, request, false, false, forWebSocket, (StreamAllocation)null, (RetryableSink)null, (Response)null);

之后会进入一个循环,进入该循环的条件就是该请求没有被取消掉,

    this.engine.sendRequest();    this.engine.readResponse();

这两句代码是不是很好理解啊,不就是engine发送了请求,然后读响应吗,然后会跳出标签label173,接着看92行代码 Response var22 = this.engine.getResponse();好家伙,这里就得到响应了,你肯定不爽,怎么这么快就发送请求和获得响应了啊?都还没看到sendRequest(),getResponse()这两个方法内部做了什么操作呢,好吧,那么我必须要来满足你:

 public void sendRequest() throws RequestException, RouteException, IOException {        if(this.cacheStrategy == null) {            if(this.httpStream != null) {                throw new IllegalStateException();            } else {                Request request = this.networkRequest(this.userRequest);                InternalCache responseCache = Internal.instance.internalCache(this.client);                Response cacheCandidate = responseCache != null?responseCache.get(request):null;                long now = System.currentTimeMillis();                this.cacheStrategy = (new Factory(now, request, cacheCandidate)).get();                this.networkRequest = this.cacheStrategy.networkRequest;                this.cacheResponse = this.cacheStrategy.cacheResponse;                if(responseCache != null) {                    responseCache.trackResponse(this.cacheStrategy);                }                if(cacheCandidate != null && this.cacheResponse == null) {                    Util.closeQuietly(cacheCandidate.body());                }                if(this.networkRequest != null) {                    this.httpStream = this.connect();                    this.httpStream.setHttpEngine(this);                    if(this.callerWritesRequestBody && this.permitsRequestBody(this.networkRequest) && this.requestBodyOut == null) {                        long contentLength = OkHeaders.contentLength(request);                        if(this.bufferRequestBody) {                            if(contentLength > 2147483647L) {                                throw new IllegalStateException("Use setFixedLengthStreamingMode() or setChunkedStreamingMode() for requests larger than 2 GiB.");                            }                            if(contentLength != -1L) {                                this.httpStream.writeRequestHeaders(this.networkRequest);                                this.requestBodyOut = new RetryableSink((int)contentLength);                            } else {                                this.requestBodyOut = new RetryableSink();                            }                        } else {                            this.httpStream.writeRequestHeaders(this.networkRequest);                            this.requestBodyOut = this.httpStream.createRequestBody(this.networkRequest, contentLength);                        }                    }                } else {                    this.streamAllocation.release();                    if(this.cacheResponse != null) {                        this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).build();                    } else {                        this.userResponse = (new Builder()).request(this.userRequest).priorResponse(stripBody(this.priorResponse)).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable Request (only-if-cached)").body(EMPTY_BODY).build();                    }                    this.userResponse = this.unzip(this.userResponse);                }            }        }    }

6-19行:获取用户设置的缓存策略。
21-41行:需要从网络上获取数据。
43 - 50行:从缓存中获取数据。这个userResponse 就是缓存从中的拿到的数据。

我们主要看从网络上获取数据这部分内容,要从服务器上获取内容首先要跟服务器建立起连接吧,那么我们来看22行connet();

  private HttpStream connect() throws RouteException, RequestException, IOException {        boolean doExtensiveHealthChecks = !this.networkRequest.method().equals("GET");        return this.streamAllocation.newStream(this.client.getConnectTimeout(), this.client.getReadTimeout(), this.client.getWriteTimeout(), this.client.getRetryOnConnectionFailure(), doExtensiveHealthChecks);    }

doExtensiveHealthChecks = false 说明该请求是GET请求,doExtensiveHealthChecks = ture 说明是POST请求,newStream(this.client.getConnectTimeout(), this.client.getReadTimeout(), this.client.getWriteTimeout(), this.client.getRetryOnConnectionFailure(), doExtensiveHealthChecks);这个方法就是建立连接的方法,让我们看一下:

    public HttpStream newStream(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks) throws RouteException, IOException {        try {            RealConnection e = this.findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);            Object resultStream;            if(e.framedConnection != null) {                resultStream = new Http2xStream(this, e.framedConnection);            } else {                e.getSocket().setSoTimeout(readTimeout);                e.source.timeout().timeout((long)readTimeout, TimeUnit.MILLISECONDS);                e.sink.timeout().timeout((long)writeTimeout, TimeUnit.MILLISECONDS);                resultStream = new Http1xStream(this, e.source, e.sink);            }            ConnectionPool var8 = this.connectionPool;            synchronized(this.connectionPool) {                ++e.streamCount;                this.stream = (HttpStream)resultStream;                return (HttpStream)resultStream;            }        } catch (IOException var11) {            throw new RouteException(var11);        }    }

RealConnection 这个就是真的连接对象了吧,findHealthyConnection这个方法里面会创建连接对象,然后建立连接会调用newConnection.connect(connectTimeout, readTimeout, writeTimeout, this.address.getConnectionSpecs(), connectionRetryEnabled);这个方法,最后会返回这个连接对象就是RealConnection e这个对象了,让我们看看他是如何建立连接的吧

public void connect(int connectTimeout, int readTimeout, int writeTimeout, List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled) throws RouteException {        if(this.protocol != null) {            throw new IllegalStateException("already connected");        } else {            RouteException routeException = null;            ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);            Proxy proxy = this.route.getProxy();            Address address = this.route.getAddress();            if(this.route.getAddress().getSslSocketFactory() == null && !connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {                throw new RouteException(new UnknownServiceException("CLEARTEXT communication not supported: " + connectionSpecs));            } else {                while(this.protocol == null) {                    try {                        this.rawSocket = proxy.type() != Type.DIRECT && proxy.type() != Type.HTTP?new Socket(proxy):address.getSocketFactory().createSocket();                        this.connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector);                    } catch (IOException var11) {                        Util.closeQuietly(this.socket);                        Util.closeQuietly(this.rawSocket);                        this.socket = null;                        this.rawSocket = null;                        this.source = null;                        this.sink = null;                        this.handshake = null;                        this.protocol = null;                        if(routeException == null) {                            routeException = new RouteException(var11);                        } else {                            routeException.addConnectException(var11);                        }                        if(!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(var11)) {                            throw routeException;                        }                    }                }            }        }    }

Address address = this.route.getAddress();获得需要连接到服务器的地址,第14行,创建了Socket对象,所以okHttp是Socket来进行连接的。有了Socket就可以获取输入输出流了,建立了了连接以后你会发现会走下面两句代码:

 this.httpStream.writeRequestHeaders(this.networkRequest);                                this.requestBodyOut =      this.httpStream.createRequestBody(this.networkRequest, contentLength);

httpStream这个对象是什么呢?我们知道http协议网络数据通信,其实就是客户端将请求数据以请求报文的格式发送给服务器,服务器获取请求后,执行相应的处理,然后将返回的结果以响应报文的格式返回给客户端,httpStream就是用来写请求报文和读取响应报文的。

我们再来看看engine.getResponse();获取响应的方法:

  public Response getResponse() {        if(this.userResponse == null) {            throw new IllegalStateException();        } else {            return this.userResponse;        }    }

就是将userResponse返回了,userResponse是什么呢?我们还记得如果是从缓存中拿的数据,那么

  if(this.cacheResponse != null) {                        this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).build();                    } else {                        this.userResponse = (new Builder()).request(this.userRequest).priorResponse(stripBody(this.priorResponse)).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable Request (only-if-cached)").body(EMPTY_BODY).build();                    }                    this.userResponse = this.unzip(this.userResponse);                }

此时userResponse 确实被赋值了,但是如果从网络上请求数据,那么这个userResponse 是什么时候被赋值的呢?不知道大家对 this.engine.readResponse();感觉到奇怪没有,它在发送请求之后,获取响应之前调用,我一开始就得挺奇怪的,我以为这个方法就是获取响应的方法,但是它没有返回值啊,它到底是来干嘛的呢?只有看源码了

 public void readResponse() throws IOException {        if(this.userResponse == null) {            if(this.networkRequest == null && this.cacheResponse == null) {                throw new IllegalStateException("call sendRequest() first!");            } else if(this.networkRequest != null) {                Response networkResponse;                if(this.forWebSocket) {                    this.httpStream.writeRequestHeaders(this.networkRequest);                    networkResponse = this.readNetworkResponse();                } else if(!this.callerWritesRequestBody) {                    networkResponse = (new HttpEngine.NetworkInterceptorChain(0, this.networkRequest)).proceed(this.networkRequest);                } else {                    if(this.bufferedRequestBody != null && this.bufferedRequestBody.buffer().size() > 0L) {                        this.bufferedRequestBody.emit();                    }                    if(this.sentRequestMillis == -1L) {                        if(OkHeaders.contentLength(this.networkRequest) == -1L && this.requestBodyOut instanceof RetryableSink) {                            long responseCache = ((RetryableSink)this.requestBodyOut).contentLength();                            this.networkRequest = this.networkRequest.newBuilder().header("Content-Length", Long.toString(responseCache)).build();                        }                        this.httpStream.writeRequestHeaders(this.networkRequest);                    }                    if(this.requestBodyOut != null) {                        if(this.bufferedRequestBody != null) {                            this.bufferedRequestBody.close();                        } else {                            this.requestBodyOut.close();                        }                        if(this.requestBodyOut instanceof RetryableSink) {                            this.httpStream.writeRequestBody((RetryableSink)this.requestBodyOut);                        }                    }                    networkResponse = this.readNetworkResponse();                }                this.receiveHeaders(networkResponse.headers());                if(this.cacheResponse != null) {                    if(validate(this.cacheResponse, networkResponse)) {                        this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).headers(combine(this.cacheResponse.headers(), networkResponse.headers())).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();                        networkResponse.body().close();                        this.releaseStreamAllocation();                        InternalCache responseCache1 = Internal.instance.internalCache(this.client);                        responseCache1.trackConditionalCacheHit();                        responseCache1.update(this.cacheResponse, stripBody(this.userResponse));                        this.userResponse = this.unzip(this.userResponse);                        return;                    }                    Util.closeQuietly(this.cacheResponse.body());                }                this.userResponse = networkResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).networkResponse(stripBody(networkResponse)).build();                if(hasBody(this.userResponse)) {                    this.maybeCache();                    this.userResponse = this.unzip(this.cacheWritingResponse(this.storeRequest, this.userResponse));                }            }        }    }

6 - 38 行 : 根据不同的条件获取响应networkResponse 。
42 - 60行 :就是真正的对userResponse 进行赋值操作了。

好了,到这里有关OkHttp的同步请求方式的源码就全部分析完了,下一次我将继续分析OkHttp异步请求的原理,最后我想用一张图来简单的总结一下同步请求的流程:

这里写图片描述

0 0
原创粉丝点击