• 03、Android进阶OkHttp原理解析


    OkHttp原理

    这里主要解析OkHttp请求网络流程和复用连接池。

    OkHttp请求网络流程

    整体的结构图如下所示:

    (1)从请求处理开始分析

    当我们要请求网络的时候需要用OkHttpClient.newCall(request)进行execute或者enqueue操作;当调用newCall方法时,会调用如下代码:

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

    我们调用enqueue异步请求网络实际上是调用了RealCall的enqueue方 法。查看newRealCall方法如下:

    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对象
    }
    

    从代码中可以看出,我们调用的enqueue方法其实是调用RealCall的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 RealCall.AsyncCall(responseCallback));
    }
    

    可以看到最终的请求是dispatcher来完成的,接下来就开始分析dispatcher。

    (2)Dispatcher任务调度

    Dispatcher主要用于控制并发的请求,它主要维护了以下变量:

    public final class Dispatcher {
      /** 最大并发请求数 */
      private int maxRequests = 64;
      /** 每个主机最大请求数 */  
      private int maxRequestsPerHost = 5;
      private @Nullable Runnable idleCallback;
      /** 消费者线程池 */
      private @Nullable ExecutorService executorService;
      /** 将要运行的异步请求队列 */
      private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
      /** 正在运行的异步请求队列 */
      private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
      /** 正在运行的同步请求队列 */
      private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
      ......  
    }    
    

    接下来看看Dispatcher的构造方法,如下所示:

    public Dispatcher(ExecutorService executorService) {
        this.executorService = executorService;
    }
    
    public Dispatcher() {
    }
    
    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;
    }
    

    Dispatcher有两个构造方法,可以使用自己设定的线程池。如果没有设定线程池,则会在请求网络前自己创建默认线程池。

    当调用 RealCall 的 enqueue 方法时,实际上是调用了 Dispatcher的enqueue方法,它的代码如下所示:

    synchronized void enqueue(RealCall.AsyncCall call) {
        // 当正在运行的异步请求队列中的数量小于64并且正在运行的请求主机数小于5时
        // 把请求加载到 runningAsyncCalls中并在线程池中执行,否则就加入到
        // readyAsyncCalls中进行缓存等待。
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
        } else {
            readyAsyncCalls.add(call);
        }
    }
    

    线程池中传进来的参数是AsyncCall,它是RealCall的内部类,其内部也实现了execute方法,如下所示

    @Override
    protected void execute() {
        boolean signalledCallback = false;
        try {
            // 返回了Response,很明显这是在请求网络
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
                signalledCallback = true;
                responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
                signalledCallback = true;
                responseCallback.onResponse(RealCall.this, response);
            }
        } catch (IOException 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);
        }
    }
    

    finished方法如下所示:

    private <T> void finished(Deque<T> 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();
        }
    }
    

    finished方法将此次请求从runningAsyncCalls移除后还执行了promoteCalls方法:

    private void promoteCalls() {
        if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
        if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    	// 从readyAsyncCalls取出下一个请求,加入runningAsyncCalls中并交由线程池处理。
        for (Iterator<RealCall.AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            RealCall.AsyncCall call = i.next();
            if (runningCallsForHost(call) < maxRequestsPerHost) {
                i.remove();
                runningAsyncCalls.add(call);
                executorService().execute(call);
            }
    
            if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
        }
    }
    

    从readyAsyncCalls取出下一个请求,加入runningAsyncCalls中并交由线程池处理。

    (3)Interceptor拦截器

    接下来在RealCall的executor方法里查看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);
    }
    

    创建了一个拦截器队列 interceptors,然后将 interceptors 通过 RealInterceptorChain 类的实例对象来进行处理。接下来看看RealInterceptorChain的proceed方法:

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
                            RealConnection connection) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();
    	......
        RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
                connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
                writeTimeout);
        // 从拦截器列表中取出拦截器
        Interceptor interceptor = interceptors.get(index);
        // 调用intercept进行拦截操作
        Response response = interceptor.intercept(next);
    	......
        return response;
    }
    

    其中proceed方法主要作用就是依次取出拦截器链表中的每个拦截器去获取Response。

    这些拦截器包括:

    • 用户自定义的拦截器

    • retryAndFollowUpInterceptor:重试和重定向拦截器,主要负责网络失败重连。

    • BridgeInterceptor:主要负责添加交易请求头。

    • CacheInterceptor:缓存拦截器,主要负责拦截缓存。

    • ConnectInterceptor:网络连接拦截器,主要负责正式开启http请求。

    • CallServerInterceptor:负责发送网络请求和读取网络响应。

    总结:OKHttp中的一个网络请求其实就是5个拦截器依次执行自己的intercept方法的过程

    在拦截器链中执行的结果,在同步请求中会直接在response返回,异步请求:

    
    final class RealCall implements Call {
    ...
      final class AsyncCall extends NamedRunnable {
        ...
        @Override 
        public Response execute() throws IOException {
        ...
            responseCallback.onResponse(RealCall.this, response);
        ...
        }
    ...
    
    

    异步请求时会把拦截器链的处理结果通过Callback的onReponse回调给用户。

  • 相关阅读:
    分段控制器UISegmentedControl的使用、同一个控制器中实现多个View的切换、addChildViewController等方法的使用
    警示框UIAlertController的使用(看完马上会用!!)
    断言NSAssert的使用
    概念篇(一)
    《iOS开发进阶》书籍目录
    《编写高质量iOS与OS X代码的52个有效方法》书籍目录
    《精通Objective-C》书籍目录
    《iOS设计模式解析》书籍目录
    《精通iOS开发》书籍目录
    常用的代码块
  • 原文地址:https://www.cnblogs.com/pengjingya/p/14948157.html
Copyright © 2020-2023  润新知