okHttp 框架源码学习

作者: 看我眼前007 | 来源:发表于2017-10-13 14:22 被阅读398次
    Retrofit,OkHttp,Okio Square 安卓平台网络层三板斧源码学习
    基于 okhttp 3.9.0 版本 okhttp github 地址

    使用方式

    OkHttpClient client = new OkHttpClient();
    
    String run(String url) throws IOException {
      Request request = new Request.Builder()
          .url(url)
          .build();
    
      Response response = client.newCall(request).execute();
      return response.body().string();
    }
    
    1. 构造一个 OkHttpClient
    2. 构造一个 Request
    3. 调用 OkHttpClient.newCall(Request request) 获得一个 Call 对象
    4. 执行 Call.execute() 获得 Response 对象
    5. 通过 Response.body() 获得 ResponseBody 对象

    OkHttpClient 创建 http 请求源码分析。

    OkHttpClient 和 OkHttpClient.Builder

    OkHttpClient 对象的创建使用了『建造者模式』

        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;
        }
    

    OkHttpClient.Builder 主要用来设置超时时间、代理、缓存、拦截器等。

    然后调用

        public OkHttpClient build() {
            return new OkHttpClient(this);
        }
    

    创建 OkHttpClient

     OkHttpClient(Builder builder) {
        this.dispatcher = builder.dispatcher;
        this.proxy = builder.proxy;
        this.protocols = builder.protocols;
        this.connectionSpecs = builder.connectionSpecs;
        this.interceptors = Util.immutableList(builder.interceptors);
        this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
        this.eventListenerFactory = builder.eventListenerFactory;
        ……
    }
    
    Request 和 Request.Builder

    Request 同样也是使用『建造者模式』

    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;
    }
    

    Request 主要为了设置 url 、请求方法(GET、POST等)、headers、请求体。

    其中有个 tag 比较特殊

        /**
         * Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
         * is unspecified or null, the request is canceled by using the request itself as the tag.
         */
        public Builder tag(Object tag) {
            this.tag = tag;
            return this;
        }
    

    根据注释可以看出 tag 主要用来取消请求。

    如果发起 POST 请求,需要使用一个 RequestBody

    okhttp_01.png

    RequestBody 主要用来设置不同的 POST 请求内容(字节流、文件、字符串)

    分析 Call 对象
    client.newCall(request)
    

    OkHttpClient 的 newCall

    @Override
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
    }
    

    发现请求代理给了 RealCall

    static RealCall newRealCall(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;
    }
    

    看下 RealCall 的构造函数

    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        this.client = client;
        this.originalRequest = originalRequest;
        this.forWebSocket = forWebSocket;
        this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
    }
    
    获得 Call.execute() 和 Call.enqueue(Callback responseCallback)

    Call.execute() 负责同步请求。

    Call.enqueue(Callback responseCallback) 负责异步请求。

    @Override
    public Response execute() throws IOException {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        try {
            client.dispatcher().executed(this);
            Response result = getResponseWithInterceptorChain();
            if (result == null) throw new IOException("Canceled");
            return result;
        } finally {
            client.dispatcher().finished(this);
        }
    }
    
    @Override
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        captureCallStackTrace();
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    

    从上面代码可以看出 Call 对象会交给 Dispatcher 对象进行管理。

    executed() 方法会把 Call 对象存放在 runningSyncCalls 队列

    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
    }
    

    enqueue() 方法会把 Call 对象存放在 runningAsyncCalls 队列,如果队列已满则会被存放在 readyAsyncCalls 队列

    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
        } else {
            readyAsyncCalls.add(call);
        }
    }
    

    然后会执行到 getResponseWithInterceptorChain

    Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(retryAndFollowUpInterceptor);
        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);
    }
    

    这里可以看到添加了一些列的 Interceptor 对象。这些 intercept 分别负责网络请求、缓存、压缩等功能。

    而这些 intercept 组合的方式就是『责任链模式』,而最后一个 CallServerInterceptor 会真正发起网络请求。

    1. 首先会创建一个 RealInterceptorChain ,传入所有的 Interceptor,index = 0
    2. 然后执行 RealInterceptorChain.proceed(Request request)
    3. 再调用 RealInterceptorChain.proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
       RealConnection connection)
    4. 创建一个新的 RealInterceptorChain ,传入 index 值 +1,对象名称为 next
    5. 获取 interceptors 中 index 位置的 Interceptor,调用 Interceptor.intercept(next)
    6. 在 interceptors 中添加的各种 Interceptor 的 intercept 中都会如下
        public Response intercept(Chain chain) throws IOException {
            ……
            chain.proceed(requestBuilder.build());
            // or
            realChain.proceed(request, streamAllocation, null, null);
            ……
        }
    7. 其中 Chain.proceed() 方法又会重复执行 3、4、5、6 步骤,直到所有的 interceptors 被遍历。
    8. 最后添加的 ConnectInterceptor 和 CallServerInterceptor 是发起网络请求的关键
    

    总结以上流程如下

    okhttp_02.png

    发起网络请求

    先看 ConnectInterceptor

    @Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        StreamAllocation streamAllocation = realChain.streamAllocation();
    
        // We need the network to satisfy this request. Possibly for validating a conditional GET.
        boolean doExtensiveHealthChecks = !request.method().equals("GET");
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
    
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
    

    其中 StreamAllocation 对象由 RetryAndFollowUpInterceptor 创建并传入到『责任链』中。

    public Response intercept(Chain chain) throws IOException {
        ……
        streamAllocation = new StreamAllocation(client.connectionPool(), createAddress(request.url()),
                call, eventListener, callStackTrace);
    
        ……
                response = realChain.proceed(request, streamAllocation, null, null);
        ……
        }
    }
    

    可以看出 ConnectInterceptor 主要作用就是通过 StreamAllocation 创建了一个 HttpCodec。

    public HttpCodec newStream(
            OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
        int connectTimeout = chain.connectTimeoutMillis();
        int readTimeout = chain.readTimeoutMillis();
        int writeTimeout = chain.writeTimeoutMillis();
        boolean connectionRetryEnabled = client.retryOnConnectionFailure();
    
        try {
            RealConnection existingConnection = connection;
    
            RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
                    writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
            HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
    
            if (existingConnection != connection) {
                eventListener.connectionAcquired(call, connection);
            }
    
            synchronized (connectionPool) {
                codec = resultCodec;
                return resultCodec;
            }
        } catch (IOException e) {
            throw new RouteException(e);
        }
    }
    

    通过上面代码我们可以看出虽然返回的只是一个 HttpCodec 但是还会创建一个 RealConnection 。而 RealConnection 则是负责连接服务器发送请求的类。

    findHealthyConnection() 方法会调用 findConnection() 方法

    private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
                                          boolean connectionRetryEnabled) throws IOException {
        ……
        RealConnection result;
        ……
    
        // Do TCP + TLS handshakes. This is a blocking operation.
        result.connect(
                connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, call, eventListener);
        ……
    }
    

    并且调用 RealConnection 的 connect() 方法进行连接

    public void connect(int connectTimeout, int readTimeout, int writeTimeout,
                        boolean connectionRetryEnabled, Call call, EventListener eventListener) {
        ……
                    connectSocket(connectTimeout, readTimeout, call, eventListener);
        ……
    }
    
    private void connectSocket(int connectTimeout, int readTimeout, Call call,
                               EventListener eventListener) throws IOException {
        Proxy proxy = route.proxy();
        Address address = route.address();
    
        rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
                ? address.socketFactory().createSocket()
                : new Socket(proxy);
    
        eventListener.connectStart(call, route.socketAddress(), proxy);
        rawSocket.setSoTimeout(readTimeout);
        try {
            Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
        }
        ……
        
        try {
            source = Okio.buffer(Okio.source(rawSocket));
            sink = Okio.buffer(Okio.sink(rawSocket));
        } ……
    }
    

    这里可以看出 connect() 方法会简历一个 Socket 连接,并把 Socket 的输入/输出流交包装成 Okio 的 Source 和 Sink 对象。

    然后到 CallServerInterceptor 中

    public Response intercept(Chain chain) throws IOException {
        ……
        Response.Builder responseBuilder = null;
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
            ……
            //写入 request body
            ……
        }
    
        httpCodec.finishRequest(); // 通过 Socket OutputStream 发送请求
    
        ……
        if (responseBuilder == null) {
            realChain.eventListener().responseHeadersStart(realChain.call());
            responseBuilder = httpCodec.readResponseHeaders(false);
        }
        Response response = responseBuilder
                .request(request)
                .handshake(streamAllocation.connection().handshake())
                .sentRequestAtMillis(sentRequestMillis)
                .receivedResponseAtMillis(System.currentTimeMillis())
                .build();
    
        ……
        return response;
    }
    

    总计流程图如下

    okhttp_03.png okhttp_04.png

    参考资料

    OkHttp

    拆轮子系列:拆 OkHttp

    Okio 框架源码学习

    相关文章

      网友评论

        本文标题:okHttp 框架源码学习

        本文链接:https://www.haomeiwen.com/subject/jyyzyxtx.html