美文网首页
okhttp流程分析

okhttp流程分析

作者: 小火车程序员 | 来源:发表于2019-03-24 21:10 被阅读0次

前言

今天我们来说一说okhttp框架的执行流程,这次只说总体流程,剩下的细节我们留在后面说

okhttp的使用

在捋源码前先回顾一下okhttp是怎么使用的

//创建OkHttpClient
OkHttpClient mOkHttpClient = new OkHttpClient.Builder().build();
//创建一个Request
Request request = new Request.Builder()
    .url("https://www.baidu.com").build();
//创建Call
Call call = mOkHttpClient.newCall(request);
//请求加入异步调度
call.enqueue(new Callback(){
    @Override
    public void onFailure(@NonNull Call call, @NonNull final IOException e){
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException{
        String result =  response.body().string();
    }
});

分析

1,OkHttpClient

OkHttpClient创建的两种方式
1,默认使用构造函数

public OkHttpClient() {
    this(new Builder());
  }

2,使用builder模式

public Builder() {
    dispatcher = new Dispatcher();
    protocols = DEFAULT_PROTOCOLS;
    connectionSpecs = DEFAULT_CONNECTION_SPECS;
    ...创建初始化参数...
}

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

可以看到这两种方式最后都是将一个Builder传递到OkHttpClient的构造函数里创建的OkHttpClient对象,一般情况下我们推荐使用第2种的builder模式,因为如果使用第一种的构造函数方法,有些参数是没法设置进去的

2,Request

Request也是采用builder的方式,这个比较简单,就是设置一些请求时的参数

public Builder() {
    this.method = "GET";
    this.headers = new Headers.Builder();
}

3,Call

Call call = mOkHttpClient.newCall(request);
可以看到是使用OkHttpClient.newCall()方法创建的Call对象,那我们就来看一下newCall这个方法

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

这里我们看到实际创建的是RealCall对象,newRealCall是一个静态方法,这是一个Factory模式,点到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对象,然后设置一个eventListener,这个eventListener我们猜测应该是一个请求过程的回调,这里先不用管他,不影响整体流程的理解,继续往下看

4,将Call加入到异步调度

call.enqueue(new Callback(){...});

由于Call实际上是RealCall,我们就直接看一下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 AsyncCall(responseCallback));
  }

首先是加同步锁,判断是否已经执行,如果是已经执行的,则抛出异常,防止重复调用
然后captureCallStackTrace(),捕获调用过程中的堆栈信息
eventListener.callStart(this);回调通知客户端请求开始了
client.dispatcher().enqueue(new AsyncCall(responseCallback));加入到异步调度中,这里要说明一下的是,AsyncCall是RealCall的内部类,所以可以直接取到RealCall的所有成员变量
看下client的dispatcher()方法

public Dispatcher dispatcher() {
    return dispatcher;
  }

直接返回的Dispatcher对象,那我们继续看Dispatcher的enqueue方法

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    //加入到运行队列
      runningAsyncCalls.add(call);
      //加入到线程池
      executorService().execute(call);
    } else {
    //加入到等待队列
      readyAsyncCalls.add(call);
    }
  }

这里是先判断一下正在运行的请求数是否小于maxRequests(默认值是64),并且请求当前host的请求数量是否小于maxRequestsPerHost(默认值5),如果条件都满足则加入到运行队列并加入到线程池中开始执行,如果不满足则加入到等待队列中
再看一下runningCallsForHost是如何获取当前call的host的请求数量的

/** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.get().forWebSocket) continue;
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

可以看到,整个过程就是遍历runningAsyncCalls,判断与当前的host相等就计数器加一,然后返回;
好,那么重点来了,把call加入到线程池后,他是如何运行的呢?由于是加入到线程池,那么AsyncCall必定是一个Runnable的实现类,而且执行代码会在run方法里,我们看一下

final class AsyncCall extends NamedRunnable {
......
}

这里看到AsyncCall是继承自NamedRunnable,再看一下NamedRunnable这个类

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

我们看到这里的run方法首先是设置了一个当前线程的name,然后调用execute方法,最后再把原来的线程name设置回来,run主要执行的就是这个execute方法了,execute是的抽象方法,我们再回到AsyncCall看这个execute方法

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        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);
      }
    }
  }

这个balabala一大堆,但如果简化一下看的就明白了

@Override 
protected void execute() {
    //使用拦截器执行网络请求,这是okhttp的特色
    Response response = getResponseWithInterceptorChain();
    //回调成功或失败
    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
    responseCallback.onResponse(RealCall.this, response);
    //调用Dispatcher的finish方法,将当前call从运行队列中移除,并将等待队列中的call加入到运行队列
    client.dispatcher().finished(this);
}

主要就是三步,使用拦截器执行网络请求,回调结果,然后finished,这里需要重点看一的是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集合,然后通过RealInterceptorChain去递归调用所有的拦截器,最后将结果返回
最后我们再回过头来看一下 client.dispatcher().finished(this); 这个方法都做了哪些操作

/** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
//实际调用的是这个方法
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
    //把当前的call从队列中移除,calls就是runningAsyncCalls
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      //这个promoteCalls是从上个函数传进来的true
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

这个finished方法将当前的call从运行队列runningAsyncCalls中移除,然后调用promoteCalls()方法,然后是一个闲置状态的回调,这里我们主要看一下promoteCalls()方法

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

前面的两个判断可以忽略掉,看下这个for循环,通过迭代器遍历等待队列readyAsyncCalls,if (runningCallsForHost(call) < maxRequestsPerHost)判断当前host正在的call是否小于最大值,如果是true,则 i.remove();从等待队列中移除,runningAsyncCalls.add(call);executorService().execute(call);加入到运行队列并加入到线程池中执,最后一个判断是如果运行队列已满则返回
这个方法的作用就是将等待队列readyAsyncCalls中的Call加入到运行队列runningAsyncCalls中

总结

okhttp的请求过程就是将请求Call加入到线程池中执行,并同时加入到队列中;执行的过程就是通过一系列的Interceptor最后得到Response返回给客户端;同步方式与异步方式类似,只是不需要线程池了

相关文章

网友评论

      本文标题:okhttp流程分析

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