Android 深入Http(4)从OkHttp源码来看Http

6、OkHttp源码角度看Http

OkHttp就是Http在Android上的实现
我们先来看一下他的使用方法(下面截取自官方文档):

在这里插入图片描述
emm没什么好讲的,入口就是 execute(),但这是同步的,我们需要改成异步的,就是enqueue()
换成自己写的,不封装就是这样:

        OkHttpClient client = new OkHttpClient();
        client.newCall(new Request.Builder()
                .url(url)
                .build())
                .enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        Log.d(TAG,"onFailure");
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        Log.d(TAG,"onResponse");
                    }
                });

嗨呀,用法顶多就这样,一般情况下我们都不会直接使用OkHttp,更多是用Retrofit。(Retrofit的低层是Okhttp,所以Retrofit能做的,OkHttp都能做)

所以我们就来分析OkHttp的源码,来分析它其中的Http是怎么工作的。

OkHttp源码分析

newCall

分析源码从执行入口看起,在这里就是enqueue(),发现它是从newCall()调用的。所以最开始,我们来分析newCall的代码,

  //OkHttp使用结构
  client.newCall(new Request.Builder()...).enqueue(...);

我们发现newCall传入的是一个Request,没错,我们后面用Builder模式拼的这一串玩意,其实就是Http的Request。我们点进newCall的源码:

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

↑括号中的参数就是一个正经的Requeset对象。
再来看返回,函数的返回是 Call类型,而下面实际return的是一个 new RealCall(.. request ..) 对象
也就是说,RealCallCall的一个实现

我们点进RealCall:

RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket){
...
this.eventListener = eventListenerFactory.create(this);
}

分析下构造参数

  • OkHttpClient client
    大总管,先不管
  • Request originalRequest
    这里的Request是我们通过Build()创建出来的,这里为什么叫originalRequest呢?
    是因为后面我们会不断的对Request做操作(就是用拦截器啊!)把它这改一下那里改一下
  • boolean forWebSocket
    来判断是不是WebSocket,WebSocket就是Http的一种扩展(可以做到服务器对客户端做推送),一般都不是WebSocket

然后我们来看下下面的eventListener,在http的一个完整的过程中,会有一些关键的时间点,比如 Http的响应,tcp层的建立等等,而在这些时间点产生的事件,则会通过 eventListener去响应。

到这里,newCall就看完了,它就是产生一个 RealCall的对象。

enqueue()

接下来我们来看okhttp的入口 enqueue(Callback),因为它实际是由RealCall执行的,所以我们要去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就知道是分发器,它拿去分发Callback了,并且分发之前,将它包装成一个 AsyncCall对象。

我们先来啃Dispatcher

Dispather
它是一个线程管理工具。

因为Http的Request和Response要使用单独的线程,如果你有多个请求和返回,如果都放在同一个线程的话会被挡到,所以Dispatch来做到线程控制

在这里插入图片描述
看到Executor懂得人都懂了,它是专门处理线程的类。但这里不讲解了,它就是用来处理线程的。
还看到有 maxRequests和maxRequestsPerHost

  • maxRequests
    最多并列执行又这么多(64)的请求,后面多出来的请求就放在后面的队列等。
  • maxRequestsPerHost
    每个主机最多并列执行又这么多(5)的请求。多出来的往后稍稍。

Dispatcer的enqueue()方法

   synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      //如果当前并发执行的网络请求少于上限,则将该网络请求放到后台中执行
      executorService().execute(call);
    } else {
      //否则放到ready队列等待
      readyAsyncCalls.add(call);
    }
  }

这个方法比较简单,就是判断当前并行的网络请求有没有达到上限,有就等,没有就放到后台执行。

然后我们来看它具体执行的AsyncCall对象。
AsyncCall
在这里插入图片描述
发现作为一个Call对象它是没有run方法的,所以我们要去它的父类 NamedRunnable对象里面看。
在这里插入图片描述
发现NamedRunnable实现了Runnable方法并重写了run()方法,run方法里就是跑了 execute()方法。
此execute()非彼execute(),点进它,它是 AsyncCall继承该类重写的execute()方法。

这个execute()方法在 AsyncCall下,
在这里插入图片描述
想必最重要的是哪几行,大家都知道了。

Response response = getResponseWithInterceptorChain()

这行代码就突然Response了,这说明getResponseWithInterceptorChain()把Http的请求响应给走完了。也就是说它是最最最最关键的方法了。

已经了解完大框架,小结一下

学到这里我们看了好几个类,它们大概做的就是为Http铺好一条路,让http请求更好走,比如给它安排了线程,给Request包装等等,我们看一下关键的步骤:

  1. 将请求Requeset通过newCall方法包装成一个RealCall
  2. 执行RealCall()enqueue()方法,并且将Callback回调作为参数代入
  3. Dispatcher给该Request分配线程,放在后台,执行Dispatch自己的 execute()方法
  4. 这个execute()方法内部执行了 getResponseWithInterceptorChain(),该方法内部走完了http的传输细节,返回Response
  5. Response响应外面CallBack的 onResponse()onFailure()

注:其实这里就可以看出OkHttp无论是enqueue()还是execute(),都会走getResponseWithInterceptorChain(),因为execute()它不用切线程,所以它是直接执行该方法,而enqueue还要考虑当前请求是否太多,否则会造成阻塞,所以要用Dispatch来管理一下,再使用这个方法。

然后走完这个方法,就会去回调之前Callback的onResponse和onFailure方法了。

上面就是OkHttp的大框架了,其实很简单,所以外面用起来也很简单,好懂。
为了更深入的了解OkHttp,我们除了了解这些,还要去了解它里面的一些配置,和网络实现的细节。
也就是OkHttpClient 它是做什么的,和 getResponseWithInterceptorChain()这个方法

OkHttpClient

我们先来看看OkHttpClient里面的全部配置。每个都注释一下,并讲述比较重要的对象:

  //线程管理器,这个之前已经了解了
  final Dispatcher dispatcher;
  
  //代理类,帮我们配置网络信息
  final @Nullable Proxy proxy;
  
  //Protocol类里面列出了支持的Http版本,http1.0、1.1、2.0  SPDY3.1(http2的前身)
  final List<Protocol> protocols;
  
  //里面是Cipher Suite(如果是Https,那么这个Cipher suite就是TLS版本、对称加密、非对称加密、Hash算法...,让对方选,所以这里是一个列表)
  //它有好几套Cipher Suite方案任君选择
  final List<ConnectionSpec> connectionSpecs;
  
  //这两个比较重要,等下会具体研究,它们大概就是对连接过程中的每一个阶段做处理
  final List<Interceptor> interceptors;
  final List<Interceptor> networkInterceptors;
 
  //就是监听整个网络请求+返回过程
  final EventListener.Factory eventListenerFactory;
  
  //用处不大,不用了解了
  final ProxySelector proxySelector;
  
  //Jar是罐子的意思,CookieJar是存放Cookie的容器
  final CookieJar cookieJar;

  //Http的Cache
  final @Nullable Cache cache;
  final @Nullable InternalCache internalCache;

  //Socket是Tcp的端口,这个就是Tcp端口的工厂
  final SocketFactory socketFactory;
  //Https下Tcp的端口
  final @Nullable SSLSocketFactory sslSocketFactory;
  
  //证书链,方便验证(https讲过)
  final @Nullable CertificateChainCleaner certificateChainCleaner;

  //Https验证的 主机名验证器, 验证对方的Host是不是想要访问的Host
  final HostnameVerifier hostnameVerifier;

  //直译叫证书固定器,用来做自签名,用来验证证书公钥,如果host的证书和这个对象传入(本地存的)的公钥一致,则证明对方是自己想要访问的一方。
  final CertificatePinner certificatePinner;

  //用来写 Authorization Bearer<...>  当权限不足的时候会报错 401
  final Authenticator proxyAuthenticator;
  final Authenticator authenticator;
  
  //线程连接池,可以有那么多的线程可以用来进行网络请求
  final ConnectionPool connectionPool;

  //DNS实现类
  final Dns dns;

  //重定向跳转, 默认为true,即跳转
  final boolean followRedirects;
  //https跳到http重定向,默认为true
  final boolean followSslRedirects;

  
  //当连接建立失败后,是否重试,默认为true
  final boolean retryOnConnectionFailure;

  //Tcp连接、读、写 超过该时间就跑错
  final int connectTimeout;
  final int readTimeout;
  final int writeTimeout;

  //长连接,发送ping(相当于一个心跳)的时间间隔
  final int pingInterval;

看完上面的配置还有一些注释,会很明白HTTP在OkHttp里面的配置,比较全而且清晰。
OkHttpClient这个大总管它有关于Http、Https基本都有的配置信息,基本都是Header和Header有关的一些东西。

getResponseWithInterceptorChain()

OkHttp最核心的方法,相当于内部的入口。所以很有必要去了解。
我们先点进这个方法:

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

上面比较“麻雀虽小五脏俱全”的感觉,代码没那么多,但每一行都是核心。

它大概有三个部分:

  1. 装一些Interceptor到List中
  2. 构建一个 Interceptor Chain
  3. 使用chain的 proceed(originalRequest)得到了网络响应的Response

我们用图分别来说下他们的概念:

在这里插入图片描述
首先这个方法会创建一个含有泛型为Interceptor的List,并往这个List里面add几个系统自带的Interceptor。如上图所示。

chain的构造方法中带了这个List,也就是说,这个chain它其实是给这个List里的所有interceptor用一条链,按顺序给链了起来,如下图:

在这里插入图片描述
↑而且实际上,这条链并不是单向的,它走到走后的Interceptor后,会往回走。

原因很简单,chain的方向就是我们Request走的方向,我们把Request带过去(发送请求),最后会把Respnse带回来,回来的时候,也要这些Interceptor来处理结果。这就是chain。

为什么要用Chain和Interceptor,为什么不直接发到另一端去

那肯定是细节化啊。
万一你突然Request少了一个重要东西,我有必要在检查少了东西的这个环节进行上报,如果直接传过去,可能会产生不必要的开销。

chain的目的在于每个Interceptor都能够自定义的去处理这些事情,所以chain和Interceptor是多么滴重要。

chain.proceed(Requset)

process(request)是做什么的呢?process是前进的意思。

概述:将Request交个下一个Interceptor处理

将代码和图来讲一下:
它有三个情况,一个是将requeset,传过去,一个是等,一个是request/response回来:

(1)执行proceed()方法

在这里插入图片描述
Interceptor1的时候执行了proceed(request),将request传给Interceptor2
(2)等Chain回来
在这里插入图片描述
(3)Chain回来了,得到Interceptor2给Interceptor的东西了,就可以去处理这个东西了
在这里插入图片描述

每个节点都会这样走,并且他们有做实际的对Request的操作(比如传Request之前,我们要作什么,拿回来Response后,我们要做什么)。所以按照顺序,我们来看下getRespnseWithInterceptorChain()里面的Interceptor

Interceptor概述

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

首先 interceptors.addAll(client.interceptors());interceptors.addAll(client.networkInterceptors());是用来加我们自定义的Interceptor(如果没有自定义,这个就是空的)的,别的Interceptor都是OkHttp自带的,我们后面再讲自定义的,先来看下OkHttp自带的Interceptor。

retryAndFollowUpInterceptor

retry就是重试,FollowUp就是重定向。我们来看下它的源码:
而每个Interceptor最重要的方法就是它们的intercept(Chain)方法,这个方法就是和上面一样做这些事情:
①处理别人给我的request1,处理完后变成request2
②执行 chain.process(request2)
③等待别的Interceptor处理完,给我传Response
④处理这个Response

就是会有一个模板代码:

//Interceptor的 intercept的模板代码
public Response intercept(Chain chain) {
     //获取requeset
     Request request = chain.requeset();
     
     //前置方法,处理Requeset
     .....

     response = chain.process(requeset);

     //后置方法,处理Response
     .....
}

OK,看完这些,我们来看关于处理重定向的Interceptor的源码:

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    //StreamAllocation: Streams是OkHttp中一个很重要的概念,它的含义是http连接时产生Request/Response对
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);
      
      ....
      while (true) {
      ...
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
      .....
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }
    ...

process()方法其实是一个递归,一个Interceptor走了process(),那么这个process()方法就是给它的下一个Interceptor走process()…直到最后走完返回来,它得到的Response其实就是它后面全部走完后得到,返回的Response。

当我们重定向的Interceptor出现了需要重定向的时候,就会走代码中catch{}cover()方法其实就是重定向的方法,它根据OkHttp的配置(比如之前看过的 retryOnConnectionFailure的字段,如果是false,就不会进行重定向了…)然后根据这些配置去改变Request,更改地址信息,最终得到结果。

这个方法是while(true)包裹的,所以,它如果出现了重定向,一定会循环走。

重定向的Interceptor嘛…主要都是把精力集中在后置代码上。比较清楚的知道它在干什么。

我们来看下一个Interceptor把。

BridgeInterceptor(client.cookieJar())

桥接Interceptor。
往下看:
在这里插入图片描述
在这里插入图片描述
直到proceed()方法前,我们一目了然桥接Interceptor的前置方法,就是在给Http报文添加 请求头 Headers
它会添加ContentType、Content-Length、User-Agent…

需要注意的是,上面Accpet-Encodeing中,如果我们没有自定义接收编码格式,OkHttp会默认是gzip
你都给我默认gzip,万一我自己解不了gzip怎么办,没问题,OkHttp会自己解析gzip(就在这边的后置代码中)~

而后置代码都是对数据Response的解析一遍。,这就是BridgeInterceptor()

CacheInterceptor(client.internalCache())

不就是Cache吗。
Cache方法里面会先创建一个CacheStrategy,然后根据这个实例去查看拿来的Request它有没有Response(就是在本地有没有Cache)
在这里插入图片描述
↓如果我们本地有cache了,那我们就不用再去做网络请求了,这个时候会直接return 伪造一个Response给上个在等proceed()方法的Interceptor。
在这里插入图片描述
总结:

  • 如果对于这个Request有cache,并且这个cache没有过期,则CacheInterceptor不会走proceed()方法,会直接返回cache的Response
  • 如果没有cache,才会走proceed()方法,走之后的网络请求。

从这里也可以看出,Interceptor有它分出来的好处。

ConnectInterceptor

这个地方是真正的和TCP、TLS做交互的地方,就是做连接的地方,它最主要的代码是下面:
在这里插入图片描述
通过newStream()能得到一个HttpCodec对象,它用于编码与解码(Http1.1用的是纯文本和http2.0用的是纯二进制)
connection()里面会去建立Tcp连接(里面会走 connectSocket()connectTLS()方法)。
之前的OkHttpClient配置全都在RealConnection()用到了。

该Interceptor几乎是最后一个Interceptor,因为它做了连接,所以它没有后置方法,直接返回response。

CallServerInterceptor

这个Interceptor没有proceed(),因为它做了实质了工作
它就是做请求的写,写在了Socket中。
然后就是做响应的读,读成了一个ResponseBody
最后就返回这个包裹着 ResponseBodyResponse了。

总结
大致就已经大概走完了Interceptor了,就是和一开始的那副chain图一样,request在各种拦截器里面被修改,最后变成了一个最终版的Request,然后发送出去。最后通过连接Tcp、TLS完后通过I/O流去读写,读回来一个Response,最后返回去。

自定义的Interceptor

我们在getResponseWithInterceptorChain()看到可以去自定义拦截器:

Response getResponseWithInterceptorChain() throws IOException {
    ...
    //自定义的request
    interceptors.addAll(client.interceptors());
    ....
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    ....
  }

我们可以在外面通过 addInterceptor去添加自定义的拦截器,和模板代码一样:

 OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {

                        //前置工作
                        ...

                        Response response = chain.proceed(chain.request())

                        //后置工作
                        ...
                        return null;
                    }
                }).build();

而这个InterceptornetworkInterceptor的区别在于顺序
如果有Cache,后面的NetworkInterceptor就不会走,如果没有Cache,通过它能获得 比较原始的 Http数据,因为这时候数据还没有在ConnectInterceptor里面解析。

到这里整个OkHttp就解析完啦。
这里做个大总结:

总结

  • OkHttp是Http在Android的实现,Http所有的属性、配置在OkHttp都能做
  • OkHttpClient能够具体体现Http的配置
  • 关于enqueue(call)它最终会走到 getResponseWithInterceptorChain()方法
  • 这个方法有多个拦截器Interceptor,每个Interceptor都会拦截Request并处理,最后分发给下一个,然后等待下一个Interceptor返回response,通过chain的proceed()方法来进行。
  • 可以自定义Interceptor,有普通的Interceptor和特殊的NetworkInterceptor(这个比较少用),用来控制流程
  • 拦截器可以全面的体现Http的一些工作机制。并且展现出OkHttp的特点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值