彻底掌握网络通信(十七)走进OkHttp3的世界(二)请求/响应流程分析

彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法
彻底掌握网络通信(十一)HttpURLConnection进行网络请求的知识准备
彻底掌握网络通信(十二)HttpURLConnection进行网络请求概览
彻底掌握网络通信(十三)HttpURLConnection进行网络请求深度分析
彻底掌握网络通信(十四)HttpURLConnection进行网络请求深度分析二:缓存
彻底掌握网络通信(十五)HttpURLConnection进行网络请求深度分析三:发送与接收详解
彻底掌握网络通信(十六)走进OkHttp3的世界(一)引言

  OkHttp3相比OkHttp好在代码封装上,使用起来更方便,其内部实现和HttpUrlConnection是几乎一致的,所以我们只要弄清在OkHttp3在结构上的封装,那么这个框架也就非常清楚的掌握了



流程分析


1. 创建OkHttpClient实例
2. 创建Request类实例
3. 通过OkHttpClient实例调用newCall方法,返回Call
4. 调用Call的enqueue方法,将待执行请求放入到线程池中即可



结合代码详解流程


1. 详解创建OkHttpClient实例

 OkHttpClient mOkHttpClient = new OkHttpClient();

其主要调用

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

在这里通过静态内部类Builder实现OkHttpClient的构建

    public Builder() {
      dispatcher = new Dispatcher();
      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();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

第2行,构建一个Dispatcher实例,看名字就知道,这是一个分发者;每一个Dispatcher都有一个线程池,用来分发异步请求;
第3行,主要说明Okhttp3支持的协议,HTTP1.1;HTTP2.0

当完成Builder类创建之后,变会调用

OkHttpClient(Builder builder)

该构造函数主要作用就是对OkHttpClient.java中相关属性赋值,代码比较简单,就不罗列了



2 . 详解创建Request类实例
这个类其实作用很简单,就是代表一个HTTP请求

        Request request = new Request.Builder()
                .url("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin")
                .addHeader("name", "test")
                .build();

其中Builder为Request.java的内部类;通过new Request.Builder()实例化内部类,并通过调用build方法返回一个Request



3 . 详解通过OkHttpClient实例调用newCall方法,返回Call

 Call call = mOkHttpClient.newCall(request);

他会调用newCall方法返回一个call

疑问一:这个call是什么,和request有什么关系
为了分析这个问题,我们看下newCall方法

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
  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;
  }

这里就很明显了call只不过是一个接口,RealCall实现了这个接口,当调用newCall方法传入request参数的时候,实际上是返回了一个RealCall;可见将Request发送出去的实际操作类是 RealCall
第4行,我们给RealCall设置了一个监听 ,即 eventListener , 这又是什么? 为了不影响文章主流程,这个疑问将在后面给出答案



4. 详解调用Call的enqueue方法,将待执行请求放入到线程池

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

我们知道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));
  }

第3行表示如果一个请求正在被执行,则抛出一个IllegalStateException异常
第8行中client.dispatcher()返回的对象为Dispatcher.java,通过调用Dispatcher的enqueue方法,并传入AsyncCall对象作为参数

疑问 3 : AsyncCall 是什么
为了解决这个疑惑,我们先看下AsyncCall的继承关系
AsyncCall 继承自 NamedRunnable,NamedRunnable实现了Runnable接口,这样理解起来就简单了

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

NamedRunnable每次执行的时候,都会调用抽象方法execute,则必然其子类在run的时候,execute也会被执行,这里我们应该注意的是AsyncCall是RealCall的子类



现在我们回到client.dispatcher().enqueue(new AsyncCall(responseCallback));这个方法中,其将调用

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

runningAsyncCalls是一个双端队列,双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。其创建方式如下

Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>()

第2行代表表示,如果这个列队里面待请求的http请求小于64个,并且同一个host的请求数小于5个的时候,则将AsyncCall添加到双端列队runningAsyncCalls当中
第4行表示通过线程池来运行AsyncCall,此时AsyncCall的execute方法将会被执行;
第6行则表示在另外的情况下将AsyncCall放到runningAsyncCalls当中,当条件合适的时候,在取执行这个请求;顾我们可以猜测到每正确执行一个HTTP请求,runningAsyncCalls都会移除对应的AsyncCall



到这里OKhttp3的大体流程已经介绍清楚了,我们也获得几个新的知识点:

  1. OkHttp3支持的最大并发是64
  2. 同一个Host支持的最大并发是5
  3. 调用call.enqueue的方法是异步发起HTTP请求
  4. AsyncCall是RealCall的子类,RealCall是由HTTP请求Request创造出来的,顾AsyncCall是可以拿到父类的Request
  5. AsyncCall继承自NamedRunnable,NamedRunnable实现了Runnable接口,当通过线程池运行AsyncCall的时候,其execute将会被执行,一个异步的HTTP请求才会正确的发送出去



疑问5 :那如何发起一个同步的Http请求

这个很简单,只要通过如下代码调用即可

        OkHttpClient mOkHttpClient = new OkHttpClient();
        //new Request.Builder()实例化内部类
        //通过build方法构建Request对象
        Request request = new Request.Builder()
                .url("http://112.4.3.136:8080/portalone/homesdk/NetTVUniLogin")
                .addHeader("name", "test")
                .build();
        Call call = mOkHttpClient.newCall(request);
        try {
            Response response = call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }

我们通过调用RealCall的execute去主动的发起一次请求;这个就相当于调用runable的run方法一样,这里可不是启动一个线程



下节我们将分析一个HTTP请求具体是怎么发送出去的,如何创建SOCKET,如何拿到RESPONSE,如何缓存
是不是真的和HttpUrlConnection的流程是一致的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值