Okhttp3 使用和原理(DEMO)

 

基本使用:html

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0106/2275.htmlandroid

http://www.jianshu.com/p/1873287eed87算法

http://blog.csdn.net/itachi85/article/details/51190687编程

 

一个最简单的DEMO缓存

public class OkHttp3BasicActivity extends Activity {

    @BindView(R.id.sn_tv)
    TextView tv;

    private OkHttpClient client ;

    String str = "";

    private Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what)
            {
                case 1:
                    tv.setText(msg.obj.toString());
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.simple_network_main);

        ButterKnife.bind(this);

        new Thread(new Runnable() {
            @Override
            public void run() {
                okrun();
            }
        }).start();

    }

    private void okrun()
    {
        client = new OkHttpClient();

        httpUrl = httpUrl+"?"+httpArg;

        Request request = new Request.Builder()
                .url("http://publicobject.com/helloworld.txt")
                .build();

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

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

                str = response.body().string();
                Message msg = Message.obtain();
                msg.what =1;
                msg.obj = str;
                handler.sendMessage(msg);
            }
        });

    }
}

 

两个须要注意的点:服务器

  1. okhttp3不能在ui线程中运行
  2. onresponse在子线程中,须要用handler回调才能操做ui

 

 

请求网络原理解析:cookie

http://liuwangshu.cn/application/network/7-okhttp3-sourcecode.html网络

HTTP请求执行流程分析

http://www.jianshu.com/p/230e2e2988e0app

 

OkHttp3源码详解(二总体流程)

整个实现流程以下:异步

  • 建立OkHttpClient对象。OkHttpClient为网络请求执行的一个中心,它会管理链接池,缓存,SocketFactory,代理,各类超时时间,DNS,请求执行结果的分发等许多内容。
  • 建立Request对象。Request用于描述一个HTTP请求,好比请求的方法是"GET"仍是"POST",请求的URL,请求的header,请求的body,请求的缓存策略等。
  • 利用前面建立的OkHttpClient对象和Request对象建立Call对象。Call是一次HTTP请求的Task,它会执行网络请求以得到响应。OkHttp中的网络请求执行Call既能够同步进行,也能够异步进行。调用call.execute()将直接执行网络请求,阻塞直到得到响应。而调用call.enqueue()传入回调,则会将Call放入一个异步执行队列,由ExecutorService在后台执行。
Call对象
若是使用enqueue方法,则调用dispatch.enqueue(),发送到线程池
若是使用execute,则不须要dispatch发送到线程池处理,直接同步处理。

线程池 executorService()

若是正在运行的异步任务队列数量小于最大请求数,线程池调用execute()执行该任务,不然加入准备队列

默认状况下,这是一个不限容量的线程池。但Dispatcher会限制每一个host同时执行的最大请求数量,默认为5,同时也会限制同时执行的总的最大请求数量

用户能够经过Dispatcher的构造函数来定制ExecutorService,这须要经过OkHttpClient.Builder在OkHttpClient的构建过程当中间接的作到。

 

线程池execute()

由getResponseWithInterceptorChain()来执行网络请求,获得response  

        Response response = getResponseWithInterceptorChain();

 成功后回调CallBack的onResponse方法  

          responseCallback.onResponse(RealCall.this, response); 

 

能够看到这里对回调接口是同步调用,也就是回调方法将在后台线程中被调用。

 

getResponseWithInterceptorChain()加上了一系列的interceptor,而后执行chain.proceed(request)

private Response getResponseWithInterceptorChain() throws IOException {  
  
    //构建全栈拦截器  
    List 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {  
      interceptors.addAll(client.networkInterceptors());//用户预约义的网络拦截器  
    }  
    interceptors.add(new CallServerInterceptor(  
        retryAndFollowUpInterceptor.isForWebSocket()));//调用服务拦截器  
  
    //内部经过责任链模式来使用拦截器  
    Interceptor.Chain chain = new RealInterceptorChain(  
        interceptors, null, null, null, 0, originalRequest);  
  
    return chain.proceed(originalRequest);//获取Response  
  }  
因而可知OkHttp中,Http请求的实际处理流程将大体以下图这样:

 

RetryAndFollowUpInterceptor

     重试与重定向拦截器,用来实现重试和重定向功能,内部经过while(true)死循环来进行重试获取Response(有重试上限,超过会抛出异常)。followUpRequest主要用来根据响应码来判断属于哪一种行为触发的重试和重定向(好比未受权,超时,重定向等),而后构建响应的Request进行下一次请求。固然,若是没有触发从新请求就会直接返回Response。

RetryAndFollowUpInterceptorintercept()中首先从client取得connection pool,用所请求的URL建立Address对象,并以此建立StreamAllocation对象。

RetryAndFollowUpInterceptor对重定向的响应也不会无休止的处理下去,它处理的最多的重定向级数为20次,超过20次时,它会抛异常出来。

总结一下RetryAndFollowUpInterceptor作的事情:

  1. 建立StreamAllocation对象,为后面流程的执行准备条件。
  2. 处理重定向的HTTP响应。
  3. 错误恢复。

 

BridgeInterceptor

桥接拦截器,用于完善请求头

这个Interceptor作的事情比较简单。能够分为发送请求和收到响应两个阶段来看。在发送请求阶段,BridgeInterceptor补全一些http header,这主要包括Content-TypeContent-LengthTransfer-EncodingHostConnectionAccept-EncodingUser-Agent,还加载Cookie,随后建立新的Request,并交给后续的Interceptor处理,以获取响应。

而在从后续的Interceptor获取响应以后,会首先保存Cookie。若是服务器返回的响应的content是以gzip压缩过的,则会先进行解压缩,移除响应中的header Content-EncodingContent-Length,构造新的响应并返回;不然直接返回响应。

 

CacheInterceptor

缓存拦截器,首先根据Request中获取缓存的Response,而后根据用于设置的缓存策略来进一步判断缓存的Response是否可用以及是否发送网络请求(CacheControl.FORCE_CACHE由于不会发送网络请求,因此networkRequest必定为空)。若是从网络中读取,此时再次根据缓存策略来决定是否缓存响应。

对于CacheInterceptor.intercept(Chain chain)的分析一样能够分为两个阶段,即请求发送阶段和响应获取以后的阶段。这两个阶段由chain.proceed(networkRequest)来分割。

在请求发送阶段,主要是尝试从cache中获取响应,获取成功的话,且响应可用未过时,则响应会被直接返回;不然经过后续的Interceptor来从网络获取,获取到响应以后,若须要缓存的,则缓存起来。

关于HTTP具体的缓存策略这里暂时再也不详述。

RealCall.getResponseWithInterceptorChain()可见CacheInterceptor的cache一样来自于OkHttpClient。OkHttp已经有实现Cache的整套策略,在Cache类,但默认状况下不会被用起来,须要本身在建立OkHttpClient时,手动建立并传给OkHttpClient.Builder。


 

ConnectInterceptor

 链接拦截器,用于打开一个链接到远程服务器。说白了就是经过StreamAllocation获取HttpStream和RealConnection对象,以便后续读写。

 

CallServerInterceptor

调用服务拦截器,拦截链中的最后一个拦截器,经过网络与调用服务器。经过HttpStream依次次进行写请求头、请求头(可选)、读响应头、读响应体。

 

CallServerInterceptor首先将http请求头部发给服务器,若是http请求有body的话,会再将body发送给服务器,继而经过httpStream.finishRequest()结束http请求的发送。

随后即是从链接中读取服务器返回的http响应,并构造Response。

若是请求的header或服务器响应的header中,Connection值为closeCallServerInterceptor还会关闭链接。

总结一下这几个Interceptor的职责:
RetryAndFollowUpInterceptor --->建立StreamAllocation对象,处理http的redirect,出错重试。对后续Interceptor的执行的影响:修改request及StreamAllocation。
BridgeInterceptor-------------->补全缺失的一些http header。对后续Interceptor的执行的影响:修改request。
CacheInterceptor-------------->处理http缓存。对后续Interceptor的执行的影响:若缓存中有所需请求的响应,则后续Interceptor再也不执行。
ConnectInterceptor------------>借助于前面分配的StreamAllocation对象创建与服务器之间的链接,并选定交互所用的协议是HTTP 1.1仍是HTTP 2。对后续Interceptor的执行的影响:建立了httpStream和connection。
CallServerInterceptor----------->处理IO,与服务器进行数据交换。对后续Interceptor的执行的影响:为Interceptor链中的最后一个Interceptor,没有后续Interceptor。
 
 
在RealInterceptorChain.proceed()中,除了对状态及获取的reponse作检查以外,最主要的事情便是构造新的RealInterceptorChain对象,获取对应Interceptor,并调用Interceptor的intercept(next)了。在这里,index充当迭代器或指示器的角色,用于指出当前正在处理的Interceptor。
public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;
  private final StreamAllocation streamAllocation;
  private final HttpStream httpStream;
  private final Connection connection;
  private final int index;
  private final Request request;
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpStream httpStream, Connection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpStream = httpStream;
    this.index = index;
    this.request = request;
  }
RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpStream, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

RealInterceptorChain + Interceptor实现了装饰器模式,实现了请求/响应的串式或流式处理。只不过内层装饰器不是外层装饰器的成员变量,而是接口方法中建立的临时变量。

 

@Override public Response intercept(Chain chain) throws IOException {  
    RealInterceptorChain realChain = (RealInterceptorChain) chain;  
    Request request = realChain.request();  
    StreamAllocation streamAllocation = realChain.streamAllocation();  
  
    // We need the network to satisfy this request. Possibly for validating a conditional GET.  
    boolean doExtensiveHealthChecks = !request.method().equals("GET");  
    HttpStream httpStream = streamAllocation.newStream(client, doExtensiveHealthChecks);  
    RealConnection connection = streamAllocation.connection();  
  
    return realChain.proceed(request, streamAllocation, httpStream, connection);  
  }

 在Http1xStram中,它利用Okio对Socket的读写操做进行封装,而建立HttpStream 对象的过程涉及到 StreamAllocationRealConnection,代码较长,这里就不展开,这个过程归纳来讲,就是找到一个可用的RealConnection,再利用RealConnection 的输入输出(BufferedSourceBufferedSink)建立HttpStream 对象,供后续步骤使用。

 

里咱们能够看到,核心工做都由HttpStream对象完成,而HttpStream实际上利用的是 Okio,而 Okio 实际上仍是用的Socket,因此没什么神秘的,只不过一层套一层,层数有点多。

 其实 Interceptor 的设计也是一种分层的思想,每一个Interceptor 就是一层。为何要套这么多层呢?分层的思想在 TCP/IP 协议中就体现得淋漓尽致,分层简化了每一层的逻辑,每层只须要关注本身的责任(单一原则思想也在此体现),而各层之间经过约定的接口/协议进行合做(面向接口编程思想),共同完成复杂的任务。

 

我的理解:

OkHttpClient.newCall(request)进行execute或者enqueue操做

 

Request保存了url,method,body,head等信息

 

1.调用dispatcher的enqueue方法

dispatcher维护了一个相似于CachedThreadPool的线程池,比较适合执行大量的耗时比较少的任务。线程池正在运行的请求主机数小于5时则把请求加载到runningAsyncCalls中并在线程池中执行。

 

2.进入拦截器链

重试和重定向:处理重定向事件

bridge:完善请求头,加载cookie

Cache:读取缓存(ETAG,LastModified),判断缓存是否过时,若是未过时则直接返回,再也不进入后续拦截器。若是开启网络链接则判断是否要缓存响应

Connection:创建链接

 

其中,Cache这里的缓存都是基于Map,key是请求中url的md5,value是在文件中查询到的缓存,页面置换基于LRU算法

创建链接后,核心工做都由HttpStream对象完成,而HttpStream实际上利用的是 Okio,而 Okio 实际上仍是用的Socket

相关文章
相关标签/搜索