Android 经常使用开源框架源码解析 系列 (一)网络框架之一 OkHttp

OkHttp
 
做用
  •     处理网络请求
  •     平时处理Http请求更加快速
  •     流量更加节省
 
1、流程图
 
    (1)建立OkHttpClient 对象;只会建立一次并做为全局进行保存的单例对象通常。
    (2)建立Request 对象;封装一些请求报文信息,好比请求的头信息,url地址,请求的类型方法等
                ps:经过build 构建者模式 链式生成Request对象
    (3)获得RealCall对象; 经过Client.newCall()方法获取call对象,readcall表明一个实际的http请求
                ps: 链接Request 和 Response 的一个桥梁;并将请求添加到dispatcher中
    (4)Dispatcher 分发器类 ***;-确认同步 or 异步
                ps:在这个类内部维护了一个线程池,这个线程池用于执行网络请求(同步/异步);当中经过三个队列维护池中的同步/异步请求
            不断的从Request请求队列当中获取须要的RealCall,根据是否调用缓存(Cache)来选择是直接Connect拦截器+CallServer仍是 Cache 拦截器
    (5)interceptors 拦截器 ***;进行服务器数据的获取,构建一个拦截器列,经过依次执行这个拦截器列中的每一个拦截器,将服务器得到的数据返回
               ps:不论同步操做仍是异步操做最终都会经过拦截器进行分发
        a、RetryAndFollow interceptors拦截器  ;网络请求失败后的重试,服务器返回当前request请求须要重定向的时候
        b、Bridge interceptors拦截器 ;设置request请求前的操做主要好比内容长度,编码,设置gzip压缩等内容拦截器同时能够添加cookie 
        c、Cache interceptors拦截器 ; 负责缓存的管理,能够直接返回cache给客户端
        d、Connect interceptors拦截器 ;为当前request请求找到一个合适的链接(能够复用已有的连接)
        e、CallServer interceptors拦截器 ;向服务器发起真正的网络请求,接受服务器返回数据并读取
 
——————————————————————————————————————
2、同步请求方法
简单步骤:
//一、建立OkHttpClient 请求客户端对象--单例对象建立
    OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
 
        public void synRequest() { //同步基本方法
    
//二、建立携带请求信息的Request对象
    Request request = new Request.Builder().url("http://www.baidu.com")
            .get().build();
    
//三、建立RealCall对象经过Client对象和Request对象构建实际的call对象;链接Request、Response桥梁
    Call call = client.newCall(request);//同步异步分水岭
    
//四、经过Call对象的execute获取Response对象
    try {   //RealCall其实是Call的具体实现类
        Response response = call.execute();//响应报文信息 -同步
        System.out.println("body:" + response.body().string());
    } catch (IOException e) {
        e.printStackTrace();
    }
同步 发送请求后,就会进入阻塞状态,直到收到数据响应(与异步最大的区别)
 
 
核心类:
    在getResponseWithInterceptorChian()方法中,构建一个拦截器的链,经过依次执行拦截器的链当中的每个不一样做用的拦截器来获取服务器的数据返回。
 
源码分析 核心类:
(1)new OkHttpClient.Builder():
    Builder() :须要不少参数的设置的时候考虑的方法
public Builder() {
  dispatcher= new Dispatcher(); (1)
  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();(2)
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
  pingInterval = 0;
}
(1)Http请求重要的分发器,尤为肯定异步请求是缓存等待仍是直接处理;针对同步请求就是直接把request放入队列当中;
(2)connectionPool,链接池;
    尤为进行统一管理客户端和服务器端的链接,当请求的URL是相同的时候能够选择复用;
    实现哪些网络链接是打开状态,哪些是复用的策略的设置;
 
(2)new Request.Builder(). …. .build():
    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }
  • 默认的链接方式:get请求
  • 保存一些头部信息
//建立一个Request对象 把当前的build对象传递回去(把以前配置好的信息赋值给这个Request对象
好比url, method,headers,body,tag)
public Request build() {
  if (url == null) throw new IllegalStateException("url == null");
  return new Request(this); 
}
 
(3)client.newCall(request): //不论同步仍是异步都会调用该方法
Call是个接口 其实现都在newRealcall() 这个实际的实现类当中
@Override 
    public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}
 
(4)newRealcall():
//建立一个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;
}
 
(5)RealCall():
一、持有了Client 和Request对象 二、赋值了一个重定向拦截器
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  this.client = client;
  this.originalRequest = originalRequest;
  this.forWebSocket = forWebSocket;
  this.retryAndFollowUpInterceptor= new RetryAndFollowUpInterceptor(client, forWebSocket);
}
 
(6)Realcall的execute():
    @Override 
    public Response execute() throws IOException {
//一、在同步代码块 当中首先判断executed 标志位 是否为 true
    同一个http请求只能执行一次,没有执行过就会把executed设置为true,若执行过抛异常
         synchronized (this) {
                if (executed) throw new IllegalStateException("Already Executed");
                    executed = true;
              }
    //二、捕捉异常信息
          captureCallStackTrace();
    //三、每当调用call或是enqueue 就会开启这个监听器
          eventListener.callStart(this);
              try {
    //四、返回一个dispatcher对象操做
                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的做用:
    一、维持call请求发给它的状态 
    二、维护一个线程池用于执行网络请求
    call请求在执行任务的时候经过上面的dispatcher分发器类,将任务推到执行队列当中进行操做,操做完成后,再执行下面等待队列的任务
 
ps:
    (7)client.dispatcher().executed(this):————>将call请求添加到request请求队列当中
synchronized void executed(RealCall call) {
  runningSyncCalls.add(call);
}
 
在这里要先提一下 ,在dispatcher类中定义了3个队列 
/** Ready async calls in the order they'll be run. */  
异步等待就绪队列
private final Deque<AsyncCall> readyAsyncCalls= new ArrayDeque<>();
 
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ 
异步正在执行队列
private final Deque<AsyncCall> runningAsyncCalls= new ArrayDeque<>();
 
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
同步正在执行队列
private final Deque<RealCall> runningSyncCalls= new ArrayDeque<>();
 
 
(8)getResponseWithInterceptorChain():拦截器链 方法 在内部依次调用拦截器进行操做
后面会注重解析该方法
    finally {
      (9) client.dispatcher().finished(this);
     }
主动回收一些请求
 
(10)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;
              }
//正在执行的请求数量为0,同时知足idleCallback不为空
          if (runningCallsCount == 0 && idleCallback != null) { 
                idleCallback.run();
          }
}
 
——————————————————————————————————————
3、异步请求方法
简单步骤:
public void asynRequest() {
    Request request_ays = new Request.Builder().url("http://www.baidu.com")
            .get().build();
    //将Request封装成Call对象
    Call call_ays = client.newCall(request_ays);
    call_ays.enqueue(new Callback() {//开启一个工做线程执行下面的方法
        @Override
        public void onFailure(Call call, IOException e) {
切记更新UI操做要在主线程而不是在如今的子线程中执行
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
切记更新UI操做要在主线程而不是在如今的子线程中执行
        }
    });
}
Dispatcher 分发器类,有分发器来选择是异步请求执行仍是就绪准备
 
源码分析 核心类:
前三步没有真正的发起网络请求,步骤同理 - 同步请求 
    call_ays.enqueue(new Callback() :
//四、传递一个Callback对象,请求结束之后的接口回调
 
@Override public void enqueue(Callback responseCallback) {
  //一、锁住当前的RealCall对象, 并对executed进行判断,有没有执行过,执行过就抛出异常
          synchronized (this) {
                if (executed) throw new IllegalStateException("Already Executed");
                    executed = true;
          }
      captureCallStackTrace();
      eventListener.callStart(this);
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
 
//二、将responseCallback对象封装成AsyncCall对象
实际上AsyncCall 由于继承自Runnable,因此能够把其想象成就是Runnable
final class AsyncCall extends NamedRunnable {
  private final Callback responseCallback;
      AsyncCall(Callback responseCallback) {
        super("OkHttp %s", redactedUrl());
        this.responseCallback = responseCallback;
  }
 
//三、调用拦截器的enqueue()方法进行异步网络请求
client.dispatcher().enqueue():
dispatcher已经在构建client的时候进行了初始化赋值操做!!!
 重要方法!!!!!!!!!!  -———————————>
    synchronized void enqueue(AsyncCallcall) {
//a、runningAsyncCalls的数量是否小于最大请求数量(64)
        同时 正在运行的主机的请求是否小于设定好的主机请求的最大值(5)
     if (runningAsyncCalls.size() < maxRequests 
                        && runningCallsForHost(call) < maxRequestsPerHost) {
//b、知足条件把当前的异步请求添加到正在执行的异步请求队列当中  
        runningAsyncCalls.add(call);
//c、经过线程池执行异步请求
        executorService().execute(call);
              } else {
//d、添加到等待就绪队列当中
                readyAsyncCalls.add(call);
             }
}
 
executorService():线程池
//使用synchronized关键字锁对象,保证ExecutorService线程池是单例的
public synchronized ExecutorService executorService() {
  if (executorService == null) {
        //ThreadPoolExecutor (corePoolSize,最大线程池数量,)
    executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
  }
      return executorService;
}
 
AsyncCall():
前面提到AsyncCall继承自NamedRunnable,那这个方法作了什么呢?
    asyncCall自身没有run()方法,是在Runnable()方法中的execute()方法中执行的,因此具体的实现应该在AsyncCall的execute()方法中实现的
public abstract class NamedRunnable implements Runnable {
  protected final String name;
  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }
  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute(); //核心方法
    } finally {
      Thread.currentThread().setName(oldName);
        }
     }
  protected abstract void execute();
}
 
execute():
@Override protected void execute() {
  boolean signalledCallback = false;
  try {
   //一、构建一个拦截器的链返回一个respone对象,每个拦截器中的链做用不一样。
    Response response = getResponseWithInterceptorChain();
      //二、判断 重定向重试拦截器是否被取消了-在子线程执行!
  if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
    //2.一、取消了 ,则调用call.callback的onFailure方法 回调失败的提示
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    } else {
    //2.2 、没有取消则调用onResponse()的回调,真正发送网络请求的就是onResponse()
          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分发器
    client.dispatcher().finished(this);
  }
}
 
finished(runningAsyncCalls, call, true);  ******* 重要方法
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
  int runningCallsCount;
  Runnable idleCallback;
  synchronized (this) {
    //一、经过call.remove把这个请求从正在执行的异步请求队列中删除
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!”);
    //二、调整整个异步请求的任务队列,出于线程安全考虑用sy
    if (promoteCalls) promoteCalls();
     //三、 从新计算线程数量并赋值
        runningCallsCount = runningCallsCount();
        idleCallback = this.idleCallback;
      }
      if (runningCallsCount == 0 && idleCallback != null) {
        idleCallback.run();
      }
}
 
——————————————————————————————————————
4、同步/异步请求方法差别 及任务调度dispatcher
  • 发起请求的方法调用 
  1.         同步是execute()
  2.         异步是enqueue(),传入callback对象
  • 阻塞线程是否
 
任务调度dispatcher
 
思考1:okHttp如何实现同步异步请求?
    答: 发送的同步/异步 请求 最后都会在dispatcher中 管理其请求队列状态!
 
思考2:什么是dispatcher
    答:dispatcher 的做用为了
  •     维护请求的状态(同 or 异)
  •     维护一个线程池,用于执行请求;把请求推送到请求队列
 
 
在dispatcher类中定义了3个队列 
 
维护了一个线程池
private @Nullable ExecutorService executorService;
 
维护一个等待就绪的异步执行队列1 ,不知足条件的时候请求进入该队列进行缓存
/** Ready async calls in the order they'll be run. */  异步的就绪队列
private final Deque<AsyncCall> readyAsyncCalls= new ArrayDeque<>();
 
维护一个正在执行的异步请求队列2 ,包含了已经取消但没有执行完的请求
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ 
private final Deque<AsyncCall> runningAsyncCalls= new ArrayDeque<>();
 
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */同步的执行队列
private final Deque<RealCall> runningSyncCalls= new ArrayDeque<>();
 
 
思考3:异步请求为何须要两个队列-想象成生产和消费者模型
dispatcher :生产者  默认运行在主线程
ExecutorService :消费者池
  •      Deque<AsyncCall> runningAsyncCalls
  •      Deque<AsyncCall> readyAsyncCalls
 
 
每当有新的异步请求经过call.enqueue的时候,经过2个条件判断是否进入正在执行队列仍是等待队列;
 
条件:runningAsyncCalls的数量是否小于最大请求数量(64)同时 正在运行的主机的请求是否小于设定好的主机请求的最大值(5)
 
promoteCalls() 手动清除缓存区
===============================dispatch核心
A、同步——请求的executed(): 
    每当有新的请求来了就直接加入 同步请求队列
/** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
      runningSyncCalls.add(call);
}
B、异步——请求的executed(): 判断条件
核心:
    这个call实际上就是一个runnable对象,不知足条件就放在就绪异步等待队列当中。正在请求的异步队列有空闲位置的时候会从就绪缓存等待中取出优先级高的请求加入队列进行执行
    条件知足 将封装好的call添加到正在执行的异步队列中而后交给线程池管理执行
synchronized void enqueue(AsyncCall call) {
  if (runningAsyncCalls.size() < maxRequests 
        && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
  } else {
        readyAsyncCalls.add(call);
      }
}
 
executorService():线程池
//使用synchronized关键字锁对象,保证ExecutorService线程池是单例的
public synchronized ExecutorService executorService() {
  if (executorService == null) {
       //ThreadPoolExecutor (corePoolSize,最大线程池数量,)
    executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
  }
      return executorService;
}
 
ThreadPoolExecutor(参数)
    corePoolSize:0 的意义 
        若是空闲一段时间后,就会自动销毁全部线程池中的线程!
    Integer.MAX_VALUE:最大线程数
        当请求到来时候能够无限扩充请求建立线程,这个无限不大于64
    keepAliveTime
        当线程数量大于核心线程数的时候,多于的线程最大的存活时间!!
这个方法的意义:
    在程序中开启了多个并发的请求,线程池会建立N个线程。当工做完成后,线程池会在60秒后相继关闭全部无用的线程
 
思考4: readyAsyncCalls队列中的线程在何时才会被执行呢?
    finally {
      client.dispatcher().finished(this);
    }
 
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        ...
  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();
  }
}
 
promoteCalls():对等待的异步请求缓存队列进行调度
private void promoteCalls() {
        // Already running max capacity.
  if (runningAsyncCalls.size() >= maxRequests)  return; 
       // No ready calls to promote.
  if (readyAsyncCalls.isEmpty())  return; 
    //一、对原来的等待缓存的异步请求队列进行遍历
  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
    AsyncCall call = i.next();
        //二、把它最后一个元素取出并移除后,加入到runningAsyncCalls 中
    if (runningCallsForHost(call) < maxRequestsPerHost) {
      i.remove();
      runningAsyncCalls.add(call);
      executorService().execute(call);
    }
 
    if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
  }
}
 
——————————————————————————————————————
5、拦截器 Interceptor
 
    拦截器是OkHttp中提供一种强大的机制,它能够实现 网络监听、请求 以及响应重写、请求失败重试等功能。(不区分同步/异步)
内部拦截器:
  • 定向拦截器
  • 桥接适配拦截器
  • 缓存拦截器
  • 链接管理拦截器
  • 网络IO流数据读写拦截器
外部拦截器:
  • App拦截器client.interceptors
  • 网络拦截器networkInterceptors
 
 
getResponseWithInterceptorChain() :返回网络响应
ps:在方法内构成一个拦截器的链,经过依次执行每一个不一样功能的拦截器来获取服务器的响应返回:
 
Response getResponseWithInterceptorChain() throws IOException {
      // Build a full stack of interceptors.
  List<Interceptor> interceptors = new ArrayList<>();
//一、建立一系列拦截器,而后将建立的拦截器放入到interceptors 集合当中
  interceptors.addAll(client.interceptors());  //添加app 拦截器
 
  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));
//二、将集合封装到RealIntercptorChain 链当中
  Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
      originalRequest, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis() );
    //依次执行拦截器链的proceed()方法
  return chain.proceed(originalRequest);
}
 
//三、核心功能:建立下一个拦截器链
@Override public Response proceed(Request request) throws IOException {
  return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
    RealConnection connection) throws IOException {
        …
// Call the next interceptor in the chain.
访问的话须要从下一个拦截器进行访问而不是从当前的拦截器
  RealInterceptorChainnext = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
      connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
      writeTimeout);
  Interceptor interceptor = interceptors.get(index);
将获取的拦截器链传入,构成一个完整的链条
 Response response = interceptor.intercept(next);
  // Confirm that the next interceptor made its required call to chain.proceed().
  if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
  }
  // Confirm that the intercepted response isn't null.
  if (response == null) {
    throw new NullPointerException("interceptor " + interceptor + " returned null");
  }
  if (response.body() == null) {
    throw new IllegalStateException(
        "interceptor " + interceptor + " returned a response with no body");
  }
  return response;
}
 
ps:简单的说把一个OkHttp的网络请求看做是一个一个拦截器执行Interceptor的方法的过程
 
小结:
    一、在发起请求前对request 进行处理
    二、调用下一个拦截器,获取response (持有链条的概念)
    三、对response进行处理,返回给上一个拦截器
 
——————————————————————
A、 RetryFollowUpInterceptor 重定向拦截器
流程:
(1)StreamAllocation
StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
    createAddress(request.url()), call, eventListener, callStackTrace);
this.streamAllocation = streamAllocation;
 
StreamAllocation对象,创建Http请求所须要的全部网络组件;分配stream
做用:
获取连接服务器端的connection连接和链接数据传输的输入输出流
ps:真正使用该对象的是ConnectInterceptor
 
(2) 调用RealInterceptorChain.proceed()进行实际的网络请求
如何进行拦截操做的呢?
(3)根据异常结果或是响应结果判断是否要进行从新请求
核心操做:
一、对重试的次数进行判断 超过20次就不会再请求而是释放
    if (++followUpCount > MAX_FOLLOW_UPS) {
      streamAllocation.release();
        throws ...
    } ——20次
 
(4)调用下一个拦截器,对response进行处理,返回给上一个拦截器
++++++++++++++++++++++++++++++++++++++++
 
B、BridgeInterceptor 桥接拦截器
做用:
  •  设置内容长度、编码方式、压缩方式等的头部信息添加;
  •   keep-Alive操做保持链接复用的基础
 
keep-Alive:当开启一个TCP链接以后,他不会关闭它的链接,而是在必定时间内保持它的链接状态
 
//调用拦截器链的proceed()方法,得到服务器发回的响应以后把该响应返回给客户端
Response networkResponse = chain.proceed(requestBuilder.build());
//将从服务器返回的网络请求响应Response转化为用户能够读取并使用的response
HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
 
//核心转化功能:转化Response
if (transparentGzip
    && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
    && HttpHeaders.hasBody(networkResponse)) {
            ...
}
a、transparentGzip 为true 表示请求的客户端支持Gzip压缩
b、返回的Gzip是否符合头部信息的编码
c、判断头部信息是否含有body体
 
//Response的body体的输入流转化为GzipSource类型,为了使用者能够直接以解压的方式读取流数据
GzipSourceresponseBody = new GzipSource(networkResponse.body().source());
 
 
核心功能:
一、负责将用户构建的一个Request请求转化为可以进行网络访问的请求
二、将这个符合网络请求的Request进行网络请求
三、将网络请求回来的响应Response 转化为用户能够读取的Response
 
++++++++++++++++++++++++++++++++++++++++
C、CacheInterceptor 缓存拦截器
OkHttpClient client = new OkHttpClient.Builder()
       .cache(new Cache(new File("cache"),24*1024*1024))
        .readTimeout(5, TimeUnit.SECONDS).build();
param1 : 缓存目录 
param2:  缓存目录大小
 
internalCache全部的实现都是经过上面的cache类实现的
final InternalCache internalCache= new InternalCache() {
    //从缓存中读取响应体Response
  @Override public Response get(Request request) throws IOException {
    return Cache.this.get(request);
  }
  @Override public CacheRequest put(Response response) throws IOException {
    return Cache.this.put(response);
  }
}
 
CacheRequest put(Response response) {
    //一、经过Response响应信息获取对应Response对应的Request请求,再调用method方法获取requestMethod
 String requestMethod = response.request().method();
    
   // 二、若是不是get方法的话,就不进行缓存
     if (!requestMethod.equals("GET")) {…}
    
  //三、写入缓存的部分 Entry:OkHttp所须要的全部属性被封装成Entry类
     Entry entry = new Entry(response);
       
//四、最后缓存都会交给DisLruCahce算法来实现实际写入
        DiskLruCache.Editor editor = null;
ps:OkHttp内部因为维护着一个清理的线程池,由这个清理的线程池来实现对缓存文件的自动的清理和管理工做
 
//五、建立真正的editor ,经过传入key值获取完成的editor
    editor = cache.edit(key(response.request().url()));
key:将网络请求的url作MD5 加密处理 获得16进制表示形式 并转化为key
 
//六、真正的开始进行网络缓存操做,将缓存写入磁盘
        entry.writeTo(editor);
 
public void writeTo(DiskLruCache.Editor editor) throws IOException {
  BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));
 
//缓存一些请求的头部信息
  sink.writeUtf8(url)
      .writeByte('\n');
  sink.writeUtf8(requestMethod)
      .writeByte('\n');
  sink.writeDecimalLong(varyHeaders.size())
      .writeByte('\n');
  for (int i = 0, size = varyHeaders.size(); i < size; i++) {
    sink.writeUtf8(varyHeaders.name(i))
        .writeUtf8(": ")
        .writeUtf8(varyHeaders.value(i))
        .writeByte('\n');
  }
        …
   //缓存http的响应行 等信息
sink.writeUtf8(new StatusLine(protocol, code, message).toString())
    .writeByte('\n');
sink.writeDecimalLong(responseHeaders.size() + 2)
    .writeByte('\n');
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
  sink.writeUtf8(responseHeaders.name(i))
      .writeUtf8(": ")
      .writeUtf8(responseHeaders.value(i))
      .writeByte('\n');
    }
    …
   //判断是不是Https请求
    if (isHttps()) {
      sink.writeByte('\n');
         ...
    }
    sink.close();
}
//七、实际的响应主体 CahceInterceptor的实现类,经过该实现类实现数据的缓存和更新
   return new CacheRequestImpl(editor);
经过源码能够看到它是经过DisLruCache书写body的
CacheRequestImpl(final DiskLruCache.Editor editor) {}
 
——————
  //从缓存中读取响应体Response
  @Override public Response get(Request request) throws IOException {
    return Cache.this.get(request);
  }
DiskLruCache.Snapshot snapshot;
//一、目标缓存快照:记录缓存在某一个特定时间包含的内容
//MD5加密计算获得的Key值
snapshot = cache.get(key);
if (snapshot == null) {
  return null;
}
//二、经过key值将缓存的值保存到snapshot缓存快照中
//三、根据缓存快照建立entry缓存对象用于缓存数据
entry = new Entry(snapshot.getSource(ENTRY_METADATA));
//四、根据entry 获取响应的response对象
Response response = entry.response(snapshot);
//五、进行响应的匹配工做,若是不是一对一的话就返回null并关闭当前流
if (!entry.matches(request, response)) {
  Util.closeQuietly(response.body());
  return null;
}
 
public Response response(DiskLruCache.Snapshot snapshot) {
      …
    //一、根据头部信息建立cacheRequest请求
 Request cacheRequest = new Request
.Builder()
                                .url(url)
                                .method(requestMethod, null)
                                .headers(varyHeaders)
                                .build();
    //二、将响应的1步信息保存到entry当中
CacheResponseBody:响应体读取
return new Response.Builder()
    .request(cacheRequest)
    .protocol(protocol)
    .code(code)
    .message(message)
    .headers(responseHeaders)
    .body(new CacheResponseBody(snapshot, contentType, contentLength))
    .handshake(handshake)
    .sentRequestAtMillis(sentRequestMillis)
    .receivedResponseAtMillis(receivedResponseMillis)
    .build();
============================================
CacheInterceptor 缓存拦截器
@Override public Response intercept(Chain chain) throws IOException {
//一、经过get方法尝试获取缓存
      Response cacheCandidate = cache != null
          ? cache.get(chain.request())
          : null;
        …
//二、经过缓存策略的工厂类获取具体的缓存策略
   CacheStrategystrategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
//三、根据缓存策略获取其内部维护的response和request 
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
//四、当有缓存的时候,更新一下缓存的命中率
    if (cache != null) {
      cache.trackResponse(strategy);
    }
            …
//五、如当前不能使用网络又没有找到响应的缓存对象,经过构建者模式构建一个response 这会抛出一个504错误
if (networkRequest == null && cacheResponse == null) {
  return new Response.Builder()
      .request(chain.request())
      .protocol(Protocol.HTTP_1_1)
      .code(504)
      .message("Unsatisfiable Request (only-if-cached)")
      .body(Util.EMPTY_RESPONSE)
      .sentRequestAtMillis(-1L)
      .receivedResponseAtMillis(System.currentTimeMillis())
      .build();
}
//六、如有缓存且不能使用网络时候,直接返回缓存的结果
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }
//七、经过调用拦截器链的preceed方法来进行网络响应的获取,里面的工做交给下一个拦截器(Connectinterpreter)
    try {
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
       closeQuietly(cacheCandidate.body());
      }
//八、判断缓存响应是否为null 从缓存中读取数据
    if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        …
    }
//九、网络缓存保存判断
if (cache != null) {
    //判断http头部有没有响应体,而且缓存策略选择为能够被缓存
  if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
    // 将网络响应写入put到缓存当中
    CacheRequest cacheRequest = cache.put(response);
    return cacheWritingResponse(cacheRequest, response);
  }
 
//十、这个请求request是不是无效的缓存方法,若是是删除该request
if (HttpMethod.invalidatesCache(networkRequest.method())) {
  try {
    cache.remove(networkRequest);
  } catch (IOException ignored) {
    // The cache cannot be written.
  }
 
 
 
CacheStrategy 缓存策略 
//维护了一个网络请求networkRequest 和一个缓存响应 cacheResponse,内部经过制定以上两者来描述是经过何种方式获取Response
 
public final class CacheStrategy{
  /** The request to send on the network, or null if this call doesn't use the network. */
  public final @Nullable Request networkRequest;
  /** The cached response to return or validate; or null if this call doesn't use a cache. */
  public final @Nullable Response cacheResponse;
}
实际建立CacheStrategy对象的核心类:
private CacheStrategy getCandidate() {
   // 判断是否有缓存响应对象,若无建立新的对象传入请求request
  if (cacheResponse == null) {
    return new CacheStrategy(request, null);
  }
    // 判断请求是不是https,是否经历过握手操做
  if (request.isHttps() && cacheResponse.handshake() == null) {
    return new CacheStrategy(request, null);
  }
    //判断缓存是否该存储
if (!isCacheable(cacheResponse, request)) {
  return new CacheStrategy(request, null);
}    
    //请求当中制定不使用缓存响应或是进行了可选择的httpget请求,就从新创建请求
acheControl requestCaching = request.cacheControl();
if (requestCaching.noCache() || hasConditions(request)) {
  return new CacheStrategy(request, null);
}
    …
    //比较缓存的response,添加缓存头操做
Response.Builder builder = cacheResponse.newBuilder();
        …
    //返回一个CacheStrategy对象
 return new CacheStrategy (null, builder.build());   
        …
  }
 
 
++++++++++++++++++++++++++++++++++++++++
C、ConnectInterceptor 链接拦截器
 
做用:打开与服务器的链接,正式开网络启请求
@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
 
(1)  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);
    RealConnectionconnection = streamAllocation.connection();
 
 (2)  return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}
 
StreamAllocation:用来创建执行Http请求所须要的全部的网络的组件(初始化在重定向拦截器中)
HttpCodec:经过建立好的StreamAllocation建立HttpCodec的对象
ps:HttpCodec 是用来编码request 和解码 response 
RealConnection:实际用来进行网络IO传输的对象
 
(1) ConnectInterceptor 从拦截器链中获取到前面的Interceptor传过来给StreamAllocaiton这个对象,而后执行streamAllocation.newStream方法(),初始化HttpCodec对象。该对象用于处理Request和Response对象
(2) 将刚才建立的用于网络IO的RealConnection对象,以及对于与服务器交互最为关键的HttpCodec等对象传递给后面的拦截器
 
streamAllocation.newStream():
public HttpCodec newStream(OkHttpClient client, 
    Interceptor.Chain chain, boolean doExtensiveHealthChecks){
            ...
    try {
    //一、生成一个realConnection对象进行实际的网络链接
      RealConnectionresultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
    //二、经过获取到的realConnection对象生成一个HttpCodec对象
              HttpCodecresultCodec = resultConnection.newCodec(client, chain, this);
                        //同步代码块 返回一个HttpCodec对象
                  synchronized (connectionPool) {
                codec = resultCodec;
                return resultCodec;
              }
    } 
            …
}
findHealthyConnection():
开启一个while循环 调用findConnection进行二次封装继续获取RealConnection
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
            int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
            boolean doExtensiveHealthChecks) throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          pingIntervalMillis, connectionRetryEnabled);
 
//若是realConnection的对象成功数量==0就返回,意味着整个网络链接结束
      synchronized (connectionPool) {
            if (candidate.successCount == 0) {
              return candidate;
            }
      }
//若是这个candidate是不健康的就销毁资源操做
    if (!candidate.isHealthy(doExtensiveHealthChecks)) {
      noNewStreams();
      continue; //循环findConnection()获取对象
    }
 
findConnection():
   //一、尝试获取这个连接,若是能够复用connection 直接赋值给releasedConnection,而后判断这个可复用的connection是否为空,
不为空就把这个连接connection赋值给RealConnection对象
releasedConnection = this.connection;
toClose = releaseIfNoNewStreams();
if (this.connection != null) {
  // We had an already-allocated connection and it's good.
  result = this.connection;
  releasedConnection = null;
}
二、若是RealConnection对象为空,就从链接池中获取一个实际的RealConnection
if (result == null) {
  // Attempt to get a connection from the pool.
 Internal.instance.get(connectionPool, address, this, null);
  if (connection != null) {
    foundPooledConnection = true;
    result = connection;
  } else {
    selectedRoute = route;
      }
}
    …
    //三、调用connect方法进行实际的网络链接
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);
ps:赋值操做!判断这个链接是否已经创建了,若是已经创建就抛出异常,这个链接与否的标志是protocol标志位协议,表示在整个链接创建和可能的协议过程当中所要用到的协议
if (protocol != null) throw new IllegalStateException("already connected”);
if (route.requiresTunnel()) {
判断是否创建Tunnerl链接
        routeDatabase().connected(result.route());
    ...
// Pool the connection. 四、将这个实际的RealConnection链接放到链接池中
Internal.instance.put(connectionPool, result);
  
      小结 ConnectInterceptor
一、创建RealConnection对象
二、选择不一样的链接方式
三、CallServerInterceptor
 
 
链接池
 
定义
OhHttp将客户端和服务器端的链接抽象成Connection类,RealConnection是它的实现类,为了管理这些链接的复用,因此有了ConnectionPool类。
  •     当他们共享相同的地址就能够复用链接!
  •     在必定时间内,维护哪些链接是打开状态,已被之后复用的策略
ps:http1.0 keep-alive机制  http2.0 多路复用机制
本章重点:
  •     对链接池的Get、Put链接操做
  •     对链接池的自动回收操做
 
链接池的 Get()
@Nullable RealConnection get(Address address, StreamAllocation streamAllocation, Route route) {
  assert (Thread.holdsLock(this));
//一、for循环遍历链接池当中的Connections,获取可用的connection
  for (RealConnection connection : connections) {
    if (connection.isEligible(address, route)) {
      streamAllocation.acquire(connection, true);
      return connection;
    }
  }
  return null;
}
streamAllocation.acquire():
public void acquire(RealConnection connection, boolean reportedAcquired) {
  assert (Thread.holdsLock(connectionPool));
  if (this.connection != null) throw new IllegalStateException();
//二、把从链接池当中获取到的RealConnection对象赋值给StreamAllocation的成员变量
  this.connection = connection;
  this.reportedAcquired = reportedAcquired;
//三、将StreamAllocationReference的弱引用添加到RealConnection的allocation集合当中
  connection.allocations.add(new StreamAllocationReference(this, callStackTrace));
}
ps:根据allocations这个ArrayList集合来判断当前链接对象所持有的StreamAllocation的数目,
经过这个集合大小判断一个网络链接的负载量是否已经超过了他的最大值
 
链接池的 put()
void put(RealConnection connection) {
  assert (Thread.holdsLock(this));
    //在添加之前 进行一个异步的清理任务
  if (!cleanupRunning) {
    cleanupRunning = true;
    executor.execute(cleanupRunnable);//如何回收connection的类
  }
  connections.add(connection);
}
 
小结
    一、不论哪一个拦截器中,请求都会产生一个SteramAllocation对象
    二、StreamAllocation对象的弱引用添加到RealConnection对象的allocations集合当中
    三、从链接池中获取连接
 
 
 
对链接池的自动回收操做
cleanupRunnable
private final Runnable cleanupRunnable = new Runnable() {
  @Override public void run() {
    while (true) {
    //一、首次清理的时候必定返回下一次清理的间隔时间;cleanup具体GC回收算法
      long waitNanoscleanup(System.nanoTime());
      if (waitNanos == -1) return;
      if (waitNanos > 0) {
        long waitMillis = waitNanos / 1000000L;
        waitNanos -= (waitMillis * 1000000L);
        synchronized (ConnectionPool.this) {
          try {
        //二、等待释放锁和时间片,等待时间结束后再次执行runnbale
            ConnectionPool.this.wait(waitMillis, (int) waitNanos);
          } catch (InterruptedException ignored) {
          }
     ...
};
cleanup():相似Java里的标记清除算法
首先标记出最不活跃的connection连接,或是叫泄漏连接,空闲连接
 
long cleanup(long now) {
    
//在同步代码块中标记泄漏的连接
synchronized (this) {
  for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
    RealConnection connection = i.next();
        
//若是被标记的连接,好比空闲的Socket连接超过5个,就会从池子中删除而后关闭连接
if (longestIdleDurationNs >= this.keepAliveDurationNs
        || idleConnectionCount > this.maxIdleConnections) {
  // We've found a connection to evict. Remove it from the list, then close it below (outside
  // of the synchronized block).
  connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
// A connection will be ready to evict soon.
   //当所有都是活跃连接的时候会返回
  return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
    //若是目前还能塞进去连接 有可能有泄漏的连接也会返回keepAliveDurationNs下次使用已被
  return keepAliveDurationNs;
} else //若是没有任何可用连接跳出死循环
  // No connections, idle or in use.
  cleanupRunning = false;
  return -1;
}
 
思考:若是找到最不活跃的连接呢?
// If the connection is in use, keep searching.
if (pruneAndGetAllocationCount(connection, now) > 0) {
  inUseConnectionCount++;
  continue;
}
pruneAndGetAllocationCount():根据弱引用是否为null来判断
private int pruneAndGetAllocationCount(RealConnection connection, long now) {
List<Reference<StreamAllocation>> references = connection.allocations;
//一、遍历维护弱引用的list
  for (int i = 0; i < references.size(); ) {
    Reference<StreamAllocation> reference = references.get(i);
   //二、查看streamAllocation对象是否为空,为空表示没有代码引用该对象则进行删除
    if (reference.get() != null) {
      i++;
      continue;
    }
    
references.remove(i);
connection.noNewStreams = true;
//三、当references对列为空时候,表示整个维护的队列已经被删空了因此返回0,表示这个连接已经没有引用了
if (references.isEmpty()) {
  connection.idleAtNanos = now - keepAliveDurationNs;
  return 0;
}
  }
//四、不然返回非0的具体值
  return references.size();
}
 
小结
    一、OkHttp使用了GC回收算法
    二、StreamAllocation的数量会渐渐减小为0
    三、被线程池检测到并回收,这样就能够保持多个健康的Keep-alive链接
 
++++++++++++++++++++++++++++++++++++++++
D、CallServerInterceptor 链接拦截器
做用:
  •         向服务器发起真正请求,
  •         并接收服务器返回给的读取响应并返回
 
//一、建立OkHttp拦截器的链,全部的请求正是经过链(proceed() )依次完成其任务
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
//建立HttpCodec 对象,因此的流对象封装成HttpCodec对象,用来编码解码的请求和响应
   HttpCodec httpCodec = realChain.httpStream();
//建立Http请求所须要的其余网络设施组件,用来分配Stream流
   StreamAllocation streamAllocation = realChain.streamAllocation();
//抽象客户端与服务器端的链接的具体实现
    RealConnection connection = (RealConnection) realChain.connection();
//网络请求
    Request request = realChain.request();
       ...
//二、利用httpCodec对象向Sokcet当中写入请求的Header头信息
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
        
//三、特殊状况:询问服务器是否能够发送带有请求体的信息,若是能接收这个规则的信息就返回给100的标示的响应吗,
会跳过写入body操做直接获取响应信息
if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
  httpCodec.flushRequest();
  realChain.eventListener().responseHeadersStart(realChain.call());
  responseBuilder = httpCodec.readResponseHeaders(true);
}
    
//四、调用body的writeTo()方法向Socket写入body信息
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
        …
//五、运行到这儿代表已经完成的整个网络请求的写入操做
httpCodec.finishRequest();
       ...
if (responseBuilder == null) {
  realChain.eventListener().responseHeadersStart(realChain.call());
//六、读取网络响应信息中的头部信息
  responseBuilder = httpCodec.readResponseHeaders(false);
}
   …
//七、读取网络响应的body信息
if (forWebSocket && code == 101) {
  // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
  response = response.newBuilder()
      .body(Util.EMPTY_RESPONSE)
      .build();
}
else {
  response = response.newBuilder()
      .body(httpCodec.openResponseBody(response))
      .build();
}
if ("close".equalsIgnoreCase(response.request().header("Connection"))
    || "close".equalsIgnoreCase(response.header("Connection"))) {
 streamAllocation.noNewStreams(); //八、当已经创建好链接的时候,禁止新的流建立
}
//九、当响应吗是20四、 205的时候抛出异常
if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
  throw new ProtocolException(
      "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
}
//十、返回Response响应信息
    return response;
}
 
小结
   一、Call对象对请求的封装
   二、dispatcher分发器对Request请求的分发
   三、getResponseWithInterceptors()方法进行拦截器链的构造
  •    RetryAndFollowUpInterceptor
  •    CacheInterceptor
  •    BridgeInterceptor
  •    ConnectionInterceptor
  •    CallServerInterceptor
相关文章
相关标签/搜索