okhttp源码学习一

一、okhttp发起网络请求的步骤

  • 构建OkHttpClient客户端
  • 构建Request请求
  • 创建Call对象,发起网络请求
  • 处理Response响应结果

1.OkHttpClient
可以通过new OkHttpClient()创建默认配置的client对象,也可以通过内置的Builder动态设置所需要的属性,可设置拦截器、缓存文件缓存大小、https验证一系列的值。

2.request
里面封装了请求的一些字段

  • 请求方法:
    常用的get、post方法。
  • 请求url:
    一个封装好的HttpUrl对象。
  • headers:
    请求头信息,可通过addHeader跟setHeader设置。
  • 请求体RequestBody:
    是一个抽象类,官方提供了两个实现类,FormBody(表单提交),MultipartBody(文件提交),分别对应两种不同MIME类型。
  • CacheControl:
    控制网络请求是否使用缓存。

3.Response
Headers+ResponseBody组成,ResponseBody是抽象类,有两个实现的子类:RealResponseBody、CacheResponseBody,分别代表真实响应跟缓存响应。

4.Call
http请求任务的封装,网络请求的操作基本都定义在这个接口里面,是OkHttp的核心类。当调用OkHttpClient的newCall方法时,内部是创建了一个RealCall对象。需要传入3个参数,client对象、request、是否是WebSocket。

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }

5.RealCall
创建了Call对象后,里面有两个方法是进行网络请求访问的,分别是:execute(同步请求),enqueue(异步请求),我们先转到execute方法的源码。

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
  • 代码可以看到,该方法只能执行一次,再执行的话就会抛出异常
  • captureCallStackTrace是捕获请求的StackTrace,后面再详细研究
  • 然后就走到Dispatcher的executed方法,Dispatcher是在创建OkHttpClient时就帮我们初始化的一个类
public static final class Builder {
	// ...省略相关代码
	Dispatcher dispatcher
	public Builder() {
		dispatcher = new Dispatcher();
	}
	// ...省略相关代码
}

进到Dispatcher的executed方法

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
 }

把创建的call对象添加到一个叫做runningSyncCalls的对象中,再看源码

/** 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<>();

原来Dispatcher里面定义了3个双向队列,看注解不难发现,发现runningSyncCalls是存储的是同步请求,readyAsyncCalls是存放等待执行任务,runningAsyncCalls是存放异步请求任务。

  • 执行完client.dispatcher().executed(this),要走到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);
    return chain.proceed(originalRequest);
  }

里面创建了一个拦截器的集合,把我们自己定义以及官方定义的拦截器都添加到集合中,通过调用RealInterceptorChain的proceed方法启动第一个拦截器的拦截方法,我们进到RealInterceptorChain的proceed方法源码的核心代码

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    	// ...省略若干代码
    	// Call the next interceptor in the chain.
    	RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    	Interceptor interceptor = interceptors.get(index);
    	Response response = interceptor.intercept(next);
      	// ...省略若干代码
      	return response;
}

会通过改变index的值,遍历我们传进去的拦截器集合,每次都会调用拦截器的intercept方法,而intercept方法中会调用RealInterceptorChain的proceed方法进而调用下一个拦截器的intercept方法,直到遍历到最后一个拦截器,而最后一个拦截器拿到响应的内容后,会递归的传递回第一个拦截器。最终又会到RealCall的executed方法,拿到Response。

6.异步请求源码跟踪

  • 入口:RealCall的enqueue方法
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

前面两步都是跟同步请求一样

  • 进到Dispatcher的enqueue方法之前,先来看看AsyncCall,就是实现了Runnable接口的一个对象,再看enqueue方法
private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  
synchronized void enqueue(AsyncCall call) {
	 //如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
     //添加到执行队列,开始执行请求
      runningAsyncCalls.add(call);
      //获得当前线程池,没有则创建一个
      executorService().execute(call);
    } else {
  	  //添加到等待队列中
      readyAsyncCalls.add(call);
    }
  }

会先判断当前异步请求是否大于64,以及与当前请求共享主机的请求数是否大于5,如果都在最大数范围,就会把请求添加到异步请求队列中,并启动线程处理当前请求。否则,就会把请求放在等待执行任务队列中。

  • 再看executorService()方法
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;
  }
  // SynchronousQueue:(同步队列)这个队列接收到任务的时候,会直接提交给线程处理,而不保留它(名字定义为 同步队列)。但有一种情况,假设所有线程都在工作怎么办?
		这种情况下,SynchronousQueue就会新建一个线程来处理这个任务。所以为了保证不出现(线程数达到了maximumPoolSize而不能新建线程)的错误,使用这个类型队列的时候,
		maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大,去规避这个使用风险。

通过懒加载方式创建一个线程池,执行ExecutorService的execute方法,会执行Runnable的run方法,进而执行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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

首先也是通过拦截器完成一系列的网络请求,拿到Response后,再通过一开始调用的call.enqueue方法传进去的callback对象,把成功与失败对调返回。

OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
        }
    });
  • OkHttp大体流程
    在这里插入图片描述

二、核心类
在这里插入图片描述

整体流程图
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值