Android网络框架OkHttp源码分析(1)——请求流程

Android网络框架——OkHttp源码分析

前言

OkHttp是一个处理网络请求的高性能框架,由Square公司贡献
它的出现替代了HttpUrlConnectionApache HttpClient

OkHttp采用了分层设计的思想,使用多层拦截器,每个拦截器解决一个问题,多层拦截器套在一起,就像设计模式中的装饰者模式一样,可以在保证每层功能高内聚的情况下,解决多样性的问题。

OkHttp源码的讲解可以分为两大部分:

  • 请求流程
  • 拦截器分析

接下来讲解第一部分:

okhttp3源码分析之请求流程(同步,异步)

我们通过一个简单的GET请求来回忆一下OkHttp的使用步骤,并以这个实例为例讲解OkHttp的请求流程,如下:

OkHttp的简单使用

//第一步、创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
    .build();

//第二步、创建请求Request
Request request = new Request.Builder()
    .url("http://github.com")
    .build();
    
//第三步、创建一个Call,用于发起网络请求
Call call = client.newCall(request);

//第四步、发起异步请求, 调用Call的enqueue()方法(同步用execute()方法)
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    	//请求失败处理
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
		//请求成功处理
    }
});

可以看见使用OkHttp 发送网络请求需要四个步骤:

  1. 创建OkHttpClient
  2. 创建请求Request
  3. 创建Call,其用于发送网络请求
  4. 调用Call的enqueue()或execute()方法发起异步或同步请求

下面将介绍每一个步骤:

1.创建OkHttpClient
//第一步、创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
    .build();

OkHttpClient相当于OkHttp的大总管,它负责将每一个具体的工作发放给每个员工,
接下来一起看看OkHttpClient.Builder()中的各个参数:

 public Builder() {
      dispatcher = new Dispatcher();// 调度器
      protocols = DEFAULT_PROTOCOLS;// HTTP 协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS;// 传输层版本和连接协议
      eventListenerFactory = EventListener.factory(EventListener.NONE);// 事件监听工厂
      proxySelector = ProxySelector.getDefault();// 代理选择器
      cookieJar = CookieJar.NO_COOKIES;// cookie
      socketFactory = SocketFactory.getDefault();// socket 工厂
      hostnameVerifier = OkHostnameVerifier.INSTANCE;// 主机名字确认
      certificatePinner = CertificatePinner.DEFAULT;// 证书链
      proxyAuthenticator = Authenticator.NONE;// 代理服务器身份验证
      authenticator = Authenticator.NONE;// 源服务器身份验证
      connectionPool = new ConnectionPool();// 连接池
      dns = Dns.SYSTEM;// 域名
      followSslRedirects = true;// 是否遵循 ssl 重定向
      followRedirects = true;// 是否遵循重定向
      retryOnConnectionFailure = true;// 连接失败的时候是否重试
      connectTimeout = 10_000;// 连接超时时间
      readTimeout = 10_000;// 读超时时间
      writeTimeout = 10_000;// 写超时时间
      pingInterval = 0;// HTTP / 2 和 Web 套接字 ping 之间的时间间隔
    }

这里只简单介绍每个参数的含义,这里先没有必要细述每一个参数的用途。

2.创建请求Request
//第二步、创建请求Request
Request request = new Request.Builder()
    .url("http://github.com")
    .build();

OkHttp网络请求中请求本身就是Request,其封装了请求所需的信息,比如url、header、requestBody等等,它和OkHttpClient一样都是使用建造者模式来配置自己的参数,如下:

public static class Builder {
    HttpUrl url; //请求地址
    String method;//请求方法
    Headers.Builder headers;//请求头
    RequestBody body;//请求体

    //这里配置默认的参数
    public Builder() {
      this.method = "GET";//默认是GET请求
      this.headers = new Headers.Builder();
    }
    
    //配置完参数后,通过Builder的参数创建一个Request
    public Request build() {
        if (url == null) throw new IllegalStateException("url == null");
        return new Request(this);
    }
}
3.创建Call,其用于发送网络请求
//第三步、创建一个Call,用于发起网络请求
Call call = client.newCall(request);

通过上述代码,我们看到是通过newCall方法返回了一个Call对象。

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

//RealCall类的newRealCall方法
  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

可以看到如上两步,就可以看到返回的是一个RealCall对象,而这个RealCall封装了请求的调用逻辑。

4. 调用Call的enqueue()或execute()方法发起异步或同步请求
//第四步、发起异步请求, 调用Call的enqueue()方法(同步用execute()方法)
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    	//请求失败处理
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
		//请求成功处理
    }
});

上述代码展示了OkHttp发送异步请求,其实OkHttp除此之外还可以发送同步请求

  • 同步请求(execute())
//RealCall类的execute方法
 @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);// 注释1
      Response result = getResponseWithInterceptorChain();// 注释2
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);// 注释3
    }
  }

注释1:
这里大管家OkHttpClient的调度器dispatcher用其execute方法将其放入了运行同步请求的队列中:

//Dispatcher类的executed方法
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
  
  //上述runningSyncCalls实质是一个双向队列
  private ArrayDeque<RealCall> runningSyncCalls = new ArrayDeque<>();

将请求放入runningSyncCalls后,执行注释2。

注释2:

// RealCall的getResponseWithInterceptorChain方法
Response getResponseWithInterceptorChain() throws IOException {
    // 创建一个拦截器链
    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());

	//调用Chain的proceed(Request)方法处理请求(该方法返回一个response)
    return chain.proceed(originalRequest);
}

因为上述调用该方法返回一个Response对象,所以chain.proceed返回的一定是一个Response,跟进一下看齐实现:

//RealInterceptorChain类的proceed方法
@Override 
public Response proceed(Request request) throws IOException {
    return proceed(request, transmitter, exchange);
 }

//上述方法调用该重载方法
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
    throws IOException {
    //...
    //再新建一个RealInterceptorChain,这里注意index加1,
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
                                                         index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    //获取interceptors列表中的下一个拦截器
    Interceptor interceptor = interceptors.get(index);
    //调用下一个拦截器的intercept(Chain)方法,传入刚才新建的RealInterceptorChain,返回Response
    Response response = interceptor.intercept(next);
    //...

    return response;
}

上述代码不难理解,就是不断调用链条中下一个拦截器的intercept()方法,其实每个拦截器由一条链连接起来,这就是典型的责任链模式,从节点的首部开始把请求传递下去,每一个拦截器都有机会处理这个请求,直到最后一个拦截器器处理完请求后,才开始逐层返回Resquese。拦截器也正是Okhttp核心功能所在

关于拦截器介绍下篇文章再讲,这里只需要知道每一个拦截器都代表了一个功能,它们会处理请求并将它传到下一个拦截器直至最后一个拦截器,再一一返回回来直到拿到这个 Response。

注释3:

//Dispatcher类的finished方法
void finished(RealCall call) {
    //传进了runningSyncCalls队列
    finished(runningSyncCalls, call);
}

//上述代码调用该重载方法
private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
        //尝试移除队列中的同步请求任务
        if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
        idleCallback = this.idleCallback;
    }
    
        //紧接着调用promoteAndExecute()方法进行异步任务的调度,如果没有异步任务要进行,promoteAndExecute()返回false
    boolean isRunning = promoteAndExecute();

    //isRunning等于false且设置了idleCallback,会执行一遍idle任务
    if (!isRunning && idleCallback != null) {
        idleCallback.run();
    }

finished()方法首先尝试从runningSyncCalls队列(执行同步请求队列)把刚才通过 executed()入队的同步任务RealCall移除,

  • 如果移除失败,就抛出异常
  • 如果移除成功,就紧接着调用promoteAndExecute()方法进行异步任务的调度并尝试执行一遍idle任务

promoteAndExecute()方法在异步请求中再详细介绍。

  • 异步请求(enqueue())
// RealCall的enqueue方法
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) { // 如果这个 call 已经被执行过,抛异常
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

上述代码可以看到将responseCallback封装到AsyncCall内,并且调用了大管家OkHttpClient的调度器dispatcherenqueue方法,

//Dispactcher类的enqueue方法
void enqueue(AsyncCall call) {
    synchronized (this) {
       readyAsyncCalls.add(call);
       //...
    }
    promoteAndExecute();
}

这里需要说明一下任务调度器Dispatcher的内部建立了一个线程池 ExecutorService ,而且维护了三个集合:

  • readyAsyncCalls : 等待被执行的异步请求集合

  • runningAsyncCalls : 正在执行的异步请求集合,包括已经被取消但未完成的请求

  • runningSyncCalls : 正在执行的同步请求集合,包括已经被取消但未完成的请求

可见,上述异步请求加入到等待被执行的异步请求集合后执行promoteAndExecute方法

//Dispatcher.java
private boolean promoteAndExecute() {
    //准备一个正在执行任务列表executableCalls
    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
        //1、这个for循环主要把readyAsyncCalls中等待执行的异步任务转移到runningAsyncCalls队列和executableCalls列表中去
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {

            //取出readyAsyncCalls中等待执行的异步任务
            AsyncCall asyncCall = i.next();

            //判断条件:1、正在运行的异步请求任务不能大于maxRequests;2、等待执行的异步任务的主机请求数不能大于maxRequestsPerHost
            if (runningAsyncCalls.size() >= maxRequests) break; 
            if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
            //满足条件,进入下面逻辑

            //把这个等待执行的异步任务从readyAsyncCalls中移除
            i.remove();
            asyncCall.callsPerHost().incrementAndGet();
            
            //把这个等待执行的异步任务添加进executableCalls列表
            executableCalls.add(asyncCall);
            //把这个等待执行的异步任务添加进runningAsyncCalls队列
            runningAsyncCalls.add(asyncCall);
        }
        
        //runningCallsCount()里面的逻辑: return runningAsyncCalls.size() + runningSyncCalls.size();
        isRunning = runningCallsCount() > 0;
    }
    //2、这个for循环主要是执行executableCalls列表中的异步任务
    for (int i = 0, size = executableCalls.size(); i < size; i++) {
        AsyncCall asyncCall = executableCalls.get(i);
        //传进executorService,调用AsyncCall的executeOn()方法,由线程池执行这个异步任务
        asyncCall.executeOn(executorService());
    }

    return isRunning;
}

promoteAndExecute()方法中主要是2个for循环:

  1. 第一个for循环是把符合条件的异步请求任务从readyAsyncCalls转移(提升)到runningAsyncCalls队列添加到executableCalls列表
  2. 第二个for循环就是遍历executableCalls列表,从executableCalls列表中获取AsyncCall对象,并且调用它的executeOn()方法,executeOn()方法传进了一个Dispatcher的executorService(线程池)

所以接下来应该看AsyncCall的executeOn方法:

//AsyncCall类的executeOn
void executeOn(ExecutorService executorService) {
    boolean success = false;
    try {
        //传进this,执行AsyncCall异步任务,AsyncCall本质是Runnable
        executorService.execute(this);
        success = true;
    } catch (RejectedExecutionException e) {
       //...
    } finally {
        if (!success) {
            //异步任务执行失败,调用Dispatcher的finished(AsyncCall)方法
            client.dispatcher().finished(this); 
        }
    }

其AsyncCall的executeOn方法将执行请求的任务又交给了executorService对象,注释中说道AsyncCall实质上就是一个Runnable,所以我们需要看他如何实现run()方法:

public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      //run方法中执行execute()方法
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

其run方法内部有调用了execute()方法,所为我们再跳转到该方法:

@Override 
protected void execute() {
    //...
    try {
        //1.调用RealCall的getResponseWithInterceptorChain()方法处理请求获得response
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        //请求处理完毕,返回响应,回调Callback的onResponse()方法
        responseCallback.onResponse(RealCall.this, response);
    } catch (IOException e) {
        //...
    } finally {
        //异步请求任务执行完毕,调用Dispatcher的finished(AsyncCall)方法
        client.dispatcher().finished(this);
    }
}

这样我们就成功的完成了一次异步请求,但是还有最后一步finished方法没有介绍,其实通过上文同步请求的介绍大家也能猜到,就是将该请求从runningAsyncCalls(执行异步请求队列)中移除。

void finished(AsyncCal call) {
    //传进runningAsyncCalls,而不是runningSyncCalls
    finished(runningSyncCalls, call);
}

//上述代码调用重载方法与上文同步请求相同

总结

同步请求过程:

  1. 调用call.execute()
  2. 由于call的实质是RealCall,执行realCall.execute()
  3. realCall.execute()中执行Dispatcher的executed(RealCall)
  4. Dispatcher的executed(RealCall)把这个同步请求任务保存进runningSyncCalls队列中,然后RealCall执行getResponseWithInterceptorChain()处理同步请求,请求经过层层拦截器后获得Response
  5. 最后RealCall执行Dispatcher的finished(RealCall)把之前暂时保存的同步请求任务从runningSyncCalls队列中移除。

异步请求过程:

  1. call.enqueue(Callback)
  2. 由于call的实质是RealCall,执行realCall.enqueue(Callback)
  3. realCall.execute()中先把传进来的Callback包装成一个AsyncCall,然后执行Dispatcher的enqueue(AsyncCall)
  4. Dispatcher的enqueue(AsyncCall)把这个异步请求任务保存进readyAsyncCalls队列中,保存后开始执行 promoteAndExecute()进行异步任务的调度
  5. promoteAndExecute方法会先把符合条件的异步请求任务从readyAsyncCalls转移到runningAsyncCalls队列和添加到executableCalls列表中去,然后遍历executableCalls列表,逐个执行AsyncCall 的executeOn(ExecutorService)
  6. 由于AsyncCall实质是一个Runnable,所以当线程执行该Call时执行其run方法,run方法内部调用execute方法
  7. execute方法会调用RealCall的getResponseWithInterceptorChain()方法处理请求获得response,并且根据请求结果处理结果
  8. 最后RealCall执行Dispatcher的finished(RealCall)把之前暂时保存的异步请求任务从runningAsyncCalls队列中移除。
上一篇:OkHTTP 重写 WebSocket 请求头


下一篇:耗时两个礼拜,8000字安卓面试长文,经验分享