美文网首页Android进阶之路Android技术知识Android开发
OkHttp3源码解析(一)——整体框架

OkHttp3源码解析(一)——整体框架

作者: 码农翻身记 | 来源:发表于2019-03-28 08:48 被阅读294次
    好一番春意盎然,暖暖的阳光底下,百合嫩芽破土而出,月季花早已红颜娇滴。

    目录

    目录

    一 、简介及框架

    OkHttp是安卓端最火热的轻量级网络框架,由移动支付Square公司开源,用于替代HttpUrlConnection和Apache的HttpClient,事实上,Android4.4开始,google已经开始将源码中的HttpURLConnection替换为OkHttp,Android6.0里已移除HttpClient。由于该框架功能强大,而应用非常简单,越来越多的安卓开发者应用了该框架。
    具有如下的功能和特性:
    1、支持同步和异步请求,支持get和post请求,支持上传和下载文件,加载图片;
    2、支持Https,Http 1.0 1.1 2.0, websocket, SPDY等协议,3.7版本开始剥离SPDY,转而大力支持 http2.0
    3、支持Http缓存,避免重复请求;
    4、内部维护连接池,支持多路复用,减少连接创建开销;
    5、无缝支持Gzip压缩,减少数据流量;
    6、内部维护任务队列线程池,友好支持并发访问,最大并发64,单个host最大5个;
    7、socket创建支持最佳路由;
    8、服务器配置多IP情况下,当前IP请求失败,支持自动切换到其他IP;
    9、使用Okio来简化书看的访问和存储,提高性能;
    10、OkHttp还处理了代理服务器问题和SSL握手失败问题。

    整体框架图

    上图是OkHttp的总体架构,大致可以分为以下几层:
    Interface——接口层:接受网络访问请求
    Protocol——协议层:处理协议逻辑
    Connection——连接层:管理网络连接,发送新的请求,接收服务器访问
    Cache——缓存层:管理本地缓存
    I/O——I/O层:实际数据读写实现
    Inteceptor——拦截器层:拦截网络访问,插入拦截逻辑

    二 、使用方法

    定义网络管理单例,全局只声明一次OkHttpClient 。

    private OkHttpClient mClient;
    /**
    * 异步请求
    *
    * @param request
    * @param callback
    */
    public void sendAsyncRequest(Request request, Callback callback) {
         mClient.newCall(request).enqueue(callback);
    }
    /**
    * 同步请求
    * @param request
    * @return
    */
    public Response sendSyncRequest(Request request) {
         Response response = null;
         try {
              response = mClient.newCall(request).execute();
         } catch (Exception e) {
         }
         return response;
    }
    

    业务层要发请求调用的示例:

    //构建请求对象
    Request request = new Request.Builder().url("https://baidu.com").build();
    //异步请求
    NetManager.getInstance().sendAsyncRequest(request, new Callback() {
          @Override
          public void onFailure(Call call, IOException e) {
          }
          @Override
          public void onResponse(Call call, Response response) throws IOException {
          }
    });
    //同步请求
    Response response = NetManager.getInstance().sendSyncRequest(request);
    

    三、调用流程分析

    OkHttp由OkHttpClien对外提供统一接口,支持同步和异步请求,无论哪种请求,都会封装为RealCall对象,再调用RealCall的同步或异步方法,并最终都会调用getResponseWithInterceptorChain执行请求任务,不同的同步的请求会直接调用该方法,而异步请求会再封装为AsynCall,再将其加入Dispatcher的任务队列,Dispatcher则维护任务调度线程池,对异步请求任务进行分发,当任务执行时会在新线程里调用RealCall的getResponseWithInterceptorChain.具体调度流程如下图所示:

    调用流程

    同步流程:

    OkHttpClient:
    @Override 
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
     }
    
    RealCall:
    @Override 
    public Response execute() throws IOException {
        synchronized (this) {
           if (executed) throw new IllegalStateException("Already Executed");
           executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        try {
          //加入同步请求队列
          client.dispatcher().executed(this);
          //直接发送请求并获取服务器返回
          Response result = getResponseWithInterceptorChain();
          if (result == null) throw new IOException("Canceled");
          return result;
        } catch (IOException e) {
          eventListener.callFailed(this, e);
          throw e;
        } finally {
          //从同步队列移除
          client.dispatcher().finished(this);
        }
    }
    
    Dispatcher:
    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
    }
    

    异步请求流程:

    OkHttpClient:
    @Override 
    public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
     }
    
    RealCall:
    @Override public void enqueue(Callback responseCallback) {
        synchronized (this) {
           if (executed) throw new IllegalStateException("Already Executed");
           executed = true;
        }
        captureCallStackTrace();
        eventListener.callStart(this);
        //先构造AsyncCall对象,加入分发器的异步任务队列
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }
    
    Dispatcher:
    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          //并发请求数在64个内,且单host并发数5个内,加入异步执行队列,放入线程池
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          //已达上限则加入等待队列
          readyAsyncCalls.add(call);
        }
    }
    

    无论是同步还是异步请求,完成后都会调用Dispatcher的finish,从队列移除:

    Dispatcher:
    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!");
          //异步请求调finish后需要执行promoteCalls,优化调度器,处理等待队列中的任务
          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<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.
        }
    }
    

    从上面流程可见,真正处理请求的核心方法是getResponseWithInterceptorChain,该方法主要处理一系列的Interceptor,
    代码如下:

    RealCall:
    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());
        //调用proceed按顺序执行每个拦截器
        return chain.proceed(originalRequest);
      }
    }
    

    拦截器处理的流程如下图所示:


    一组拦截器

    RetryAndFollowUpInterceptor:在网络请求失败后进行重试,当服务器返回重定向时直接发起新的请求,并在允许情况下复用当前连接。
    BridgeInterceptor:处理头部协议内容,包括设置请求内容长度,编码格式,gzip压缩,并对响应内容进行解压,添加cookie,还有User-Agent,Host,Keep-Alive等。
    CacheInterceptor:负责缓存的管理,如果有符合条件的缓存则直接返回,当服务器返回有改变则更新当前缓存,缓存失效则删除。
    ConnectionInterceptor:从连接池中查找合适的连接,如果有合适的则复用已有的连接,没有则新建连接。
    CallServerInterceptor:负责向服务器发起真正的请求,并读取返回服务器的响应。

    四、拦截器原理

    OkHttp的Interceptor是一种典型的责任链模式,将每个处理单独包装为一个个独立的Interceptor,一个完整的请求由执行一组Interceptor来完成。下面分析下Interceptor的具体原理,核心代码如下:

    Interceptor:
    public interface Interceptor {
       /*
       * 拦截器要处理的逻辑
       * 除了最后一个拦截器,其他的都会调用chain.proceed,继续处理下一个拦截器
       * 等待下一个拦截器返回Response 
       */
      Response intercept(Chain chain) throws IOException;
    
      interface Chain {
        Request request();
        /*
        * 从下一个拦截器开始构造链,然后执行当前拦截器
        */
        Response proceed(Request request) throws IOException;
        ...
    }
    

    以RetryAndFollowUpInterceptor为例,intercept的处理流程是:

    RetryAndFollowUpInterceptor:
    @Override public Response intercept(Chain chain) throws IOException {
         ...
         try {
            //处理下一个拦截器,等待返回response 
            response = realChain.proceed(request, streamAllocation, null, null);
            releaseConnection = false;
          } catch (RouteException e) {
          }
          ...
    }
    

    chain.proceed的核心逻辑如下:

    RealInterceptorChain:
    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
          RealConnection connection) throws IOException {
    {
        ...
        //从下一个拦截器开始构造链
        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);
        ...
    }
    

    拦截器链的执行流程如图:


    拦截器链流程

    五、总结

    本文介绍了okhttp具有的功能特性,简单调用方法,包括异步和同步请求,分析了总体的框架层次,然后从源码角度分析了异步和同步请求任务的调度流程,执行的流程会通过若干个拦截器完成请求各个阶段的逻辑处理,说明了各个拦截器的功能,最后分析了拦截器的源码及原理,通过本文能对okhttp有整体的了解。

    公众号

    相关文章

      网友评论

        本文标题:OkHttp3源码解析(一)——整体框架

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